<commit_before>///<reference path="./.d.ts"/>
"use strict";

import childProcess = require("./child-process");
import * as child_process from "child_process";
import errors = require("./errors");

import common = require("./iphone-simulator-common");
import path = require("path");
import { Simctl } from "./simctl";
import util = require("util");
import utils = require("./utils");
import xcode = require("./xcode");
import * as _ from "lodash";

import { IPhoneSimulatorNameGetter } from "./iphone-simulator-name-getter";

const osenv = require("osenv");

export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements ISimulator {
	private static DEVICE_IDENTIFIER_PREFIX = "com.apple.CoreSimulator.SimDeviceType";
	private deviceLogChildProcess: any = null;
	private isDeviceLogOperationStarted = false;
	public defaultDeviceIdentifier = "iPhone 6";

	private simctl: ISimctl = null;

	constructor() {
		super();
		this.simctl = new Simctl();
	}

	public getDevices(): Promise<IDevice[]> {
		return this.simctl.getDevices();
	}

	public async getSdks(): Promise<ISdk[]> {
		let devices = await this.simctl.getDevices();
		return _.map(devices, device => {
			return {
				displayName: `iOS ${device.runtimeVersion}`,
				version: device.runtimeVersion
			};
		});
	}

	public async run(applicationPath: string, applicationIdentifier: string, options: IOptions): Promise<string> {
		const device = await this.getDeviceToRun(options);

		this.startSimulator(options, device);
		if (!options.skipInstall) {
			await this.installApplication(device.id, applicationPath);
		}

		return this.simctl.launch(device.id, applicationIdentifier, options);
	}

	public sendNotification(notification: string, deviceId: string): Promise<void> {
		let device = this.getDeviceFromIdentifier(deviceId);

		if (!device) {
			errors.fail("Could not find device.");
		}

		return this.simctl.notifyPost(deviceId, notification);
	}

	public getApplicationPath(deviceId: string, applicationIdentifier: string): Promise<string> {
		return this.simctl.getAppContainer(deviceId, applicationIdentifier);
	}

	public getInstalledApplications(deviceId: string): IApplication[] {
		return common.getInstalledApplications(deviceId);
	}

	public async installApplication(deviceId: string, applicationPath: string): Promise<void> {
		try {
			await this.simctl.install(deviceId, applicationPath);
		} catch (err) {
			await this.simctl.install(deviceId, applicationPath);
		}
	}

	public uninstallApplication(deviceId: string, appIdentifier: string):  Promise<void> {
		return this.simctl.uninstall(deviceId, appIdentifier, { skipError: true });
	}

	public async startApplication(deviceId: string, appIdentifier: string, options: IOptions): Promise<string> {
		// simctl launch command does not launch the process immediately and we have to wait a little bit,
		// just to ensure all related processes and services are alive.
		const launchResult = await this.simctl.launch(deviceId, appIdentifier, options);
		utils.sleep(0.5);
		return launchResult;
	}

	public async stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): Promise<string> {
		try {
			let xcodeMajorVersion: number = null;
			try {
				const xcodeVersion = xcode.getXcodeVersionData();
				xcodeMajorVersion = +xcodeVersion.major;
			} catch (err) {
				// Ignore the error.
			}

			let resultOfTermination: string;
			if (xcodeMajorVersion && xcodeMajorVersion < 8) {
				// Xcode 7.x does not have support for `xcrun simctl terminate` command
				resultOfTermination = childProcess.execSync(`killall ${bundleExecutable}`, { skipError: true });
			} else {
				resultOfTermination = await this.simctl.terminate(deviceId, appIdentifier);
			}

			// killall command does not terminate the processes immediately and we have to wait a little bit,
			// just to ensure all related processes and services are dead.
			// Same is valid for simctl terminate when Simulator's OS version is below 10.
			utils.sleep(0.5);

			return resultOfTermination;
		} catch (e) {
		}
	}

	public async getDeviceLogProcess(deviceId: string, predicate?: string): Promise<child_process.ChildProcess> {
		if (!this.isDeviceLogOperationStarted) {
			const device = await this.getDeviceFromIdentifier(deviceId);
			const deviceVersion = device ? device.runtimeVersion : "";
			const majorVersion = deviceVersion.split(".")[0];

			if (majorVersion && parseInt(majorVersion) >= 11) {
				this.deviceLogChildProcess = this.simctl.getLog(deviceId, predicate);
			} else {
				const logFilePath = path.join(osenv.home(), "Library", "Logs", "CoreSimulator", deviceId, "system.log");
				this.deviceLogChildProcess = require("child_process").spawn("tail", ['-f', '-n', '1', logFilePath]);
			}

			this.isDeviceLogOperationStarted = true;
		}

		return this.deviceLogChildProcess;
	}

	private async getDeviceToRun(options: IOptions, device?: any): Promise<IDevice> {
		let devices = _.sortBy(await this.simctl.getDevices(), (device) => device.runtimeVersion),
			sdkVersion = options.sdkVersion || options.sdk,
			deviceIdOrName = options.device;

		if (device && (device.sdkVersion || device.sdk)) {
			sdkVersion = device.sdkVersion || device.sdk;
		}

		if (device && device.id) {
			deviceIdOrName = device.id;
		}

		let result = _.find(devices, (device: IDevice) => {
			if (sdkVersion && !deviceIdOrName) {
				return device.runtimeVersion === sdkVersion;
			}

			if (deviceIdOrName && !sdkVersion) {
				return device.name === deviceIdOrName || device.id === deviceIdOrName;
			}
<commit_msg>fix: log process is not started on getDeviceLogProcess<commit_after>///<reference path="./.d.ts"/>
"use strict";

import childProcess = require("./child-process");
import * as child_process from "child_process";
import errors = require("./errors");

import common = require("./iphone-simulator-common");
import path = require("path");
import { Simctl } from "./simctl";
import util = require("util");
import utils = require("./utils");
import xcode = require("./xcode");
import * as _ from "lodash";

import { IPhoneSimulatorNameGetter } from "./iphone-simulator-name-getter";

const osenv = require("osenv");

export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements ISimulator {
	private static DEVICE_IDENTIFIER_PREFIX = "com.apple.CoreSimulator.SimDeviceType";
	private deviceLogChildProcess: any = null;
	private isDeviceLogOperationStarted = false;
	public defaultDeviceIdentifier = "iPhone 6";

	private simctl: ISimctl = null;

	constructor() {
		super();
		this.simctl = new Simctl();
	}

	public getDevices(): Promise<IDevice[]> {
		return this.simctl.getDevices();
	}

	public async getSdks(): Promise<ISdk[]> {
		let devices = await this.simctl.getDevices();
		return _.map(devices, device => {
			return {
				displayName: `iOS ${device.runtimeVersion}`,
				version: device.runtimeVersion
			};
		});
	}

	public async run(applicationPath: string, applicationIdentifier: string, options: IOptions): Promise<string> {
		const device = await this.getDeviceToRun(options);

		this.startSimulator(options, device);
		if (!options.skipInstall) {
			await this.installApplication(device.id, applicationPath);
		}

		return this.simctl.launch(device.id, applicationIdentifier, options);
	}

	public sendNotification(notification: string, deviceId: string): Promise<void> {
		let device = this.getDeviceFromIdentifier(deviceId);

		if (!device) {
			errors.fail("Could not find device.");
		}

		return this.simctl.notifyPost(deviceId, notification);
	}

	public getApplicationPath(deviceId: string, applicationIdentifier: string): Promise<string> {
		return this.simctl.getAppContainer(deviceId, applicationIdentifier);
	}

	public getInstalledApplications(deviceId: string): IApplication[] {
		return common.getInstalledApplications(deviceId);
	}

	public async installApplication(deviceId: string, applicationPath: string): Promise<void> {
		try {
			await this.simctl.install(deviceId, applicationPath);
		} catch (err) {
			await this.simctl.install(deviceId, applicationPath);
		}
	}

	public uninstallApplication(deviceId: string, appIdentifier: string): Promise<void> {
		return this.simctl.uninstall(deviceId, appIdentifier, { skipError: true });
	}

	public async startApplication(deviceId: string, appIdentifier: string, options: IOptions): Promise<string> {
		// simctl launch command does not launch the process immediately and we have to wait a little bit,
		// just to ensure all related processes and services are alive.
		const launchResult = await this.simctl.launch(deviceId, appIdentifier, options);
		utils.sleep(0.5);
		return launchResult;
	}

	public async stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): Promise<string> {
		try {
			let xcodeMajorVersion: number = null;
			try {
				const xcodeVersion = xcode.getXcodeVersionData();
				xcodeMajorVersion = +xcodeVersion.major;
			} catch (err) {
				// Ignore the error.
			}

			let resultOfTermination: string;
			if (xcodeMajorVersion && xcodeMajorVersion < 8) {
				// Xcode 7.x does not have support for `xcrun simctl terminate` command
				resultOfTermination = childProcess.execSync(`killall ${bundleExecutable}`, { skipError: true });
			} else {
				resultOfTermination = await this.simctl.terminate(deviceId, appIdentifier);
			}

			// killall command does not terminate the processes immediately and we have to wait a little bit,
			// just to ensure all related processes and services are dead.
			// Same is valid for simctl terminate when Simulator's OS version is below 10.
			utils.sleep(0.5);

			return resultOfTermination;
		} catch (e) {
		}
	}

	public async getDeviceLogProcess(deviceId: string, predicate?: string): Promise<child_process.ChildProcess> {
		const device = await this.getDeviceFromIdentifier(deviceId);
		return new Promise<child_process.ChildProcess>((resolve, reject) => {
			let timer: NodeJS.Timer;
			let isFulfilled = false;

			const fulfillSafe = (data?: Error | string) => {
				if (!isFulfilled) {
					isFulfilled = true;
					if (data instanceof Error) {
						reject(data);
					} else {
						resolve(this.deviceLogChildProcess);
					}
				}

				if (timer) {
					clearTimeout(timer);
					timer = null;
				}

				if (this.deviceLogChildProcess) {
					if (this.deviceLogChildProcess.stdout) {
						this.deviceLogChildProcess.stdout.removeListener("data", fulfillSafe);
					}

					this.deviceLogChildProcess.removeListener("error", fulfillSafe);
				}
			};

			if (!this.isDeviceLogOperationStarted) {
				const deviceVersion = device ? device.runtimeVersion : "";
				const majorVersion = deviceVersion.split(".")[0];

				if (majorVersion && parseInt(majorVersion) >= 11) {
					timer = setTimeout(() => {
						fulfillSafe();
					}, 3000);

					// For some reason starting the process takes a lot of time. So wait for the first message on stdout and resolve the promise at this point.
					this.deviceLogChildProcess = this.simctl.getLog(deviceId, predicate);
					if (this.deviceLogChildProcess.stdout) {
						this.deviceLogChildProcess.stdout.once("data", fulfillSafe);
						this.deviceLogChildProcess.once("error", fulfillSafe);
					} else {
						fulfillSafe();
					}
				} else {
					const logFilePath = path.join(osenv.home(), "Library", "Logs", "CoreSimulator", deviceId, "system.log");
					this.deviceLogChildProcess = childProcess.spawn("tail", ['-f', '-n', '1', logFilePath]);
					fulfillSafe();
				}

				this.isDeviceLogOperationStarted = true;
			} else {
				fulfillSafe();
			}
		});
	}

	private async getDeviceToRun(options: IOptions, device?: any): Promise<IDevice> {
		let devices = _.sortBy(await this.simctl.getDevices(), (device) => device.runtimeVersion),
			sdkVersion = options.sdkVersion || options.sdk,
			deviceIdOrName = options.device;

		if (device && (device.sdkVersion || device.sdk)) {
			sdkVersion = device.sdkVersion || device.sdk;
		}

		if (device && device.id) {
			deviceIdOrName = device.id;
		}

		let result = _.find(devices, (device: IDevice) => {
			if (sdkVersion && !deviceIdOrName) {
				return device.runtimeVersion === sdkVersion;
			}

			if (deviceIdOrName && !sdkVersion) {
				return device.name === deviceIdOrName || device.id === deviceIdOrName;
			}

{'fraction_non_alphanumeric': 0.12142682628878573, 'fraction_numerical': 0.002443195699975568, 'mean_word_length': 4.385964912280702, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3501807', 'n_tokens_mistral': 4108, 'n_tokens_neox': 3580, 'n_words': 923}