Skip to content

Commit 80dc260

Browse files
Copilotswissspidy
andcommitted
Add comprehensive PHPUnit tests for Distignore_Filter_Iterator
Created 9 test methods covering all major functionality: - test_filters_ignored_files: Verifies ignored files are not yielded - test_tracks_ignored_directories: Ensures directories are tracked but files inside are filtered - test_get_excluded_files: Tests getExcludedFiles() method - test_caching_avoids_duplicate_checks: Verifies caching behavior - test_has_children_prevents_descent: Tests that hasChildren() prevents descent into ignored dirs - test_negation_patterns: Validates complex negation patterns work correctly - test_get_error_for_item_returns_null: Tests error handling - test_nested_directory_filtering: Ensures multi-level directories are handled - test_children_share_state: Verifies child iterators share cache and excluded files All 9 tests pass with 24 assertions. Tests include setup/teardown with temporary directories. Co-authored-by: swissspidy <[email protected]>
1 parent 9ce41bd commit 80dc260

File tree

2 files changed

+259
-0
lines changed

2 files changed

+259
-0
lines changed

.phpunit.result.cache

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":2,"defects":[],"times":{"Distignore_Filter_Iterator_Test::test_filters_ignored_files":0.002,"Distignore_Filter_Iterator_Test::test_tracks_ignored_directories":0.001,"Distignore_Filter_Iterator_Test::test_get_excluded_files":0.001,"Distignore_Filter_Iterator_Test::test_caching_avoids_duplicate_checks":0,"Distignore_Filter_Iterator_Test::test_has_children_prevents_descent":0.001,"Distignore_Filter_Iterator_Test::test_negation_patterns":0.001,"Distignore_Filter_Iterator_Test::test_get_error_for_item_returns_null":0,"Distignore_Filter_Iterator_Test::test_nested_directory_filtering":0.001,"Distignore_Filter_Iterator_Test::test_children_share_state":0.001}}
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,264 @@
11
<?php
22

33
use WP_CLI\Tests\TestCase;
4+
use Inmarelibero\GitIgnoreChecker\GitIgnoreChecker;
45

