chromium/ios/build/bots/scripts/wpr_runner_test.py

#!/usr/bin/env vpython3
# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unittests for wpr_runner.py."""

import glob
import logging
import os
import subprocess
import unittest
from unittest.mock import Mock, call

import iossim_util
import test_runner
import test_runner_test
import wpr_runner


class WprProxySimulatorTestRunnerTest(test_runner_test.TestCase):
  """Tests for test_runner.WprProxySimulatorTestRunner."""

  def setUp(self):
    super(WprProxySimulatorTestRunnerTest, self).setUp()

    self.mock(test_runner, 'get_current_xcode_info', lambda: {
        'version': 'test version', 'build': 'test build', 'path': 'test/path'})
    self.mock(test_runner.subprocess,
              'check_output', lambda _: b'fake-bundle-id')
    self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
    self.mock(os.path, 'exists', lambda _: True)
    self.mock(test_runner.TestRunner, 'set_sigterm_handler',
              lambda self, handler: 0)
    self.mock(test_runner.SimulatorTestRunner, 'getSimulator',
              lambda _: 'fake-id')
    self.mock(test_runner.SimulatorTestRunner, 'deleteSimulator',
              lambda a, b: True)
    self.mock(wpr_runner.WprProxySimulatorTestRunner,
              'copy_trusted_certificate', lambda a, b: True)
    self.mock(iossim_util, 'get_simulator',
              lambda a, b: 'E4E66320-177A-450A-9BA1-488D85B7278E')

  def test_app_not_found(self):
    """Ensures AppNotFoundError is raised."""

    self.mock(os.path, 'exists', lambda p: not p.endswith('bad-host-app-path'))

    with self.assertRaises(test_runner.AppNotFoundError):
      wpr_runner.WprProxySimulatorTestRunner(
          'fake-app',
          'bad-host-app-path',
          'fake-iossim',
          'replay-path',
          'platform',
          'os',
          'wpr-tools-path',
          'out-dir',
      )

  def test_replay_path_not_found(self):
    """Ensures ReplayPathNotFoundError is raised."""

    self.mock(os.path, 'exists', lambda p: not p.endswith('bad-replay-path'))

    with self.assertRaises(wpr_runner.ReplayPathNotFoundError):
      wpr_runner.WprProxySimulatorTestRunner(
          'fake-app',
          'fake-host-app',
          'fake-iossim',
          'bad-replay-path',
          'platform',
          'os',
          'wpr-tools-path',
          'out-dir',
      )

  def test_wpr_tools_not_found(self):
    """Ensures WprToolsNotFoundError is raised."""

    self.mock(os.path, 'exists', lambda p: not p.endswith('bad-tools-path'))

    with self.assertRaises(wpr_runner.WprToolsNotFoundError):
      wpr_runner.WprProxySimulatorTestRunner(
          'fake-app',
          'fake-host-app',
          'fake-iossim',
          'replay-path',
          'platform',
          'os',
          'bad-tools-path',
          'out-dir',
      )

  def test_cert_not_found(self):
    """Ensures CertPathNotFoundError is raised"""

    runner = wpr_runner.WprProxySimulatorTestRunner(
        'fake-app',
        'fake-host-app',
        'fake-iossim',
        'replay-path',
        'platform',
        'os',
        'wpr-tools-path',
        'out-dir',
    )

    class FakeOsPath():

      def __init__(self):
        self.numCalls = 0

      def __call__(self, a):
        if self.numCalls == 0:
          self.numCalls += 1
          return True
        self.numCalls += 1
        return not a.endswith('wpr-tools-path/web_page_replay_go/wpr_cert.pem')

    self.mock(os.path, 'exists', FakeOsPath())
    self.mock(subprocess, 'check_call', lambda *args: None)

    self.unmock(wpr_runner.WprProxySimulatorTestRunner,
                'copy_trusted_certificate')

    with self.assertRaises(wpr_runner.CertPathNotFoundError):
      runner.copy_trusted_certificate('fake-udid')

  def test_copy_cert(self):
    """Ensures right commands are issued to copy cert"""

    runner = wpr_runner.WprProxySimulatorTestRunner(
        'fake-app',
        'fake-host-app',
        'fake-iossim',
        'replay-path',
        'platform',
        'os',
        'wpr-tools-path',
        'out-dir',
    )

    self.unmock(wpr_runner.WprProxySimulatorTestRunner,
                'copy_trusted_certificate')

    check_call_mock = Mock()

    self.mock(subprocess, 'check_call', check_call_mock)

    runner.copy_trusted_certificate('UDID')

    calls = [
        call(['xcrun', 'simctl', 'boot', 'UDID']),
        call([
            'xcrun', 'simctl', 'keychain', 'UDID', 'add-root-cert',
            'wpr-tools-path/web_page_replay_go/wpr_cert.pem'
        ]),
        call(['xcrun', 'simctl', 'shutdown', 'UDID'])
    ]

    check_call_mock.assert_has_calls(calls)

    # ensure subprocess.check_call was only called 3 times
    self.assertEqual(check_call_mock.call_count, 3)

  def test_init(self):
    """Ensures instance is created."""
    tr = wpr_runner.WprProxySimulatorTestRunner(
        'fake-app',
        'fake-host-app',
        'fake-iossim',
        'replay-path',
        'platform',
        'os',
        'wpr-tools-path',
        'out-dir',
    )

    self.assertTrue(tr)

  def run_wpr_test(self, test_filter=[], invert=False):
    """Wrapper that mocks the _run method and returns its result."""

    class FakeStdout:

      def __init__(self):
        self.line_index = 0
        self.lines = [
            b'Test Case \'-[a 1]\' started.',
            b'Test Case \'-[a 1]\' has uninteresting logs.',
            b'Test Case \'-[a 1]\' passed (0.1 seconds)',
            b'Test Case \'-[b 2]\' started.',
            b'Test Case \'-[b 2]\' passed (0.1 seconds)',
            b'Test Case \'-[c 3]\' started.',
            b'Test Case \'-[c 3]\' has interesting failure info.',
            b'Test Case \'-[c 3]\' failed (0.1 seconds)',
        ]

      def readline(self):
        if self.line_index < len(self.lines):
          return_line = self.lines[self.line_index]
          self.line_index += 1
          return return_line
        else:
          return None

    class FakeProcess:

      def __init__(self):
        self.stdout = FakeStdout()
        self.returncode = 0

      def stdout(self):
        return self.stdout

      def wait(self):
        return

    def popen(recipe_cmd, env, stdout, stderr):
      return FakeProcess()

    tr = wpr_runner.WprProxySimulatorTestRunner(
        'fake-app',
        'fake-host-app',
        'fake-iossim',
        'replay-path',
        'platform',
        'os',
        'wpr-tools-path',
        'out-dir',
    )
    self.mock(wpr_runner.WprProxySimulatorTestRunner, 'wprgo_start',
              lambda a, b: None)
    self.mock(wpr_runner.WprProxySimulatorTestRunner, 'wprgo_stop',
              lambda _: None)
    self.mock(wpr_runner.WprProxySimulatorTestRunner, 'get_wpr_test_command',
              lambda a, b, c: ["command", "arg"])

    self.mock(os.path, 'isfile', lambda _: True)
    self.mock(glob, 'glob', lambda _: ["file1", "file2"])
    self.mock(subprocess, 'Popen', popen)

    tr.xctest_path = 'fake.xctest'
    cmd = {'invert': invert, 'test_filter': test_filter}
    return tr._run(cmd=cmd, clones=1)

  def test_run_no_filter(self):
    """Ensures the _run method can handle passed and failed tests."""
    result = self.run_wpr_test()
    self.assertIn('file1.a/1', result.passed_tests())
    self.assertIn('file1.b/2', result.passed_tests())
    self.assertIn('file1.c/3', result.failed_tests())
    self.assertIn('file2.a/1', result.passed_tests())
    self.assertIn('file2.b/2', result.passed_tests())
    self.assertIn('file2.c/3', result.failed_tests())

  def test_run_with_filter(self):
    """Ensures the _run method works with a filter."""
    result = self.run_wpr_test(test_filter=["file1"], invert=False)
    self.assertIn('file1.a/1', result.passed_tests())
    self.assertIn('file1.b/2', result.passed_tests())
    self.assertIn('file1.c/3', result.failed_tests())
    self.assertNotIn('file2.a/1', result.passed_tests())
    self.assertNotIn('file2.b/2', result.passed_tests())
    self.assertNotIn('file2.c/3', result.failed_tests())

  def test_run_with_inverted_filter(self):
    """Ensures the _run method works with an inverted filter."""
    result = self.run_wpr_test(test_filter=["file1"], invert=True)
    self.assertNotIn('file1.a/1', result.passed_tests())
    self.assertNotIn('file1.b/2', result.passed_tests())
    self.assertNotIn('file1.c/3', result.failed_tests())
    self.assertIn('file2.a/1', result.passed_tests())
    self.assertIn('file2.b/2', result.passed_tests())
    self.assertIn('file2.c/3', result.failed_tests())


if __name__ == '__main__':
  logging.basicConfig(
      format='[%(asctime)s:%(levelname)s] %(message)s',
      level=logging.DEBUG,
      datefmt='%I:%M:%S')
  unittest.main()