<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

import * as childProcess from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as chromeFinder from './chrome-finder';
import {ask} from './ask';
import {DEFAULT_FLAGS} from './flags';

const mkdirp = require('mkdirp');
import * as net from 'net';
const rimraf = require('rimraf');
const log = require('../lighthouse-core/lib/log');
const spawn = childProcess.spawn;
const execSync = childProcess.execSync;
const isWindows = process.platform === 'win32';

export class ChromeLauncher {
  prepared = false;
  pollInterval: number = 500;
  autoSelectChrome: boolean;
  TMP_PROFILE_DIR: string;
  outFile?: number;
  errFile?: number;
  pidFile: string;
  startingUrl: string;
  chromeFlags: Array<string>;
  chrome?: childProcess.ChildProcess;
  port: number;

  constructor(opts: {
    startingUrl?: string,
    chromeFlags?: Array<string>,
    autoSelectChrome?: boolean,
    port?: number
  } = {}) {
    // choose the first one (default)
    this.autoSelectChrome = defaults(opts.autoSelectChrome, true);
    this.startingUrl = defaults(opts.startingUrl, 'about:blank');
    this.chromeFlags = defaults(opts.chromeFlags, []);
    this.port = defaults(opts.port, 9222);
  }

  get flags() {
    const flags = DEFAULT_FLAGS.concat([
      `--remote-debugging-port=${this.port}`,
      // Place Chrome profile in a custom location we'll rm -rf later
      `--user-data-dir=${this.TMP_PROFILE_DIR}`
    ]);

    if (process.platform === 'linux') {
      flags.push('--disable-setuid-sandbox');
    }

    flags.push(...this.chromeFlags);
    flags.push(this.startingUrl);

    return flags;
  }

  prepare() {
    switch (process.platform) {
      case 'darwin':
      case 'linux':
        this.TMP_PROFILE_DIR = unixTmpDir();
        break;

      case 'win32':
        this.TMP_PROFILE_DIR = win32TmpDir();
        break;

      default:
        throw new Error('Platform ' + process.platform + ' is not supported');
    }

    this.outFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-out.log`, 'a');
    this.errFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-err.log`, 'a');

    // fix for Node4
    // you can't pass a fd to fs.writeFileSync
    this.pidFile = `${this.TMP_PROFILE_DIR}/chrome.pid`;

    log.verbose('ChromeLauncher', `created ${this.TMP_PROFILE_DIR}`);

    this.prepared = true;
  }

  run() {
    if (!this.prepared) {
      this.prepare();
    }

    return Promise.resolve()
        .then(() => {
          const installations = (<any>chromeFinder)[process.platform]();

          if (installations.length < 1) {
            return Promise.reject(new Error('No Chrome Installations Found'));
          } else if (installations.length === 1 || this.autoSelectChrome) {
            return installations[0];
          }

          return ask('Choose a Chrome installation to use with Lighthouse', installations);
        })
        .then(execPath => this.spawn(execPath));
  }

  spawn(execPath: string) {
    const spawnPromise = new Promise(resolve => {<commit_msg>Clean up supported platform launcher.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

import * as childProcess from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as chromeFinder from './chrome-finder';
import {ask} from './ask';
import {DEFAULT_FLAGS} from './flags';

const mkdirp = require('mkdirp');
import * as net from 'net';
const rimraf = require('rimraf');
const log = require('../lighthouse-core/lib/log');
const spawn = childProcess.spawn;
const execSync = childProcess.execSync;
const isWindows = process.platform === 'win32';

export class ChromeLauncher {
type SupportedPlatforms = 'darwin'|'linux'|'win32';
  prepared = false;
  pollInterval: number = 500;
  autoSelectChrome: boolean;
  TMP_PROFILE_DIR: string;
  outFile?: number;
  errFile?: number;
  pidFile: string;
  startingUrl: string;
  chromeFlags: Array<string>;
  chrome?: childProcess.ChildProcess;
  port: number;

  constructor(opts: {
    startingUrl?: string,
    chromeFlags?: Array<string>,
    autoSelectChrome?: boolean,
    port?: number
  } = {}) {
    // choose the first one (default)
    this.autoSelectChrome = defaults(opts.autoSelectChrome, true);
    this.startingUrl = defaults(opts.startingUrl, 'about:blank');
    this.chromeFlags = defaults(opts.chromeFlags, []);
    this.port = defaults(opts.port, 9222);
  }

  get flags() {
    const flags = DEFAULT_FLAGS.concat([
      `--remote-debugging-port=${this.port}`,
      // Place Chrome profile in a custom location we'll rm -rf later
      `--user-data-dir=${this.TMP_PROFILE_DIR}`
    ]);

    if (process.platform === 'linux') {
      flags.push('--disable-setuid-sandbox');
    }

    flags.push(...this.chromeFlags);
    flags.push(this.startingUrl);

    return flags;
  }

  private prepare() {
    const platform = process.platform as SupportedPlatforms;

    switch (platform) {
      case 'darwin':
      case 'linux':
        this.TMP_PROFILE_DIR = unixTmpDir();
        break;

      case 'win32':
        this.TMP_PROFILE_DIR = win32TmpDir();
        break;

      default:
        throw new Error(`Platform ${platform} is not supported`);
    }

    this.outFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-out.log`, 'a');
    this.errFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-err.log`, 'a');

    // fix for Node4
    // you can't pass a fd to fs.writeFileSync
    this.pidFile = `${this.TMP_PROFILE_DIR}/chrome.pid`;

    log.verbose('ChromeLauncher', `created ${this.TMP_PROFILE_DIR}`);

    this.prepared = true;
  }

  run() {
    if (!this.prepared) {
      this.prepare();
    }

    const installations = await chromeFinder[process.platform as SupportedPlatforms]();
    if (installations.length === 0) {
      throw new Error('No Chrome Installations Found');
    }

    const chromePath = installations[0];
    return await this.spawn(chromePath);
  }

  spawn(execPath: string) {
    const spawnPromise = new Promise(resolve => {
{'fraction_non_alphanumeric': 0.1309326710816777, 'fraction_numerical': 0.007036423841059603, 'mean_word_length': 3.2691401648998824, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '2867670', 'n_tokens_mistral': 2236, 'n_tokens_neox': 2160, 'n_words': 616}