Home | History | Annotate | Download | only in port
      1 # Copyright (C) 2010 Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #    * Redistributions of source code must retain the above copyright
      8 # notice, this list of conditions and the following disclaimer.
      9 #    * Redistributions in binary form must reproduce the above
     10 # copyright notice, this list of conditions and the following disclaimer
     11 # in the documentation and/or other materials provided with the
     12 # distribution.
     13 #    * Neither the name of Google Inc. nor the names of its
     14 # contributors may be used to endorse or promote products derived from
     15 # this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 import logging
     30 import optparse
     31 import sys
     32 import tempfile
     33 import webkitpy.thirdparty.unittest2 as unittest
     34 
     35 from webkitpy.common.system.executive import Executive, ScriptError
     36 from webkitpy.common.system import executive_mock
     37 from webkitpy.common.system.filesystem_mock import MockFileSystem
     38 from webkitpy.common.system.outputcapture import OutputCapture
     39 from webkitpy.common.system.path import abspath_to_uri
     40 from webkitpy.thirdparty.mock import Mock
     41 from webkitpy.tool.mocktool import MockOptions
     42 from webkitpy.common.system.executive_mock import MockExecutive, MockExecutive2
     43 from webkitpy.common.system.systemhost_mock import MockSystemHost
     44 
     45 from webkitpy.layout_tests.port import Port, Driver, DriverOutput
     46 from webkitpy.layout_tests.port.base import VirtualTestSuite
     47 from webkitpy.layout_tests.port.test import add_unit_tests_to_mock_filesystem, TestPort
     48 
     49 class PortTest(unittest.TestCase):
     50     def make_port(self, executive=None, with_tests=False, port_name=None, **kwargs):
     51         host = MockSystemHost()
     52         if executive:
     53             host.executive = executive
     54         if with_tests:
     55             add_unit_tests_to_mock_filesystem(host.filesystem)
     56             return TestPort(host, **kwargs)
     57         return Port(host, port_name or 'baseport', **kwargs)
     58 
     59     def test_default_child_processes(self):
     60         port = self.make_port()
     61         self.assertIsNotNone(port.default_child_processes())
     62 
     63     def test_format_wdiff_output_as_html(self):
     64         output = "OUTPUT %s %s %s" % (Port._WDIFF_DEL, Port._WDIFF_ADD, Port._WDIFF_END)
     65         html = self.make_port()._format_wdiff_output_as_html(output)
     66         expected_html = "<head><style>.del { background: #faa; } .add { background: #afa; }</style></head><pre>OUTPUT <span class=del> <span class=add> </span></pre>"
     67         self.assertEqual(html, expected_html)
     68 
     69     def test_wdiff_command(self):
     70         port = self.make_port()
     71         port._path_to_wdiff = lambda: "/path/to/wdiff"
     72         command = port._wdiff_command("/actual/path", "/expected/path")
     73         expected_command = [
     74             "/path/to/wdiff",
     75             "--start-delete=##WDIFF_DEL##",
     76             "--end-delete=##WDIFF_END##",
     77             "--start-insert=##WDIFF_ADD##",
     78             "--end-insert=##WDIFF_END##",
     79             "/actual/path",
     80             "/expected/path",
     81         ]
     82         self.assertEqual(command, expected_command)
     83 
     84     def _file_with_contents(self, contents, encoding="utf-8"):
     85         new_file = tempfile.NamedTemporaryFile()
     86         new_file.write(contents.encode(encoding))
     87         new_file.flush()
     88         return new_file
     89 
     90     def test_pretty_patch_os_error(self):
     91         port = self.make_port(executive=executive_mock.MockExecutive2(exception=OSError))
     92         oc = OutputCapture()
     93         oc.capture_output()
     94         self.assertEqual(port.pretty_patch_text("patch.txt"),
     95                          port._pretty_patch_error_html)
     96 
     97         # This tests repeated calls to make sure we cache the result.
     98         self.assertEqual(port.pretty_patch_text("patch.txt"),
     99                          port._pretty_patch_error_html)
    100         oc.restore_output()
    101 
    102     def test_pretty_patch_script_error(self):
    103         # FIXME: This is some ugly white-box test hacking ...
    104         port = self.make_port(executive=executive_mock.MockExecutive2(exception=ScriptError))
    105         port._pretty_patch_available = True
    106         self.assertEqual(port.pretty_patch_text("patch.txt"),
    107                          port._pretty_patch_error_html)
    108 
    109         # This tests repeated calls to make sure we cache the result.
    110         self.assertEqual(port.pretty_patch_text("patch.txt"),
    111                          port._pretty_patch_error_html)
    112 
    113     def test_wdiff_text(self):
    114         port = self.make_port()
    115         port.wdiff_available = lambda: True
    116         port._run_wdiff = lambda a, b: 'PASS'
    117         self.assertEqual('PASS', port.wdiff_text(None, None))
    118 
    119     def test_diff_text(self):
    120         port = self.make_port()
    121         # Make sure that we don't run into decoding exceptions when the
    122         # filenames are unicode, with regular or malformed input (expected or
    123         # actual input is always raw bytes, not unicode).
    124         port.diff_text('exp', 'act', 'exp.txt', 'act.txt')
    125         port.diff_text('exp', 'act', u'exp.txt', 'act.txt')
    126         port.diff_text('exp', 'act', u'a\xac\u1234\u20ac\U00008000', 'act.txt')
    127 
    128         port.diff_text('exp' + chr(255), 'act', 'exp.txt', 'act.txt')
    129         port.diff_text('exp' + chr(255), 'act', u'exp.txt', 'act.txt')
    130 
    131         # Though expected and actual files should always be read in with no
    132         # encoding (and be stored as str objects), test unicode inputs just to
    133         # be safe.
    134         port.diff_text(u'exp', 'act', 'exp.txt', 'act.txt')
    135         port.diff_text(
    136             u'a\xac\u1234\u20ac\U00008000', 'act', 'exp.txt', 'act.txt')
    137 
    138         # And make sure we actually get diff output.
    139         diff = port.diff_text('foo', 'bar', 'exp.txt', 'act.txt')
    140         self.assertIn('foo', diff)
    141         self.assertIn('bar', diff)
    142         self.assertIn('exp.txt', diff)
    143         self.assertIn('act.txt', diff)
    144         self.assertNotIn('nosuchthing', diff)
    145 
    146     def test_setup_test_run(self):
    147         port = self.make_port()
    148         # This routine is a no-op. We just test it for coverage.
    149         port.setup_test_run()
    150 
    151     def test_test_dirs(self):
    152         port = self.make_port()
    153         port.host.filesystem.write_text_file(port.layout_tests_dir() + '/canvas/test', '')
    154         port.host.filesystem.write_text_file(port.layout_tests_dir() + '/css2.1/test', '')
    155         dirs = port.test_dirs()
    156         self.assertIn('canvas', dirs)
    157         self.assertIn('css2.1', dirs)
    158 
    159     def test_skipped_perf_tests(self):
    160         port = self.make_port()
    161 
    162         def add_text_file(dirname, filename, content='some content'):
    163             dirname = port.host.filesystem.join(port.perf_tests_dir(), dirname)
    164             port.host.filesystem.maybe_make_directory(dirname)
    165             port.host.filesystem.write_text_file(port.host.filesystem.join(dirname, filename), content)
    166 
    167         add_text_file('inspector', 'test1.html')
    168         add_text_file('inspector', 'unsupported_test1.html')
    169         add_text_file('inspector', 'test2.html')
    170         add_text_file('inspector/resources', 'resource_file.html')
    171         add_text_file('unsupported', 'unsupported_test2.html')
    172         add_text_file('', 'Skipped', '\n'.join(['Layout', '', 'SunSpider', 'Supported/some-test.html']))
    173         self.assertEqual(port.skipped_perf_tests(), ['Layout', 'SunSpider', 'Supported/some-test.html'])
    174 
    175     def test_get_option__set(self):
    176         options, args = optparse.OptionParser().parse_args([])
    177         options.foo = 'bar'
    178         port = self.make_port(options=options)
    179         self.assertEqual(port.get_option('foo'), 'bar')
    180 
    181     def test_get_option__unset(self):
    182         port = self.make_port()
    183         self.assertIsNone(port.get_option('foo'))
    184 
    185     def test_get_option__default(self):
    186         port = self.make_port()
    187         self.assertEqual(port.get_option('foo', 'bar'), 'bar')
    188 
    189     def test_additional_platform_directory(self):
    190         port = self.make_port(port_name='foo')
    191         port.default_baseline_search_path = lambda: ['LayoutTests/platform/foo']
    192         layout_test_dir = port.layout_tests_dir()
    193         test_file = 'fast/test.html'
    194 
    195         # No additional platform directory
    196         self.assertEqual(
    197             port.expected_baselines(test_file, '.txt'),
    198             [(None, 'fast/test-expected.txt')])
    199         self.assertEqual(port.baseline_path(), 'LayoutTests/platform/foo')
    200 
    201         # Simple additional platform directory
    202         port._options.additional_platform_directory = ['/tmp/local-baselines']
    203         port._filesystem.write_text_file('/tmp/local-baselines/fast/test-expected.txt', 'foo')
    204         self.assertEqual(
    205             port.expected_baselines(test_file, '.txt'),
    206             [('/tmp/local-baselines', 'fast/test-expected.txt')])
    207         self.assertEqual(port.baseline_path(), '/tmp/local-baselines')
    208 
    209         # Multiple additional platform directories
    210         port._options.additional_platform_directory = ['/foo', '/tmp/local-baselines']
    211         self.assertEqual(
    212             port.expected_baselines(test_file, '.txt'),
    213             [('/tmp/local-baselines', 'fast/test-expected.txt')])
    214         self.assertEqual(port.baseline_path(), '/foo')
    215 
    216     def test_nonexistant_expectations(self):
    217         port = self.make_port(port_name='foo')
    218         port.expectations_files = lambda: ['/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations', '/mock-checkout/third_party/WebKit/LayoutTests/platform/nonexistant/TestExpectations']
    219         port._filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations', '')
    220         self.assertEqual('\n'.join(port.expectations_dict().keys()), '/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations')
    221 
    222     def test_additional_expectations(self):
    223         port = self.make_port(port_name='foo')
    224         port.port_name = 'foo'
    225         port._filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/foo/TestExpectations', '')
    226         port._filesystem.write_text_file(
    227             '/tmp/additional-expectations-1.txt', 'content1\n')
    228         port._filesystem.write_text_file(
    229             '/tmp/additional-expectations-2.txt', 'content2\n')
    230 
    231         self.assertEqual('\n'.join(port.expectations_dict().values()), '')
    232 
    233         port._options.additional_expectations = [
    234             '/tmp/additional-expectations-1.txt']
    235         self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n')
    236 
    237         port._options.additional_expectations = [
    238             '/tmp/nonexistent-file', '/tmp/additional-expectations-1.txt']
    239         self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n')
    240 
    241         port._options.additional_expectations = [
    242             '/tmp/additional-expectations-1.txt', '/tmp/additional-expectations-2.txt']
    243         self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n\ncontent2\n')
    244 
    245     def test_additional_env_var(self):
    246         port = self.make_port(options=optparse.Values({'additional_env_var': ['FOO=BAR', 'BAR=FOO']}))
    247         self.assertEqual(port.get_option('additional_env_var'), ['FOO=BAR', 'BAR=FOO'])
    248         environment = port.setup_environ_for_server()
    249         self.assertTrue(('FOO' in environment) & ('BAR' in environment))
    250         self.assertEqual(environment['FOO'], 'BAR')
    251         self.assertEqual(environment['BAR'], 'FOO')
    252 
    253     def test_find_no_paths_specified(self):
    254         port = self.make_port(with_tests=True)
    255         layout_tests_dir = port.layout_tests_dir()
    256         tests = port.tests([])
    257         self.assertNotEqual(len(tests), 0)
    258 
    259     def test_find_one_test(self):
    260         port = self.make_port(with_tests=True)
    261         tests = port.tests(['failures/expected/image.html'])
    262         self.assertEqual(len(tests), 1)
    263 
    264     def test_find_glob(self):
    265         port = self.make_port(with_tests=True)
    266         tests = port.tests(['failures/expected/im*'])
    267         self.assertEqual(len(tests), 2)
    268 
    269     def test_find_with_skipped_directories(self):
    270         port = self.make_port(with_tests=True)
    271         tests = port.tests(['userscripts'])
    272         self.assertNotIn('userscripts/resources/iframe.html', tests)
    273 
    274     def test_find_with_skipped_directories_2(self):
    275         port = self.make_port(with_tests=True)
    276         tests = port.tests(['userscripts/resources'])
    277         self.assertEqual(tests, [])
    278 
    279     def test_is_test_file(self):
    280         filesystem = MockFileSystem()
    281         self.assertTrue(Port.is_test_file(filesystem, '', 'foo.html'))
    282         self.assertTrue(Port.is_test_file(filesystem, '', 'foo.svg'))
    283         self.assertTrue(Port.is_test_file(filesystem, '', 'test-ref-test.html'))
    284         self.assertFalse(Port.is_test_file(filesystem, '', 'foo.png'))
    285         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.html'))
    286         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.svg'))
    287         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.xht'))
    288         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.html'))
    289         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.svg'))
    290         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.xhtml'))
    291         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-ref.html'))
    292         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-notref.html'))
    293         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-notref.xht'))
    294         self.assertFalse(Port.is_test_file(filesystem, '', 'foo-ref.xhtml'))
    295         self.assertFalse(Port.is_test_file(filesystem, '', 'ref-foo.html'))
    296         self.assertFalse(Port.is_test_file(filesystem, '', 'notref-foo.xhr'))
    297 
    298     def test_parse_reftest_list(self):
    299         port = self.make_port(with_tests=True)
    300         port.host.filesystem.files['bar/reftest.list'] = "\n".join(["== test.html test-ref.html",
    301         "",
    302         "# some comment",
    303         "!= test-2.html test-notref.html # more comments",
    304         "== test-3.html test-ref.html",
    305         "== test-3.html test-ref2.html",
    306         "!= test-3.html test-notref.html",
    307         "fuzzy(80,500) == test-3 test-ref.html"])
    308 
    309         # Note that we don't support the syntax in the last line; the code should ignore it, rather than crashing.
    310 
    311         reftest_list = Port._parse_reftest_list(port.host.filesystem, 'bar')
    312         self.assertEqual(reftest_list, {'bar/test.html': [('==', 'bar/test-ref.html')],
    313             'bar/test-2.html': [('!=', 'bar/test-notref.html')],
    314             'bar/test-3.html': [('==', 'bar/test-ref.html'), ('==', 'bar/test-ref2.html'), ('!=', 'bar/test-notref.html')]})
    315 
    316     def test_reference_files(self):
    317         port = self.make_port(with_tests=True)
    318         self.assertEqual(port.reference_files('passes/svgreftest.svg'), [('==', port.layout_tests_dir() + '/passes/svgreftest-expected.svg')])
    319         self.assertEqual(port.reference_files('passes/xhtreftest.svg'), [('==', port.layout_tests_dir() + '/passes/xhtreftest-expected.html')])
    320         self.assertEqual(port.reference_files('passes/phpreftest.php'), [('!=', port.layout_tests_dir() + '/passes/phpreftest-expected-mismatch.svg')])
    321 
    322     def test_operating_system(self):
    323         self.assertEqual('mac', self.make_port().operating_system())
    324 
    325     def test_http_server_supports_ipv6(self):
    326         port = self.make_port()
    327         self.assertTrue(port.http_server_supports_ipv6())
    328         port.host.platform.os_name = 'cygwin'
    329         self.assertFalse(port.http_server_supports_ipv6())
    330         port.host.platform.os_name = 'win'
    331         self.assertTrue(port.http_server_supports_ipv6())
    332 
    333     def test_check_httpd_success(self):
    334         port = self.make_port(executive=MockExecutive2())
    335         port.path_to_apache = lambda: '/usr/sbin/httpd'
    336         capture = OutputCapture()
    337         capture.capture_output()
    338         self.assertTrue(port.check_httpd())
    339         _, _, logs = capture.restore_output()
    340         self.assertEqual('', logs)
    341 
    342     def test_httpd_returns_error_code(self):
    343         port = self.make_port(executive=MockExecutive2(exit_code=1))
    344         port.path_to_apache = lambda: '/usr/sbin/httpd'
    345         capture = OutputCapture()
    346         capture.capture_output()
    347         self.assertFalse(port.check_httpd())
    348         _, _, logs = capture.restore_output()
    349         self.assertEqual('httpd seems broken. Cannot run http tests.\n', logs)
    350 
    351     def test_test_exists(self):
    352         port = self.make_port(with_tests=True)
    353         self.assertTrue(port.test_exists('passes'))
    354         self.assertTrue(port.test_exists('passes/text.html'))
    355         self.assertFalse(port.test_exists('passes/does_not_exist.html'))
    356 
    357         self.assertTrue(port.test_exists('virtual'))
    358         self.assertFalse(port.test_exists('virtual/does_not_exist.html'))
    359         self.assertTrue(port.test_exists('virtual/passes/text.html'))
    360 
    361     def test_test_isfile(self):
    362         port = self.make_port(with_tests=True)
    363         self.assertFalse(port.test_isfile('passes'))
    364         self.assertTrue(port.test_isfile('passes/text.html'))
    365         self.assertFalse(port.test_isfile('passes/does_not_exist.html'))
    366 
    367         self.assertFalse(port.test_isfile('virtual'))
    368         self.assertTrue(port.test_isfile('virtual/passes/text.html'))
    369         self.assertFalse(port.test_isfile('virtual/does_not_exist.html'))
    370 
    371     def test_test_isdir(self):
    372         port = self.make_port(with_tests=True)
    373         self.assertTrue(port.test_isdir('passes'))
    374         self.assertFalse(port.test_isdir('passes/text.html'))
    375         self.assertFalse(port.test_isdir('passes/does_not_exist.html'))
    376         self.assertFalse(port.test_isdir('passes/does_not_exist/'))
    377 
    378         self.assertTrue(port.test_isdir('virtual'))
    379         self.assertFalse(port.test_isdir('virtual/does_not_exist.html'))
    380         self.assertFalse(port.test_isdir('virtual/does_not_exist/'))
    381         self.assertFalse(port.test_isdir('virtual/passes/text.html'))
    382 
    383     def test_tests(self):
    384         port = self.make_port(with_tests=True)
    385         tests = port.tests([])
    386         self.assertIn('passes/text.html', tests)
    387         self.assertIn('virtual/passes/text.html', tests)
    388 
    389         tests = port.tests(['passes'])
    390         self.assertIn('passes/text.html', tests)
    391         self.assertIn('passes/passes/test-virtual-passes.html', tests)
    392         self.assertNotIn('virtual/passes/text.html', tests)
    393 
    394         tests = port.tests(['virtual/passes'])
    395         self.assertNotIn('passes/text.html', tests)
    396         self.assertIn('virtual/passes/test-virtual-passes.html', tests)
    397         self.assertIn('virtual/passes/passes/test-virtual-passes.html', tests)
    398         self.assertNotIn('virtual/passes/test-virtual-virtual/passes.html', tests)
    399         self.assertNotIn('virtual/passes/virtual/passes/test-virtual-passes.html', tests)
    400 
    401     def test_build_path(self):
    402         port = self.make_port(options=optparse.Values({'build_directory': '/my-build-directory/'}))
    403         self.assertEqual(port._build_path(), '/my-build-directory/Release')
    404 
    405     def test_dont_require_http_server(self):
    406         port = self.make_port()
    407         self.assertEqual(port.requires_http_server(), False)
    408 
    409 
    410 class NaturalCompareTest(unittest.TestCase):
    411     def setUp(self):
    412         self._port = TestPort(MockSystemHost())
    413 
    414     def assert_cmp(self, x, y, result):
    415         self.assertEqual(cmp(self._port._natural_sort_key(x), self._port._natural_sort_key(y)), result)
    416 
    417     def test_natural_compare(self):
    418         self.assert_cmp('a', 'a', 0)
    419         self.assert_cmp('ab', 'a', 1)
    420         self.assert_cmp('a', 'ab', -1)
    421         self.assert_cmp('', '', 0)
    422         self.assert_cmp('', 'ab', -1)
    423         self.assert_cmp('1', '2', -1)
    424         self.assert_cmp('2', '1', 1)
    425         self.assert_cmp('1', '10', -1)
    426         self.assert_cmp('2', '10', -1)
    427         self.assert_cmp('foo_1.html', 'foo_2.html', -1)
    428         self.assert_cmp('foo_1.1.html', 'foo_2.html', -1)
    429         self.assert_cmp('foo_1.html', 'foo_10.html', -1)
    430         self.assert_cmp('foo_2.html', 'foo_10.html', -1)
    431         self.assert_cmp('foo_23.html', 'foo_10.html', 1)
    432         self.assert_cmp('foo_23.html', 'foo_100.html', -1)
    433 
    434 
    435 class KeyCompareTest(unittest.TestCase):
    436     def setUp(self):
    437         self._port = TestPort(MockSystemHost())
    438 
    439     def assert_cmp(self, x, y, result):
    440         self.assertEqual(cmp(self._port.test_key(x), self._port.test_key(y)), result)
    441 
    442     def test_test_key(self):
    443         self.assert_cmp('/a', '/a', 0)
    444         self.assert_cmp('/a', '/b', -1)
    445         self.assert_cmp('/a2', '/a10', -1)
    446         self.assert_cmp('/a2/foo', '/a10/foo', -1)
    447         self.assert_cmp('/a/foo11', '/a/foo2', 1)
    448         self.assert_cmp('/ab', '/a/a/b', -1)
    449         self.assert_cmp('/a/a/b', '/ab', 1)
    450         self.assert_cmp('/foo-bar/baz', '/foo/baz', -1)
    451 
    452 
    453 class VirtualTestSuiteTest(unittest.TestCase):
    454     def test_basic(self):
    455         suite = VirtualTestSuite('suite', 'base/foo', ['--args'])
    456         self.assertEqual(suite.name, 'virtual/suite/base/foo')
    457         self.assertEqual(suite.base, 'base/foo')
    458         self.assertEqual(suite.args, ['--args'])
    459 
    460     def test_no_slash(self):
    461         suite = VirtualTestSuite('suite/bar', 'base/foo', ['--args'])
    462         self.assertFalse(hasattr(suite, 'name'))
    463         self.assertFalse(hasattr(suite, 'base'))
    464         self.assertFalse(hasattr(suite, 'args'))
    465 
    466     def test_legacy(self):
    467         suite = VirtualTestSuite('suite/bar', 'base/foo', ['--args'], use_legacy_naming=True)
    468         self.assertEqual(suite.name, 'virtual/suite/bar')
    469         self.assertEqual(suite.base, 'base/foo')
    470         self.assertEqual(suite.args, ['--args'])
    471