Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ARM simulator support for xcuitest #917

Merged
merged 7 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/saucectl.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,10 @@
"description": "The set of one or more versions of the device platform on which to run the test suite.",
"type": "array",
"minItems": 1
},
"armRequired": {
"description": "If set to true, the simulator will run on an ARM-based Mac. If set to false, the simulator will run on an Intel-based Mac.",
"type": "boolean"
}
},
"required": [
Expand Down
4 changes: 4 additions & 0 deletions api/v1alpha/framework/xcuitest.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@
"description": "The set of one or more versions of the device platform on which to run the test suite.",
"type": "array",
"minItems": 1
},
"armRequired": {
"description": "If set to true, the simulator will run on an ARM-based Mac. If set to false, the simulator will run on an Intel-based Mac.",
"type": "boolean"
}
},
"required": [
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type VirtualDevice struct {
PlatformName string `yaml:"platformName,omitempty" json:"platformName"`
Orientation string `yaml:"orientation,omitempty" json:"orientation,omitempty"`
PlatformVersions []string `yaml:"platformVersions,omitempty" json:"platformVersions,omitempty"`
ARMRequired bool `yaml:"armRequired,omitempty" json:"armRequired,omitempty"`
}

const (
Expand Down
12 changes: 9 additions & 3 deletions internal/flags/simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/csv"
"errors"
"fmt"
"strconv"
"strings"

"github.com/saucelabs/saucectl/internal/config"
Expand All @@ -16,8 +17,8 @@ type Simulator struct {
Changed bool
}

// String returns a string represenation of the simulator.
func (e Simulator) String() string {
// String returns a string representation of the simulator.
func (e *Simulator) String() string {
if !e.Changed {
return ""
}
Expand Down Expand Up @@ -54,13 +55,18 @@ func (e *Simulator) Set(s string) error {
e.Orientation = val
case "platformVersion":
e.PlatformVersions = []string{val}
case "armRequired":
e.ARMRequired, err = strconv.ParseBool(val)
if err != nil {
return err
}
}
}

return nil
}

// Type returns the value type.
func (e Simulator) Type() string {
func (e *Simulator) Type() string {
return "simulator"
}
7 changes: 5 additions & 2 deletions internal/http/webdriver.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,13 @@ type SauceOpts struct {
TunnelIdentifier string `json:"tunnelIdentifier,omitempty"`
TunnelParent string `json:"parentTunnel,omitempty"` // note that 'parentTunnel` is backwards, because that's the way sauce likes it
ScreenResolution string `json:"screen_resolution,omitempty"`
SauceCloudNode string `json:"_sauceCloudNode,omitempty"`
UserAgent string `json:"user_agent,omitempty"`
TimeZone string `json:"timeZone,omitempty"`
Visibility string `json:"public,omitempty"`

// VMD specific settings.

ARMRequired bool `json:"armRequired,omitempty"`
}

type env struct {
Expand Down Expand Up @@ -135,7 +138,6 @@ func (c *Webdriver) StartJob(ctx context.Context, opts job.StartOptions) (jobID
TunnelIdentifier: opts.Tunnel.ID,
TunnelParent: opts.Tunnel.Parent,
ScreenResolution: opts.ScreenResolution,
SauceCloudNode: opts.Experiments["_sauceCloudNode"],
TestName: opts.Name,
BuildName: opts.Build,
Tags: opts.Tags,
Expand All @@ -152,6 +154,7 @@ func (c *Webdriver) StartJob(ctx context.Context, opts job.StartOptions) (jobID
MaxDuration: 10800,
TimeZone: opts.TimeZone,
Visibility: opts.Visibility,
ARMRequired: opts.ARMRequired,
},
DeviceName: opts.DeviceName,
DeviceOrientation: opts.DeviceOrientation,
Expand Down
116 changes: 72 additions & 44 deletions internal/job/starter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,56 +16,84 @@ type StartOptions struct {
PrevAttempts []report.Attempt `json:"-"`

// Timeout is used for local/per-suite timeout.
Timeout time.Duration `json:"-"`

User string `json:"username"`
AccessKey string `json:"accessKey"`
App string `json:"app,omitempty"`
TestApp string `json:"testApp,omitempty"`
Suite string `json:"suite,omitempty"`
OtherApps []string `json:"otherApps,omitempty"`
Framework string `json:"framework,omitempty"`
ConfigFilePath string `json:"-"`
CLIFlags map[string]interface{} `json:"-"`
Timeout time.Duration `json:"-"`
StartTime time.Time `json:"startTime,omitempty"`

User string `json:"username"`
AccessKey string `json:"accessKey"`

App string `json:"app,omitempty"`
OtherApps []string `json:"otherApps,omitempty"`

// FrameworkVersion contains the targeted version of the framework
// It should not be confused with automation tool (like jest/folio).
// This is currently supported only for frameworks available on Sauce Cloud:
// Currently supported: Cypress.
Suite string `json:"suite,omitempty"`

// FrameworkVersion contains the targeted version of the framework.
// It should not be confused with RunnerVersion.
FrameworkVersion string `json:"frameworkVersion,omitempty"`
Framework string `json:"framework,omitempty"`

PlatformName string `json:"platformName,omitempty"`
PlatformVersion string `json:"platformVersion,omitempty"`

Tunnel TunnelOptions `json:"tunnel,omitempty"`

Experiments map[string]string `json:"experiments,omitempty"`

// Job Metadata.

Name string `json:"name,omitempty"`
Build string `json:"build,omitempty"`
Tags []string `json:"tags,omitempty"`

// Job Access Control.

Visibility string `json:"public,omitempty"`

// Thresholds & Retries.

Attempt int `json:"-"`
CurrentPassCount int `json:"-"`
BrowserName string `json:"browserName,omitempty"`
BrowserVersion string `json:"browserVersion,omitempty"`
PlatformName string `json:"platformName,omitempty"`
PlatformVersion string `json:"platformVersion,omitempty"`
DeviceID string `json:"deviceId,omitempty"`
Attempt int `json:"-"`
CurrentPassCount int `json:"-"`
PassThreshold int `json:"-"`

Retries int `json:"-"`
SmartRetry SmartRetry `json:"-"`

// Cypress & Playwright & TestCafe only.

BrowserName string `json:"browserName,omitempty"`
BrowserVersion string `json:"browserVersion,omitempty"`
TimeZone string `json:"timeZone,omitempty"`
RunnerVersion string `json:"runnerVersion,omitempty"`
ScreenResolution string `json:"screenResolution,omitempty"`

// RDC & VMD only.

TestApp string `json:"testApp,omitempty"`
DeviceName string `json:"deviceName,omitempty"`
DeviceOrientation string `json:"deviceOrientation"`
DevicePrivateOnly bool `json:"devicePrivateOnly,omitempty"`
DeviceType string `json:"deviceType,omitempty"`
DeviceHasCarrier bool `json:"deviceHasCarrier,omitempty"`
RealDevice bool `json:"realDevice,omitempty"`
Name string `json:"name,omitempty"`
Build string `json:"build,omitempty"`
Tags []string `json:"tags,omitempty"`
Tunnel TunnelOptions `json:"tunnel,omitempty"`
ScreenResolution string `json:"screenResolution,omitempty"`
Retries int `json:"-"`
PassThreshold int `json:"-"`
SmartRetry SmartRetry `json:"-"`
RunnerVersion string `json:"runnerVersion,omitempty"`
Experiments map[string]string `json:"experiments,omitempty"`
TestOptions map[string]interface{} `json:"testOptions,omitempty"`
TestsToRun []string `json:"testsToRun,omitempty"`
TestsToSkip []string `json:"testsToSkip,omitempty"`
StartTime time.Time `json:"startTime,omitempty"`
AppSettings AppSettings `json:"appSettings,omitempty"`
RealDeviceKind string `json:"realDeviceKind,omitempty"`
TimeZone string `json:"timeZone,omitempty"`
Visibility string `json:"public,omitempty"`
Env map[string]string `json:"-"`

// RDC only.

AppSettings AppSettings `json:"appSettings,omitempty"`
DeviceID string `json:"deviceId,omitempty"`
DeviceHasCarrier bool `json:"deviceHasCarrier,omitempty"`
DevicePrivateOnly bool `json:"devicePrivateOnly,omitempty"`
DeviceType string `json:"deviceType,omitempty"`
RealDevice bool `json:"realDevice,omitempty"`
TestsToRun []string `json:"testsToRun,omitempty"`
TestsToSkip []string `json:"testsToSkip,omitempty"`
RealDeviceKind string `json:"realDeviceKind,omitempty"`

// VMD specific settings.

ARMRequired bool `json:"armRequired,omitempty"`
Env map[string]string `json:"-"`

// CLI.

ConfigFilePath string `json:"-"`
CLIFlags map[string]interface{} `json:"-"`
}

// AppSettings represents app settings for real device
Expand Down
21 changes: 0 additions & 21 deletions internal/saucecloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"github.com/saucelabs/saucectl/internal/apps"
"github.com/saucelabs/saucectl/internal/build"
"github.com/saucelabs/saucectl/internal/config"
"github.com/saucelabs/saucectl/internal/espresso"
"github.com/saucelabs/saucectl/internal/framework"
"github.com/saucelabs/saucectl/internal/hashio"
"github.com/saucelabs/saucectl/internal/iam"
Expand Down Expand Up @@ -284,11 +283,6 @@ func (r *CloudRunner) runJob(opts job.StartOptions) (j job.Job, skipped bool, er
return job.Job{}, r.interrupted, fmt.Errorf("failed to retrieve job status for suite %s: %s", opts.DisplayName, err.Error())
}

// Enrich RDC data
if opts.RealDevice {
enrichRDCReport(&j, opts)
}

// Check timeout
if j.TimedOut {
log.Error().
Expand All @@ -312,21 +306,6 @@ func (r *CloudRunner) runJob(opts job.StartOptions) (j job.Job, skipped bool, er
return j, false, nil
}

// enrichRDCReport added the fields from the opts as the API does not provides it.
func enrichRDCReport(j *job.Job, opts job.StartOptions) {
switch opts.Framework {
case "espresso":
j.OS = espresso.Android
}

if opts.DeviceID != "" {
j.DeviceName = opts.DeviceID
} else {
j.DeviceName = opts.DeviceName
j.OSVersion = opts.PlatformVersion
}
}

func (r *CloudRunner) runJobs(jobOpts chan job.StartOptions, results chan<- result) {
for opts := range jobOpts {
start := time.Now()
Expand Down
2 changes: 2 additions & 0 deletions internal/saucecloud/espresso.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type deviceConfig struct {
hasCarrier bool
deviceType string
privateOnly bool
armRequired bool
}

// EspressoRunner represents the Sauce Labs cloud implementation for cypress.
Expand Down Expand Up @@ -156,6 +157,7 @@ func enumerateDevices(devices []config.Device, virtualDevices []config.VirtualDe
platformName: e.PlatformName,
platformVersion: p,
orientation: e.Orientation,
armRequired: e.ARMRequired,
})
}
}
Expand Down
8 changes: 6 additions & 2 deletions internal/saucecloud/xcuitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ func (r *XcuitestRunner) runSuites() bool {
go func() {
for _, s := range suites {
for _, d := range enumerateDevices(s.Devices, s.Simulators) {
log.Debug().Str("suite", s.Name).Str("deviceName", d.name).Str("deviceID", d.ID).Str("platformVersion", d.platformVersion).Msg("Starting job")
log.Debug().Str("suite", s.Name).
Str("deviceName", d.name).Str("deviceID", d.ID).
Str("platformVersion", d.platformVersion).
Msg("Starting job")
r.startJob(jobOpts, s.App, s.TestApp, s.OtherApps, s, d)
}
}
Expand Down Expand Up @@ -235,7 +238,8 @@ func (r *XcuitestRunner) startJob(jobOpts chan<- job.StartOptions, appFileID, te
DevicePrivateOnly: d.privateOnly,

// VMD specific settings
Env: s.Env,
Env: s.Env,
ARMRequired: d.armRequired,

// Overwrite device settings
RealDeviceKind: strings.ToLower(xcuitest.IOS),
Expand Down
Loading