1#!/usr/bin/env vpython3
2# Copyright 2020 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import logging
7import os
8import subprocess
9import sys
10import tempfile
11import time
12import unittest
13
14import mock
15from parameterized import parameterized
16
17import test_runner
18
19
20class TestRunnerTest(unittest.TestCase):
21  def setUp(self):
22    logging.disable(logging.CRITICAL)
23    time.sleep = mock.Mock()
24
25  def tearDown(self):
26    logging.disable(logging.NOTSET)
27
28  @parameterized.expand([
29      'url_unittests',
30      './url_unittests',
31      'out/release/url_unittests',
32      './out/release/url_unittests',
33  ])
34  @mock.patch.object(os.path, 'isfile', return_value=True)
35  @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
36  @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
37  # Tests that the test runner doesn't attempt to download ash-chrome if not
38  # required.
39  def test_do_not_require_ash_chrome(self, command, mock_popen, mock_download,
40                                     _):
41    args = ['script_name', 'test', command]
42    with mock.patch.object(sys, 'argv', args):
43      test_runner.Main()
44      self.assertEqual(1, mock_popen.call_count)
45      mock_popen.assert_called_with([command])
46      self.assertFalse(mock_download.called)
47
48  @parameterized.expand([
49      'browser_tests',
50      'components_browsertests',
51      'content_browsertests',
52      'lacros_chrome_browsertests',
53  ])
54  @mock.patch.object(os,
55                     'listdir',
56                     return_value=['wayland-0', 'wayland-0.lock'])
57  @mock.patch.object(tempfile,
58                     'mkdtemp',
59                     side_effect=['/tmp/xdg', '/tmp/ash-data'])
60  @mock.patch.object(os.environ, 'copy', side_effect=[{}, {}])
61  @mock.patch.object(os.path, 'exists', return_value=True)
62  @mock.patch.object(os.path, 'isfile', return_value=True)
63  @mock.patch.object(test_runner,
64                     '_GetLatestVersionOfAshChrome',
65                     return_value='793554')
66  @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
67  @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
68  # Tests that the test runner downloads and spawns ash-chrome if ash-chrome is
69  # required.
70  def test_require_ash_chrome(self, command, mock_popen, mock_download, *_):
71    args = ['script_name', 'test', command]
72    with mock.patch.object(sys, 'argv', args):
73      test_runner.Main()
74      mock_download.assert_called_with('793554')
75      self.assertEqual(2, mock_popen.call_count)
76
77      ash_chrome_args = mock_popen.call_args_list[0][0][0]
78      self.assertTrue(ash_chrome_args[0].endswith(
79          'build/lacros/prebuilt_ash_chrome/793554/test_ash_chrome'))
80      expected_ash_chrome_args = [
81          '--user-data-dir=/tmp/ash-data',
82          '--enable-wayland-server',
83          '--no-startup-window',
84      ]
85      if command == 'lacros_chrome_browsertests':
86        expected_ash_chrome_args.append(
87            '--lacros-mojo-socket-for-testing=/tmp/ash-data/lacros.sock')
88      self.assertListEqual(expected_ash_chrome_args, ash_chrome_args[1:])
89      ash_chrome_env = mock_popen.call_args_list[0][1].get('env', {})
90      self.assertDictEqual({'XDG_RUNTIME_DIR': '/tmp/xdg'}, ash_chrome_env)
91
92      test_args = mock_popen.call_args_list[1][0][0]
93      if command == 'lacros_chrome_browsertests':
94        self.assertListEqual([
95            command,
96            '--lacros-mojo-socket-for-testing=/tmp/ash-data/lacros.sock'
97        ], test_args)
98      else:
99        self.assertListEqual([command], test_args)
100
101      test_env = mock_popen.call_args_list[1][1].get('env', {})
102      self.assertDictEqual(
103          {
104              'XDG_RUNTIME_DIR': '/tmp/xdg',
105              'EGL_PLATFORM': 'surfaceless'
106          }, test_env)
107
108
109  @mock.patch.object(os,
110                     'listdir',
111                     return_value=['wayland-0', 'wayland-0.lock'])
112  @mock.patch.object(os.path, 'exists', return_value=True)
113  @mock.patch.object(os.path, 'isfile', return_value=True)
114  @mock.patch.object(test_runner,
115                     '_GetLatestVersionOfAshChrome',
116                     return_value='793554')
117  @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
118  @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
119  # Tests that when a ash-chrome version is specified, that version is used
120  # instead of the latest one.
121  def test_specify_ash_chrome_version(self, mock_popen, mock_download, *_):
122    args = [
123        'script_name', 'test', 'browser_tests', '--ash-chrome-version', '781122'
124    ]
125    with mock.patch.object(sys, 'argv', args):
126      test_runner.Main()
127      mock_download.assert_called_with('781122')
128
129  @mock.patch.object(os,
130                     'listdir',
131                     return_value=['wayland-0', 'wayland-0.lock'])
132  @mock.patch.object(os.path, 'exists', return_value=True)
133  @mock.patch.object(os.path, 'isfile', return_value=True)
134  @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
135  @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
136  # Tests that if a ash-chrome version is specified, uses ash-chrome to run
137  # tests anyway even if |_TARGETS_REQUIRE_ASH_CHROME| indicates an ash-chrome
138  # is not required.
139  def test_overrides_do_not_require_ash_chrome(self, mock_popen, mock_download,
140                                               *_):
141    args = [
142        'script_name', 'test', './url_unittests', '--ash-chrome-version',
143        '793554'
144    ]
145    with mock.patch.object(sys, 'argv', args):
146      test_runner.Main()
147      mock_download.assert_called_with('793554')
148      self.assertEqual(2, mock_popen.call_count)
149
150  @mock.patch.object(os,
151                     'listdir',
152                     return_value=['wayland-0', 'wayland-0.lock'])
153  @mock.patch.object(os.path, 'exists', return_value=True)
154  @mock.patch.object(os.path, 'isfile', return_value=True)
155  @mock.patch.object(test_runner, '_GetLatestVersionOfAshChrome')
156  @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
157  @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
158  # Tests that when an ash-chrome path is specified, the test runner doesn't try
159  # to download prebuilt ash-chrome.
160  def test_specify_ash_chrome_path(self, mock_popen, mock_download,
161                                   mock_get_latest_version, *_):
162    args = [
163        'script_name',
164        'test',
165        'browser_tests',
166        '--ash-chrome-path',
167        '/ash/test_ash_chrome',
168    ]
169    with mock.patch.object(sys, 'argv', args):
170      test_runner.Main()
171      self.assertFalse(mock_get_latest_version.called)
172      self.assertFalse(mock_download.called)
173
174  @mock.patch.object(os.path, 'isfile', return_value=True)
175  @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
176  @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
177  # Tests that arguments not known to the test runner are forwarded to the
178  # command that invokes tests.
179  def test_command_arguments(self, mock_popen, mock_download, _):
180    args = [
181        'script_name', 'test', './url_unittests', '--gtest_filter=Suite.Test'
182    ]
183    with mock.patch.object(sys, 'argv', args):
184      test_runner.Main()
185      mock_popen.assert_called_with(
186          ['./url_unittests', '--gtest_filter=Suite.Test'])
187      self.assertFalse(mock_download.called)
188
189  @mock.patch.dict(os.environ, {'ASH_WRAPPER': 'gdb --args'}, clear=False)
190  @mock.patch.object(os,
191                     'listdir',
192                     return_value=['wayland-0', 'wayland-0.lock'])
193  @mock.patch.object(tempfile,
194                     'mkdtemp',
195                     side_effect=['/tmp/xdg', '/tmp/ash-data'])
196  @mock.patch.object(os.environ, 'copy', side_effect=[{}, {}])
197  @mock.patch.object(os.path, 'exists', return_value=True)
198  @mock.patch.object(os.path, 'isfile', return_value=True)
199  @mock.patch.object(test_runner,
200                     '_GetLatestVersionOfAshChrome',
201                     return_value='793554')
202  @mock.patch.object(test_runner, '_DownloadAshChromeIfNecessary')
203  @mock.patch.object(subprocess, 'Popen', return_value=mock.Mock())
204  # Tests that, when the ASH_WRAPPER environment variable is set, it forwards
205  # the commands to the invocation of ash.
206  def test_ash_wrapper(self, mock_popen, *_):
207    args = [
208        'script_name', 'test', './browser_tests', '--gtest_filter=Suite.Test'
209    ]
210    with mock.patch.object(sys, 'argv', args):
211      test_runner.Main()
212      ash_args = mock_popen.call_args_list[0][0][0]
213      self.assertTrue(ash_args[2].endswith('test_ash_chrome'))
214      self.assertEqual(['gdb', '--args'], ash_args[:2])
215
216
217  # Test when ash is newer, test runner skips running tests and returns 0.
218  @mock.patch.object(os.path, 'exists', return_value=True)
219  @mock.patch.object(os.path, 'isfile', return_value=True)
220  @mock.patch.object(test_runner, '_FindLacrosMajorVersion', return_value=91)
221  def test_version_skew_ash_newer(self, *_):
222    args = [
223        'script_name', 'test', './browser_tests', '--gtest_filter=Suite.Test',
224        '--ash-chrome-path-override=\
225lacros_version_skew_tests_v92.0.100.0/test_ash_chrome'
226    ]
227    with mock.patch.object(sys, 'argv', args):
228      self.assertEqual(test_runner.Main(), 0)
229
230  @mock.patch.object(os.path, 'exists', return_value=True)
231  def test_lacros_version_from_chrome_version(self, *_):
232    version_data = '''\
233MAJOR=95
234MINOR=0
235BUILD=4615
236PATCH=0\
237'''
238    open_lib = '__builtin__.open'
239    if sys.version_info[0] >= 3:
240      open_lib = 'builtins.open'
241    with mock.patch(open_lib,
242                    mock.mock_open(read_data=version_data)) as mock_file:
243      version = test_runner._FindLacrosMajorVersion()
244      self.assertEqual(95, version)
245
246  @mock.patch.object(os.path, 'exists', return_value=True)
247  def test_lacros_version_from_metadata(self, *_):
248    metadata_json = '''
249{
250  "content": {
251    "version": "92.1.4389.2"
252  },
253  "metadata_version": 1
254}
255    '''
256    open_lib = '__builtin__.open'
257    if sys.version_info[0] >= 3:
258      open_lib = 'builtins.open'
259    with mock.patch(open_lib,
260                    mock.mock_open(read_data=metadata_json)) as mock_file:
261      version = test_runner._FindLacrosMajorVersionFromMetadata()
262      self.assertEqual(92, version)
263      mock_file.assert_called_with('metadata.json', 'r')
264
265
266if __name__ == '__main__':
267  unittest.main()
268