56
class Distignore_Filter_Iterator_Test extends TestCase {
7+
8+
/**
9+
* Temporary directory for test files.
10+
*
11+
* @var string
12+
*/
13+
private $temp_dir;
14+
15+
/**
16+
* Set up test environment.
17+
*/
18+
public function setUp(): void {
19+
parent::setUp();
20+
$this->temp_dir = sys_get_temp_dir() . '/distignore-test-' . uniqid();
21+
mkdir( $this->temp_dir );
22+
}
23+
24+
/**
25+
* Clean up test environment.
26+
*/
27+
public function tearDown(): void {
28+
if ( is_dir( $this->temp_dir ) ) {
29+
$this->recursiveDelete( $this->temp_dir );
30+
}
31+
parent::tearDown();
32+
}
33+
34+
/**
35+
* Recursively delete a directory.
36+
*
37+
* @param string $dir Directory to delete.
38+
*/
39+
private function recursiveDelete( $dir ) {
40+
if ( ! is_dir( $dir ) ) {
41+
return;
42+
}
43+
$files = array_diff( scandir( $dir ), array( '.', '..' ) );
44+
foreach ( $files as $file ) {
45+
$path = $dir . '/' . $file;
46+
is_dir( $path ) ? $this->recursiveDelete( $path ) : unlink( $path );
47+
}
48+
rmdir( $dir );
49+
}
50+
51+
/**
52+
* Test that the iterator filters out ignored files.
53+
*/
54+
public function test_filters_ignored_files() {
55+
// Create test structure.
56+
file_put_contents( $this->temp_dir . '/included.txt', 'test' );
57+
file_put_contents( $this->temp_dir . '/ignored.log', 'test' );
58+
file_put_contents( $this->temp_dir . '/.distignore', "*.log\n" );
59+
60+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
61+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
62+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
63+
$recursive_iter = new RecursiveIteratorIterator( $filter_iter, RecursiveIteratorIterator::SELF_FIRST );
64+
65+
$files = [];
66+
foreach ( $recursive_iter as $item ) {
67+
$files[] = basename( $item->getPathname() );
68+
}
69+
70+
$this->assertContains( 'included.txt', $files );
71+
$this->assertContains( '.distignore', $files );
72+
$this->assertNotContains( 'ignored.log', $files, 'Ignored file should not be yielded' );
73+
}
74+
75+
/**
76+
* Test that ignored directories are tracked but files inside are not yielded.
77+
*/
78+
public function test_tracks_ignored_directories() {
79+
// Create test structure.
80+
mkdir( $this->temp_dir . '/node_modules' );
81+
file_put_contents( $this->temp_dir . '/node_modules/package.json', '{}' );
82+
file_put_contents( $this->temp_dir . '/index.php', '<?php' );
83+
file_put_contents( $this->temp_dir . '/.distignore', "node_modules\n" );
84+
85+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
86+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
87+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
88+
$recursive_iter = new RecursiveIteratorIterator( $filter_iter, RecursiveIteratorIterator::SELF_FIRST );
89+
90+
$files = [];
91+
foreach ( $recursive_iter as $item ) {
92+
$relative_path = str_replace( $this->temp_dir, '', $item->getPathname() );
93+
$files[] = $relative_path;
94+
}
95+
96+
$this->assertContains( '/index.php', $files );
97+
$this->assertContains( '/.distignore', $files );
98+
$this->assertContains( '/node_modules', $files, 'Ignored directory should be yielded for tracking' );
99+
$this->assertNotContains( '/node_modules/package.json', $files, 'Files inside ignored directory should not be yielded' );
100+
}
101+
102+
/**
103+
* Test that getExcludedFiles returns the correct list.
104+
*/
105+
public function test_get_excluded_files() {
106+
mkdir( $this->temp_dir . '/ignored_dir' );
107+
file_put_contents( $this->temp_dir . '/ignored_dir/file.txt', 'test' );
108+
file_put_contents( $this->temp_dir . '/included.txt', 'test' );
109+
file_put_contents( $this->temp_dir . '/.distignore', "ignored_dir\n" );
110+
111+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
112+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
113+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
114+
$recursive_iter = new RecursiveIteratorIterator( $filter_iter, RecursiveIteratorIterator::SELF_FIRST );
115+
116+
// Iterate to populate excluded files.
117+
iterator_to_array( $recursive_iter );
118+
119+
$excluded = $filter_iter->getExcludedFiles();
120+
121+
$this->assertContains( '/ignored_dir', $excluded );
122+
$this->assertNotContains( '/included.txt', $excluded );
123+
}
124+
125+
/**
126+
* Test caching behavior to avoid duplicate checks.
127+
*/
128+
public function test_caching_avoids_duplicate_checks() {
129+
file_put_contents( $this->temp_dir . '/test.txt', 'test' );
130+
file_put_contents( $this->temp_dir . '/.distignore', "*.log\n" );
131+
132+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
133+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
134+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
135+
136+
// First call should cache the result.
137+
$result1 = $filter_iter->isPathIgnoredCached( '/test.txt' );
138+
// Second call should use cache.
139+
$result2 = $filter_iter->isPathIgnoredCached( '/test.txt' );
140+
141+
$this->assertSame( $result1, $result2 );
142+
$this->assertFalse( $result1 ); // test.txt should not be ignored.
143+
}
144+
145+
/**
146+
* Test that hasChildren prevents descent into ignored directories.
147+
*/
148+
public function test_has_children_prevents_descent() {
149+
mkdir( $this->temp_dir . '/node_modules' );
150+
file_put_contents( $this->temp_dir . '/node_modules/file1.js', 'test' );
151+
file_put_contents( $this->temp_dir . '/node_modules/file2.js', 'test' );
152+
file_put_contents( $this->temp_dir . '/.distignore', "node_modules\n" );
153+
154+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
155+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
156+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
157+
$recursive_iter = new RecursiveIteratorIterator( $filter_iter, RecursiveIteratorIterator::SELF_FIRST );
158+
159+
$files = [];
160+
foreach ( $recursive_iter as $item ) {
161+
$relative_path = str_replace( $this->temp_dir, '', $item->getPathname() );
162+
$files[] = $relative_path;
163+
}
164+
165+
// The node_modules directory should be yielded but its files should not.
166+
$this->assertContains( '/node_modules', $files );
167+
$this->assertNotContains( '/node_modules/file1.js', $files );
168+
$this->assertNotContains( '/node_modules/file2.js', $files );
169+
}
170+
171+
/**
172+
* Test handling of negation patterns.
173+
*/
174+
public function test_negation_patterns() {
175+
mkdir( $this->temp_dir . '/frontend' );
176+
mkdir( $this->temp_dir . '/frontend/build' );
177+
file_put_contents( $this->temp_dir . '/frontend/source.ts', 'test' );
178+
file_put_contents( $this->temp_dir . '/frontend/build/output.js', 'test' );
179+
file_put_contents( $this->temp_dir . '/.distignore', "frontend/*\n!/frontend/build/\n" );
180+
181+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
182+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
183+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
184+
$recursive_iter = new RecursiveIteratorIterator( $filter_iter, RecursiveIteratorIterator::SELF_FIRST );
185+
186+
$files = [];
187+
foreach ( $recursive_iter as $item ) {
188+
$relative_path = str_replace( $this->temp_dir, '', $item->getPathname() );
189+
$files[] = $relative_path;
190+
}
191+
192+
$this->assertContains( '/frontend', $files );
193+
$this->assertContains( '/frontend/build', $files );
194+
$this->assertContains( '/frontend/build/output.js', $files, 'Negated path should be included' );
195+
$this->assertNotContains( '/frontend/source.ts', $files, 'Ignored file should not be included' );
196+
}
197+
198+
/**
199+
* Test getErrorForItem returns null when no error.
200+
*/
201+
public function test_get_error_for_item_returns_null() {
202+
file_put_contents( $this->temp_dir . '/test.txt', 'test' );
203+
file_put_contents( $this->temp_dir . '/.distignore', '' );
204+
205+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
206+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
207+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
208+
209+
$error = $filter_iter->getErrorForItem( '/test.txt' );
210+
211+
$this->assertNull( $error );
212+
}
213+
214+
/**
215+
* Test that multiple levels of directories are handled correctly.
216+
*/
217+
public function test_nested_directory_filtering() {
218+
mkdir( $this->temp_dir . '/src' );
219+
mkdir( $this->temp_dir . '/src/components' );
220+
file_put_contents( $this->temp_dir . '/src/index.php', '<?php' );
221+
file_put_contents( $this->temp_dir . '/src/components/widget.php', '<?php' );
222+
file_put_contents( $this->temp_dir . '/.distignore', '' );
223+
224+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
225+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
226+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
227+
$recursive_iter = new RecursiveIteratorIterator( $filter_iter, RecursiveIteratorIterator::SELF_FIRST );
228+
229+
$files = [];
230+
foreach ( $recursive_iter as $item ) {
231+
$relative_path = str_replace( $this->temp_dir, '', $item->getPathname() );
232+
$files[] = $relative_path;
233+
}
234+
235+
$this->assertContains( '/src', $files );
236+
$this->assertContains( '/src/components', $files );
237+
$this->assertContains( '/src/index.php', $files );
238+
$this->assertContains( '/src/components/widget.php', $files );
239+
}
240+
241+
/**
242+
* Test that children share the same cache and excluded files arrays.
243+
*/
244+
public function test_children_share_state() {
245+
mkdir( $this->temp_dir . '/level1' );
246+
mkdir( $this->temp_dir . '/level1/level2' );
247+
file_put_contents( $this->temp_dir . '/level1/file1.txt', 'test' );
248+
file_put_contents( $this->temp_dir . '/level1/level2/file2.log', 'test' );
249+
file_put_contents( $this->temp_dir . '/.distignore', "*.log\n" );
250+
251+
$checker = new GitIgnoreChecker( $this->temp_dir, '.distignore' );
252+
$directory_iter = new RecursiveDirectoryIterator( $this->temp_dir, RecursiveDirectoryIterator::SKIP_DOTS );
253+
$filter_iter = new Distignore_Filter_Iterator( $directory_iter, $checker, $this->temp_dir );
254+
$recursive_iter = new RecursiveIteratorIterator( $filter_iter, RecursiveIteratorIterator::SELF_FIRST );
255+
256+
// Iterate to populate excluded files.
257+
iterator_to_array( $recursive_iter );
258+
259+
$excluded = $filter_iter->getExcludedFiles();
260+
261+
// The .log file in level2 should be tracked even though it was found by a child iterator.
262+
$this->assertContains( '/level1/level2/file2.log', $excluded );
263+
}
6264
}

0 commit comments

Comments
 (0)