text: /**
   * Copyright (c) 2015-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   */
  
  'use strict';
  
  /**
   * This script runs instrumentation tests one by one with retries
   * Instrumentation tests tend to be flaky, so rerunning them individually increases
   * chances for success and reduces total average execution time.
   *
   * We assume that all instrumentation tests are flat in one folder
   * Available arguments:
   * --path - path to all .java files with tests
   * --package - com.facebook.react.tests
   * --retries [num] - how many times to retry possible flaky commands: npm install and running tests, default 1
   */
  /*eslint-disable no-undef */
  
  const argv = require('yargs').argv;
  const async = require('async');
  const child_process = require('child_process');
  const fs = require('fs');
  const path = require('path');
  
  const colors = {
      GREEN: '\x1b[32m',
      RED: '\x1b[31m',
      RESET: '\x1b[0m'
  };
  
  const test_opts = {
      FILTER: new RegExp(argv.filter || '.*', 'i'),
      IGNORE: argv.ignore || null,
      PACKAGE: argv.package || 'com.facebook.react.tests',
      PATH: argv.path || './ReactAndroid/src/androidTest/java/com/facebook/react/tests',
      RETRIES: parseInt(argv.retries || 2, 10),
  
      TEST_TIMEOUT: parseInt(argv['test-timeout'] || 1000 * 60 * 10),
  
      OFFSET: argv.offset,
      COUNT: argv.count
  }
  
  let max_test_class_length = Number.NEGATIVE_INFINITY;
  
  let testClasses = fs.readdirSync(path.resolve(process.cwd(), test_opts.PATH))
      .filter((file) => {
          return file.endsWith('.java');
      }).map((clazz) => {
          return path.basename(clazz, '.java');
      });
  
  if (test_opts.IGNORE) {
      test_opts.IGNORE = new RegExp(test_opts.IGNORE, 'i');
      testClasses = testClasses.filter(className => {
          return !test_opts.IGNORE.test(className);
      });
  }
  
  testClasses = testClasses.map((clazz) => {
      return test_opts.PACKAGE + '.' + clazz;
  }).filter((clazz) => {
      return test_opts.FILTER.test(clazz);
  });
  
  // only process subset of the tests at corresponding offset and count if args provided
  if (test_opts.COUNT != null && test_opts.OFFSET != null) {
      const testCount = testClasses.length;
      const start = test_opts.COUNT * test_opts.OFFSET;
      const end = start + test_opts.COUNT;
  
      if (start >= testClasses.length) {
          testClasses = [];
      } else if (end >= testClasses.length) {
          testClasses = testClasses.slice(start);
      } else {
          testClasses = testClasses.slice(start, end);
      }
  }
  
  return async.mapSeries(testClasses, (clazz, callback) => {
      if(clazz.length > max_test_class_length) {
          max_test_class_length = clazz.length;
      }
  
      return async.retry(test_opts.RETRIES, (retryCb) => {
          const test_process = child_process.spawn('./ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh', [test_opts.PACKAGE, clazz], {
              stdio: 'inherit'
          })
  
          const timeout = setTimeout(() => {
              test_process.kill();
          }, test_opts.TEST_TIMEOUT);
  
          test_process.on('error', (err) => {
              clearTimeout(timeout);
              retryCb(err);
          });
  
          test_process.on('exit', (code) => {
              clearTimeout(timeout);
  
              if(code !== 0) {
                  return retryCb(new Error(`Process exited with code: ${code}`));
              }
  
              return retryCb();
          });
      }, (err) => {
          return callback(null, {
              name: clazz,
              status: err ? 'failure' : 'success'
          });
      });
  }, (err, results) => {
      print_test_suite_results(results);
  
      const failures = results.filter((test) => {
          return test.status === 'failure';
      });
  
      return failures.length === 0 ? process.exit(0) : process.exit(1);
  });
  
  function print_test_suite_results(results) {
      console.log('\n\nTest Suite Results:\n');
  
      let color;
      let failing_suites = 0;
      let passing_suites = 0;
  
      function pad_output(num_chars) {
          let i = 0;
  
          while(i < num_chars) {
              process.stdout.write(' ');
              i++;
          }
      }
      results.forEach((test) => {
          if(test.status === 'success') {
              color = colors.GREEN;
              passing_suites++;
          } else if(test.status === 'failure') {
              color = colors.RED;
              failing_suites++;
          }
  
          process.stdout.write(color);
          process.stdout.write(test.name);
          pad_output((max_test_class_length - test.name.length) + 8);
          process.stdout.write(test.status);
          process.stdout.write(`${colors.RESET}\n`);
      });
  
      console.log(`\n${passing_suites} passing, ${failing_suites} failing!`);
  }
  