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 29import logging 30import optparse 31import sys 32import tempfile 33import unittest 34 35from webkitpy.common.system.executive import Executive, ScriptError 36from webkitpy.common.system import executive_mock 37from webkitpy.common.system.filesystem_mock import MockFileSystem 38from webkitpy.common.system.outputcapture import OutputCapture 39from webkitpy.common.system.path import abspath_to_uri 40from webkitpy.tool.mocktool import MockOptions 41from webkitpy.common.system.executive_mock import MockExecutive, MockExecutive2 42from webkitpy.common.system.systemhost import SystemHost 43from webkitpy.common.system.systemhost_mock import MockSystemHost 44 45from webkitpy.layout_tests.port import Port, Driver, DriverOutput 46from webkitpy.layout_tests.port.base import VirtualTestSuite 47from webkitpy.layout_tests.port.test import add_unit_tests_to_mock_filesystem, TestPort 48 49class 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 # Test for missing newline at end of file diff output. 147 content_a = "Hello\n\nWorld" 148 content_b = "Hello\n\nWorld\n\n\n" 149 expected = "--- exp.txt\n+++ act.txt\n@@ -1,3 +1,5 @@\n Hello\n \n-World\n\ No newline at end of file\n+World\n+\n+\n" 150 self.assertEqual(expected, port.diff_text(content_a, content_b, 'exp.txt', 'act.txt')) 151 152 def test_setup_test_run(self): 153 port = self.make_port() 154 # This routine is a no-op. We just test it for coverage. 155 port.setup_test_run() 156 157 def test_test_dirs(self): 158 port = self.make_port() 159 port.host.filesystem.write_text_file(port.layout_tests_dir() + '/canvas/test', '') 160 port.host.filesystem.write_text_file(port.layout_tests_dir() + '/css2.1/test', '') 161 dirs = port.test_dirs() 162 self.assertIn('canvas', dirs) 163 self.assertIn('css2.1', dirs) 164 165 def test_skipped_perf_tests(self): 166 port = self.make_port() 167 168 def add_text_file(dirname, filename, content='some content'): 169 dirname = port.host.filesystem.join(port.perf_tests_dir(), dirname) 170 port.host.filesystem.maybe_make_directory(dirname) 171 port.host.filesystem.write_text_file(port.host.filesystem.join(dirname, filename), content) 172 173 add_text_file('inspector', 'test1.html') 174 add_text_file('inspector', 'unsupported_test1.html') 175 add_text_file('inspector', 'test2.html') 176 add_text_file('inspector/resources', 'resource_file.html') 177 add_text_file('unsupported', 'unsupported_test2.html') 178 add_text_file('', 'Skipped', '\n'.join(['Layout', '', 'SunSpider', 'Supported/some-test.html'])) 179 self.assertEqual(port.skipped_perf_tests(), ['Layout', 'SunSpider', 'Supported/some-test.html']) 180 181 def test_get_option__set(self): 182 options, args = optparse.OptionParser().parse_args([]) 183 options.foo = 'bar' 184 port = self.make_port(options=options) 185 self.assertEqual(port.get_option('foo'), 'bar') 186 187 def test_get_option__unset(self): 188 port = self.make_port() 189 self.assertIsNone(port.get_option('foo')) 190 191 def test_get_option__default(self): 192 port = self.make_port() 193 self.assertEqual(port.get_option('foo', 'bar'), 'bar') 194 195 def test_additional_platform_directory(self): 196 port = self.make_port(port_name='foo') 197 port.default_baseline_search_path = lambda: ['LayoutTests/platform/foo'] 198 layout_test_dir = port.layout_tests_dir() 199 test_file = 'fast/test.html' 200 201 # No additional platform directory 202 self.assertEqual( 203 port.expected_baselines(test_file, '.txt'), 204 [(None, 'fast/test-expected.txt')]) 205 self.assertEqual(port.baseline_path(), 'LayoutTests/platform/foo') 206 207 # Simple additional platform directory 208 port._options.additional_platform_directory = ['/tmp/local-baselines'] 209 port._filesystem.write_text_file('/tmp/local-baselines/fast/test-expected.txt', 'foo') 210 self.assertEqual( 211 port.expected_baselines(test_file, '.txt'), 212 [('/tmp/local-baselines', 'fast/test-expected.txt')]) 213 self.assertEqual(port.baseline_path(), '/tmp/local-baselines') 214 215 # Multiple additional platform directories 216 port._options.additional_platform_directory = ['/foo', '/tmp/local-baselines'] 217 self.assertEqual( 218 port.expected_baselines(test_file, '.txt'), 219 [('/tmp/local-baselines', 'fast/test-expected.txt')]) 220 self.assertEqual(port.baseline_path(), '/foo') 221 222 def test_nonexistant_expectations(self): 223 port = self.make_port(port_name='foo') 224 port.expectations_files = lambda: ['/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations', '/mock-checkout/third_party/WebKit/LayoutTests/platform/nonexistant/TestExpectations'] 225 port._filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations', '') 226 self.assertEqual('\n'.join(port.expectations_dict().keys()), '/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations') 227 228 def test_additional_expectations(self): 229 port = self.make_port(port_name='foo') 230 port.port_name = 'foo' 231 port._filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/foo/TestExpectations', '') 232 port._filesystem.write_text_file( 233 '/tmp/additional-expectations-1.txt', 'content1\n') 234 port._filesystem.write_text_file( 235 '/tmp/additional-expectations-2.txt', 'content2\n') 236 237 self.assertEqual('\n'.join(port.expectations_dict().values()), '') 238 239 port._options.additional_expectations = [ 240 '/tmp/additional-expectations-1.txt'] 241 self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n') 242 243 port._options.additional_expectations = [ 244 '/tmp/nonexistent-file', '/tmp/additional-expectations-1.txt'] 245 self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n') 246 247 port._options.additional_expectations = [ 248 '/tmp/additional-expectations-1.txt', '/tmp/additional-expectations-2.txt'] 249 self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n\ncontent2\n') 250 251 def test_additional_env_var(self): 252 port = self.make_port(options=optparse.Values({'additional_env_var': ['FOO=BAR', 'BAR=FOO']})) 253 self.assertEqual(port.get_option('additional_env_var'), ['FOO=BAR', 'BAR=FOO']) 254 environment = port.setup_environ_for_server() 255 self.assertTrue(('FOO' in environment) & ('BAR' in environment)) 256 self.assertEqual(environment['FOO'], 'BAR') 257 self.assertEqual(environment['BAR'], 'FOO') 258 259 def test_find_no_paths_specified(self): 260 port = self.make_port(with_tests=True) 261 layout_tests_dir = port.layout_tests_dir() 262 tests = port.tests([]) 263 self.assertNotEqual(len(tests), 0) 264 265 def test_find_one_test(self): 266 port = self.make_port(with_tests=True) 267 tests = port.tests(['failures/expected/image.html']) 268 self.assertEqual(len(tests), 1) 269 270 def test_find_glob(self): 271 port = self.make_port(with_tests=True) 272 tests = port.tests(['failures/expected/im*']) 273 self.assertEqual(len(tests), 2) 274 275 def test_find_with_skipped_directories(self): 276 port = self.make_port(with_tests=True) 277 tests = port.tests(['userscripts']) 278 self.assertNotIn('userscripts/resources/iframe.html', tests) 279 280 def test_find_with_skipped_directories_2(self): 281 port = self.make_port(with_tests=True) 282 tests = port.tests(['userscripts/resources']) 283 self.assertEqual(tests, []) 284 285 def test_is_test_file(self): 286 filesystem = MockFileSystem() 287 self.assertTrue(Port.is_test_file(filesystem, '', 'foo.html')) 288 self.assertTrue(Port.is_test_file(filesystem, '', 'foo.svg')) 289 self.assertTrue(Port.is_test_file(filesystem, '', 'test-ref-test.html')) 290 self.assertFalse(Port.is_test_file(filesystem, '', 'foo.png')) 291 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.html')) 292 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.svg')) 293 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected.xht')) 294 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.html')) 295 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.svg')) 296 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-expected-mismatch.xhtml')) 297 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-ref.html')) 298 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-notref.html')) 299 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-notref.xht')) 300 self.assertFalse(Port.is_test_file(filesystem, '', 'foo-ref.xhtml')) 301 self.assertFalse(Port.is_test_file(filesystem, '', 'ref-foo.html')) 302 self.assertFalse(Port.is_test_file(filesystem, '', 'notref-foo.xhr')) 303 304 def test_parse_reftest_list(self): 305 port = self.make_port(with_tests=True) 306 port.host.filesystem.files['bar/reftest.list'] = "\n".join(["== test.html test-ref.html", 307 "", 308 "# some comment", 309 "!= test-2.html test-notref.html # more comments", 310 "== test-3.html test-ref.html", 311 "== test-3.html test-ref2.html", 312 "!= test-3.html test-notref.html", 313 "fuzzy(80,500) == test-3 test-ref.html"]) 314 315 # Note that we don't support the syntax in the last line; the code should ignore it, rather than crashing. 316 317 reftest_list = Port._parse_reftest_list(port.host.filesystem, 'bar') 318 self.assertEqual(reftest_list, {'bar/test.html': [('==', 'bar/test-ref.html')], 319 'bar/test-2.html': [('!=', 'bar/test-notref.html')], 320 'bar/test-3.html': [('==', 'bar/test-ref.html'), ('==', 'bar/test-ref2.html'), ('!=', 'bar/test-notref.html')]}) 321 322 def test_reference_files(self): 323 port = self.make_port(with_tests=True) 324 self.assertEqual(port.reference_files('passes/svgreftest.svg'), [('==', port.layout_tests_dir() + '/passes/svgreftest-expected.svg')]) 325 self.assertEqual(port.reference_files('passes/xhtreftest.svg'), [('==', port.layout_tests_dir() + '/passes/xhtreftest-expected.html')]) 326 self.assertEqual(port.reference_files('passes/phpreftest.php'), [('!=', port.layout_tests_dir() + '/passes/phpreftest-expected-mismatch.svg')]) 327 328 def test_operating_system(self): 329 self.assertEqual('mac', self.make_port().operating_system()) 330 331 def test_http_server_supports_ipv6(self): 332 port = self.make_port() 333 self.assertTrue(port.http_server_supports_ipv6()) 334 port.host.platform.os_name = 'cygwin' 335 self.assertFalse(port.http_server_supports_ipv6()) 336 port.host.platform.os_name = 'win' 337 self.assertFalse(port.http_server_supports_ipv6()) 338 339 def test_check_httpd_success(self): 340 port = self.make_port(executive=MockExecutive2()) 341 port.path_to_apache = lambda: '/usr/sbin/httpd' 342 capture = OutputCapture() 343 capture.capture_output() 344 self.assertTrue(port.check_httpd()) 345 _, _, logs = capture.restore_output() 346 self.assertEqual('', logs) 347 348 def test_httpd_returns_error_code(self): 349 port = self.make_port(executive=MockExecutive2(exit_code=1)) 350 port.path_to_apache = lambda: '/usr/sbin/httpd' 351 capture = OutputCapture() 352 capture.capture_output() 353 self.assertFalse(port.check_httpd()) 354 _, _, logs = capture.restore_output() 355 self.assertEqual('httpd seems broken. Cannot run http tests.\n', logs) 356 357 def test_test_exists(self): 358 port = self.make_port(with_tests=True) 359 self.assertTrue(port.test_exists('passes')) 360 self.assertTrue(port.test_exists('passes/text.html')) 361 self.assertFalse(port.test_exists('passes/does_not_exist.html')) 362 363 self.assertTrue(port.test_exists('virtual')) 364 self.assertFalse(port.test_exists('virtual/does_not_exist.html')) 365 self.assertTrue(port.test_exists('virtual/virtual_passes/passes/text.html')) 366 367 def test_test_isfile(self): 368 port = self.make_port(with_tests=True) 369 self.assertFalse(port.test_isfile('passes')) 370 self.assertTrue(port.test_isfile('passes/text.html')) 371 self.assertFalse(port.test_isfile('passes/does_not_exist.html')) 372 373 self.assertFalse(port.test_isfile('virtual')) 374 self.assertTrue(port.test_isfile('virtual/virtual_passes/passes/text.html')) 375 self.assertFalse(port.test_isfile('virtual/does_not_exist.html')) 376 377 def test_test_isdir(self): 378 port = self.make_port(with_tests=True) 379 self.assertTrue(port.test_isdir('passes')) 380 self.assertFalse(port.test_isdir('passes/text.html')) 381 self.assertFalse(port.test_isdir('passes/does_not_exist.html')) 382 self.assertFalse(port.test_isdir('passes/does_not_exist/')) 383 384 self.assertTrue(port.test_isdir('virtual')) 385 self.assertFalse(port.test_isdir('virtual/does_not_exist.html')) 386 self.assertFalse(port.test_isdir('virtual/does_not_exist/')) 387 self.assertFalse(port.test_isdir('virtual/virtual_passes/passes/text.html')) 388 389 def test_tests(self): 390 port = self.make_port(with_tests=True) 391 tests = port.tests([]) 392 self.assertIn('passes/text.html', tests) 393 self.assertIn('virtual/virtual_passes/passes/text.html', tests) 394 395 tests = port.tests(['passes']) 396 self.assertIn('passes/text.html', tests) 397 self.assertIn('passes/virtual_passes/test-virtual-passes.html', tests) 398 self.assertNotIn('virtual/virtual_passes/passes/text.html', tests) 399 400 tests = port.tests(['virtual/virtual_passes/passes']) 401 self.assertNotIn('passes/text.html', tests) 402 self.assertIn('virtual/virtual_passes/passes/test-virtual-passes.html', tests) 403 self.assertNotIn('passes/test-virtual-passes.html', tests) 404 self.assertNotIn('virtual/virtual_passes/passes/test-virtual-virtual/passes.html', tests) 405 self.assertNotIn('virtual/virtual_passes/passes/virtual_passes/passes/test-virtual-passes.html', tests) 406 407 def test_build_path(self): 408 port = self.make_port(options=optparse.Values({'build_directory': '/my-build-directory/'})) 409 self.assertEqual(port._build_path(), '/my-build-directory/Release') 410 411 def test_dont_require_http_server(self): 412 port = self.make_port() 413 self.assertEqual(port.requires_http_server(), False) 414 415 def test_can_load_actual_virtual_test_suite_file(self): 416 port = Port(SystemHost(), 'baseport') 417 418 # If this call returns successfully, we found and loaded the LayoutTests/VirtualTestSuites. 419 _ = port.virtual_test_suites() 420 421 def test_good_virtual_test_suite_file(self): 422 port = self.make_port() 423 fs = port._filesystem 424 fs.write_text_file(fs.join(port.layout_tests_dir(), 'VirtualTestSuites'), 425 '[{"prefix": "bar", "base": "fast/bar", "args": ["--bar"]}]') 426 427 # If this call returns successfully, we found and loaded the LayoutTests/VirtualTestSuites. 428 _ = port.virtual_test_suites() 429 430 def test_virtual_test_suite_file_is_not_json(self): 431 port = self.make_port() 432 fs = port._filesystem 433 fs.write_text_file(fs.join(port.layout_tests_dir(), 'VirtualTestSuites'), 434 '{[{[') 435 self.assertRaises(ValueError, port.virtual_test_suites) 436 437 def test_missing_virtual_test_suite_file(self): 438 port = self.make_port() 439 self.assertRaises(AssertionError, port.virtual_test_suites) 440 441 442class NaturalCompareTest(unittest.TestCase): 443 def setUp(self): 444 self._port = TestPort(MockSystemHost()) 445 446 def assert_cmp(self, x, y, result): 447 self.assertEqual(cmp(self._port._natural_sort_key(x), self._port._natural_sort_key(y)), result) 448 449 def test_natural_compare(self): 450 self.assert_cmp('a', 'a', 0) 451 self.assert_cmp('ab', 'a', 1) 452 self.assert_cmp('a', 'ab', -1) 453 self.assert_cmp('', '', 0) 454 self.assert_cmp('', 'ab', -1) 455 self.assert_cmp('1', '2', -1) 456 self.assert_cmp('2', '1', 1) 457 self.assert_cmp('1', '10', -1) 458 self.assert_cmp('2', '10', -1) 459 self.assert_cmp('foo_1.html', 'foo_2.html', -1) 460 self.assert_cmp('foo_1.1.html', 'foo_2.html', -1) 461 self.assert_cmp('foo_1.html', 'foo_10.html', -1) 462 self.assert_cmp('foo_2.html', 'foo_10.html', -1) 463 self.assert_cmp('foo_23.html', 'foo_10.html', 1) 464 self.assert_cmp('foo_23.html', 'foo_100.html', -1) 465 466 467class KeyCompareTest(unittest.TestCase): 468 def setUp(self): 469 self._port = TestPort(MockSystemHost()) 470 471 def assert_cmp(self, x, y, result): 472 self.assertEqual(cmp(self._port.test_key(x), self._port.test_key(y)), result) 473 474 def test_test_key(self): 475 self.assert_cmp('/a', '/a', 0) 476 self.assert_cmp('/a', '/b', -1) 477 self.assert_cmp('/a2', '/a10', -1) 478 self.assert_cmp('/a2/foo', '/a10/foo', -1) 479 self.assert_cmp('/a/foo11', '/a/foo2', 1) 480 self.assert_cmp('/ab', '/a/a/b', -1) 481 self.assert_cmp('/a/a/b', '/ab', 1) 482 self.assert_cmp('/foo-bar/baz', '/foo/baz', -1) 483 484 485class VirtualTestSuiteTest(unittest.TestCase): 486 def test_basic(self): 487 suite = VirtualTestSuite(prefix='suite', base='base/foo', args=['--args']) 488 self.assertEqual(suite.name, 'virtual/suite/base/foo') 489 self.assertEqual(suite.base, 'base/foo') 490 self.assertEqual(suite.args, ['--args']) 491 492 def test_no_slash(self): 493 self.assertRaises(AssertionError, VirtualTestSuite, prefix='suite/bar', base='base/foo', args=['--args']) 494