1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-23 14:00:55 +01:00

Search for more test locations in PHPUnitTestEngine

Summary:
See https://github.com/facebook/arcanist/pull/53

  - Work correctly for directories with `%` in their name; this breaks under sprintf().
  - Search for `src/` -> `tests/` style directories.
  - Add coverage for search paths.

Test Plan:
Ran unit tests.

I don't have a working test case for PHPUnit tests, can one of you guys apply this and verify I didn't break your setups?

Reviewers: quard, aurelijus

Reviewed By: aurelijus

CC: aran

Differential Revision: https://secure.phabricator.com/D3558
This commit is contained in:
epriestley 2012-09-29 18:03:43 -07:00
parent 36acc9a01d
commit 777c119a0e
3 changed files with 186 additions and 38 deletions

View file

@ -135,6 +135,7 @@ phutil_register_library_map(array(
'ComprehensiveLintEngine' => 'lint/engine/ComprehensiveLintEngine.php',
'ExampleLintEngine' => 'lint/engine/ExampleLintEngine.php',
'NoseTestEngine' => 'unit/engine/NoseTestEngine.php',
'PHPUnitTestEngineTestCase' => 'unit/engine/__tests__/PHPUnitTestEngineTestCase.php',
'PhpunitTestEngine' => 'unit/engine/PhpunitTestEngine.php',
'PhutilLintEngine' => 'lint/engine/PhutilLintEngine.php',
'PhutilUnitTestEngine' => 'unit/engine/PhutilUnitTestEngine.php',
@ -239,6 +240,7 @@ phutil_register_library_map(array(
'ComprehensiveLintEngine' => 'ArcanistLintEngine',
'ExampleLintEngine' => 'ArcanistLintEngine',
'NoseTestEngine' => 'ArcanistBaseUnitTestEngine',
'PHPUnitTestEngineTestCase' => 'ArcanistTestCase',
'PhpunitTestEngine' => 'ArcanistBaseUnitTestEngine',
'PhutilLintEngine' => 'ArcanistLintEngine',
'PhutilUnitTestEngine' => 'ArcanistBaseUnitTestEngine',

View file

@ -272,54 +272,139 @@ final class PhpunitTestEngine extends ArcanistBaseUnitTestEngine {
/**
* Some nasty guessing here.
*
* Walk up to the project root trying to find
* [Tt]ests directory and replicate the structure there.
*
* Assume that the class path is
* /www/project/module/package/subpackage/FooBar.php
* and a project root is /www/project it will look for it by these paths:
* /www/project/module/package/subpackage/[Tt]ests/FooBarTest.php
* /www/project/module/package/[Tt]ests/subpackage/FooBarTest.php
* /www/project/module/[Tt]ests/package/subpackage/FooBarTest.php
* /www/project/Tt]ests/module/package/subpackage/FooBarTest.php
*
* TODO: Add support for finding tests based on PSR-1 naming conventions:
* /www/project/src/Something/Foo/Bar.php tests should be detected in
* /www/project/tests/Something/Foo/BarTest.php
* Search for test cases for a given file in a large number of "reasonable"
* locations. See @{method:getSearchLocationsForTests} for specifics.
*
* TODO: Add support for finding tests in testsuite folders from
* phpunit.xml configuration.
*
* @param string $path
*
* @return string|boolean
* @param string PHP file to locate test cases for.
* @return string|null Path to test cases, or null.
*/
private function findTestFile($path) {
$expected_file = substr(basename($path), 0, -4) . 'Test.php';
$expected_dir = null;
$dirname = dirname($path);
foreach (Filesystem::walkToRoot($dirname) as $dir) {
$expected_dir = DIRECTORY_SEPARATOR
. substr($dirname, strlen($dir) + 1)
. $expected_dir;
$look_for = $dir . DIRECTORY_SEPARATOR
. '%s' . $expected_dir . $expected_file;
$root = $this->projectRoot;
$path = Filesystem::resolvePath($path, $root);
if (Filesystem::pathExists(sprintf($look_for, 'Tests'))) {
return sprintf($look_for, 'Tests');
} else if (Filesystem::pathExists(sprintf($look_for, 'Tests'))) {
return sprintf($look_for, 'Tests');
$file = basename($path);
$possible_files = array(
$file,
substr($file, 0, -4).'Test.php',
);
$search = self::getSearchLocationsForTests($path);
foreach ($search as $search_path) {
foreach ($possible_files as $possible_file) {
$full_path = $search_path.$possible_file;
if (!Filesystem::pathExists($full_path)) {
// If the file doesn't exist, it's clearly a miss.
continue;
}
if (!Filesystem::isDescendant($full_path, $root)) {
// Don't look above the project root.
continue;
}
if (Filesystem::resolvePath($full_path) == $path) {
// Don't return the original file.
continue;
}
return $full_path;
}
if ($dir == $this->projectRoot) {
break;
}
}
return false;
return null;
}
/**
* Get places to look for PHP Unit tests that cover a given file. For some
* file "/a/b/c/X.php", we look in the same directory:
*
* /a/b/c/
*
* We then look in all parent directories for a directory named "tests/"
* (or "Tests/"):
*
* /a/b/c/tests/
* /a/b/tests/
* /a/tests/
* /tests/
*
* We also try to replace each directory component with "tests/":
*
* /a/b/tests/
* /a/tests/c/
* /tests/b/c/
*
* We also try to add "tests/" at each directory level:
*
* /a/b/c/tests/
* /a/b/tests/c/
* /a/tests/b/c/
* /tests/a/b/c/
*
* This finds tests with a layout like:
*
* docs/
* src/
* tests/
*
* ...or similar. This list will be further pruned by the caller; it is
* intentionally filesystem-agnostic to be unit testable.
*
* @param string PHP file to locate test cases for.
* @return list<string> List of directories to search for tests in.
*/
public static function getSearchLocationsForTests($path) {
$file = basename($path);
$dir = dirname($path);
$test_dir_names = array('tests', 'Tests');
$try_directories = array();
// Try in the current directory.
$try_directories[] = array($dir);
// Try in a tests/ directory anywhere in the ancestry.
foreach (Filesystem::walkToRoot($dir) as $parent_dir) {
if ($parent_dir == '/') {
// We'll restore this later.
$parent_dir = '';
}
foreach ($test_dir_names as $test_dir_name) {
$try_directories[] = array($parent_dir, $test_dir_name);
}
}
// Try replacing each directory component with 'tests/'.
$parts = trim($dir, DIRECTORY_SEPARATOR);
$parts = explode(DIRECTORY_SEPARATOR, $parts);
foreach (array_reverse(array_keys($parts)) as $key) {
foreach ($test_dir_names as $test_dir_name) {
$try = $parts;
$try[$key] = $test_dir_name;
array_unshift($try, '');
$try_directories[] = $try;
}
}
// Try adding 'tests/' at each level.
foreach (array_reverse(array_keys($parts)) as $key) {
foreach ($test_dir_names as $test_dir_name) {
$try = $parts;
$try[$key] = $test_dir_name.DIRECTORY_SEPARATOR.$try[$key];
array_unshift($try, '');
$try_directories[] = $try;
}
}
$results = array();
foreach ($try_directories as $parts) {
$results[implode(DIRECTORY_SEPARATOR, $parts).DIRECTORY_SEPARATOR] = true;
}
return array_keys($results);
}
/**

View file

@ -0,0 +1,61 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Tests for @{class:PHPUnitTestEngine}.
*
* @group testcase
*/
final class PHPUnitTestEngineTestCase extends ArcanistTestCase {
public function testSearchLocations() {
$path = '/path/to/some/file/X.php';
$this->assertEqual(
array(
'/path/to/some/file/',
'/path/to/some/file/tests/',
'/path/to/some/file/Tests/',
'/path/to/some/tests/',
'/path/to/some/Tests/',
'/path/to/tests/',
'/path/to/Tests/',
'/path/tests/',
'/path/Tests/',
'/tests/',
'/Tests/',
'/path/to/tests/file/',
'/path/to/Tests/file/',
'/path/tests/some/file/',
'/path/Tests/some/file/',
'/tests/to/some/file/',
'/Tests/to/some/file/',
'/path/to/some/tests/file/',
'/path/to/some/Tests/file/',
'/path/to/tests/some/file/',
'/path/to/Tests/some/file/',
'/path/tests/to/some/file/',
'/path/Tests/to/some/file/',
'/tests/path/to/some/file/',
'/Tests/path/to/some/file/',
),
PhpunitTestEngine::getSearchLocationsForTests($path));
}
}