Skip to content

Commit

Permalink
feat: Add 'simulatorLogLevel' capability (#2479)
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach authored Oct 17, 2024
1 parent cdb9f4b commit b75f5ec
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/reference/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ about capabilities, refer to the [Appium documentation](https://appium.io/docs/e
|`appium:autoFillPasswords`| It allows you to turn on/off autofill passwords function when text field is foccused. Works only with iOS16.4+ simulators | `true` |
|`appium:permissions`| Allows to set permissions for the specified application bundle on Simulator only. The capability value is expected to be a valid JSON string with `{"<bundleId1>": {"<serviceName1>": "<serviceStatus1>", ...}, ...}` format. Since Xcode SDK 11.4 Apple provides native APIs to interact with application settings. Check the output of `xcrun simctl privacy booted` command to get the list of available permission names. Use `yes`, `no` and `unset` as values in order to `grant`, `revoke` or `reset` the corresponding permission. Below Xcode SDK 11.4 it is required that `applesimutils` package is installed and available in PATH. The list of available service names and statuses can be found at https://github.com/wix/AppleSimulatorUtils. | `{"com.apple.mobilecal": {"calendar": "YES"}}` |
|`appium:iosSimulatorLogsPredicate`|Set the `--predicate` flag in the ios simulator logs|`'process != "locationd" AND process != "DTServiceHub"' AND process != "mobileassetd"`|
|`appium:simulatorLogLevel`|Allows to customize the minimum log level for logs collected from simulators. Possible values are `default` (the default value), `info` and `debug`| `debug` |
|`appium:simulatorPasteboardAutomaticSync`| Handle the `-PasteboardAutomaticSync` flag when simulator process launches. It could improve launching simulator performance not to sync pasteboard with the system when this value is `off`. `on` forces the flag enabled. `system` does not provide the flag to the launching command. `on`, `off`, or `system` is available. They are case insensitive. Defaults to `off` | `system` |
|`appium:simulatorDevicesSetPath`| This capability allows to set an alternative path to the simulator devices set in case you have multiple sets deployed on your local system. Such feature could be useful if you, for example, would like to save disk space on the main system volume. | `/MyVolume/Devices` |
|`appium:safariGlobalPreferences`| Allows changing of Mobile Safari's preferences at the session startup. Check the documentation on arguments of [mobile: updateSafariPreferences](./execute-methods.md#mobile-updatesafaripreferences) extension to get more details on the value type requirements. Only available on real devices since driver version 7.9.0. A new Safari instance must be launched upon test startup for this capability to take effect on real devices. | `{ ShowTabBar: 0, WarnAboutFraudulentWebsites: 0 }` |
Expand Down
1 change: 1 addition & 0 deletions lib/commands/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export default {
sim: /** @type {import('appium-ios-simulator').Simulator} */ (this.device),
showLogs: this.opts.showIOSLog,
iosSimulatorLogsPredicate: this.opts.iosSimulatorLogsPredicate,
simulatorLogLevel: this.opts.simulatorLogLevel,
log: this.log,
}),
{
Expand Down
3 changes: 3 additions & 0 deletions lib/desired-caps.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ const desiredCapConstraints = /** @type {const} */ ({
iosSimulatorLogsPredicate: {
isString: true,
},
simulatorLogLevel: {
isString: true,
},
appPushTimeout: {
isNumber: true,
},
Expand Down
33 changes: 28 additions & 5 deletions lib/device-log/ios-simulator-log.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
import _ from 'lodash';
import {SubProcess, exec} from 'teen_process';
import {util} from 'appium/support';
import { LineConsumingLog } from './line-consuming-log';
import type { Simulator } from 'appium-ios-simulator';
import type { AppiumLogger } from '@appium/types';

const EXECVP_ERROR_PATTERN = /execvp\(\)/;
const LOG_STREAMING_PROCESS_NAME_PATTERN = /^com\.apple\.xpc\.launchd\.oneshot\.0x[0-f]+\.log$/;

const START_TIMEOUT = 10000;

export interface IOSSimulatorLogOptions {
sim: Simulator;
showLogs?: boolean;
iosSimulatorLogsPredicate?: string;
simulatorLogLevel?: string;
log: AppiumLogger;
}

export class IOSSimulatorLog extends LineConsumingLog {
private readonly sim: Simulator;
private readonly showLogs: boolean;
private readonly predicate?: string;
private readonly logLevel?: string;
private proc: SubProcess | null;

constructor(opts: IOSSimulatorLogOptions) {
super({log: opts.log});
this.sim = opts.sim;
this.showLogs = !!opts.showLogs;
this.predicate = opts.iosSimulatorLogsPredicate;
this.logLevel = opts.simulatorLogLevel;
this.proc = null;
}

Expand All @@ -41,13 +46,14 @@ export class IOSSimulatorLog extends LineConsumingLog {
if (this.predicate) {
spawnArgs.push('--predicate', this.predicate);
}
if (this.logLevel) {
spawnArgs.push('--level', this.logLevel);
}
this.log.debug(
`Starting log capture for iOS Simulator with udid '${this.sim.udid}' ` + `using simctl`,
`Starting log capture for iOS Simulator with udid '${this.sim.udid}' ` +
`via simctl using the following arguments '${util.quote(spawnArgs)}'`
);
try {
// cleanup existing listeners if the previous session has not been terminated properly
await exec('pkill', ['-f', [this.sim.udid, ...spawnArgs].join(' ')]);
} catch (ign) {}
await this.cleanupObsoleteLogStreams();
try {
this.proc = await this.sim.simctl.spawnSubProcess(spawnArgs);
await this.finishStartingLogCapture();
Expand Down Expand Up @@ -112,6 +118,23 @@ export class IOSSimulatorLog extends LineConsumingLog {
};
await this.proc.start(startDetector, START_TIMEOUT);
}

private async cleanupObsoleteLogStreams(): Promise<void> {
const processes = await this.sim.ps();
const pids = processes
.filter(({name}) => LOG_STREAMING_PROCESS_NAME_PATTERN.test(name))
.map(({pid}) => pid);
if (_.isEmpty(pids)) {
return;
}
try {
await exec('kill', pids.map(String));
} catch (e) {
this.log.warn(
`Cound not terminate one or more obsolete log streams: ${e.stderr || e.message}`
);
}
}
}

export default IOSSimulatorLog;

0 comments on commit b75f5ec

Please sign in to comment.