Skip to content

Commit ccb49ea

Browse files
committed
Added tests.
1 parent 394e914 commit ccb49ea

File tree

5 files changed

+270
-0
lines changed

5 files changed

+270
-0
lines changed

.github/workflows/tests.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: ["master"]
6+
pull_request:
7+
branches: ["master"]
8+
9+
jobs:
10+
tests:
11+
name: PHPUnit (PHP ${{ matrix.php }})
12+
runs-on: ubuntu-latest
13+
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
php: ["8.5"]
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup PHP
24+
uses: shivammathur/setup-php@v2
25+
with:
26+
php-version: ${{ matrix.php }}
27+
coverage: none
28+
29+
- name: Install dependencies
30+
run: composer install --no-interaction --prefer-dist --optimize-autoloader
31+
32+
- name: Run tests
33+
run: vendor/bin/phpunit
34+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/vendor/
22
composer.lock
3+
.phpunit.result.cache

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
"Dragonwize\\PhpCodeQuality\\": "src/"
1818
}
1919
},
20+
"autoload-dev": {
21+
"psr-4": {
22+
"Dragonwize\\PhpCodeQuality\\Tests\\": "tests/"
23+
}
24+
},
2025
"authors": [
2126
{
2227
"name": "dragonwize",

phpunit.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
>
7+
<testsuites>
8+
<testsuite name="php-code-quality">
9+
<directory>tests</directory>
10+
</testsuite>
11+
</testsuites>
12+
13+
<source>
14+
<include>
15+
<directory>src</directory>
16+
</include>
17+
</source>
18+
</phpunit>
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Dragonwize\PhpCodeQuality\Tests\PhpCsFixer\Rule;
6+
7+
use Dragonwize\PhpCodeQuality\PhpCsFixer\Rule\VariableNameCaseFixer;
8+
use PhpCsFixer\Tokenizer\Tokens;
9+
use PHPUnit\Framework\Attributes\DataProvider;
10+
use PHPUnit\Framework\TestCase;
11+
12+
final class VariableNameCaseFixerTest extends TestCase
13+
{
14+
private VariableNameCaseFixer $fixer;
15+
16+
protected function setUp(): void
17+
{
18+
$this->fixer = new VariableNameCaseFixer();
19+
Tokens::clearCache();
20+
}
21+
22+
protected function tearDown(): void
23+
{
24+
Tokens::clearCache();
25+
}
26+
27+
// -------------------------------------------------------------------------
28+
// Metadata tests
29+
// -------------------------------------------------------------------------
30+
31+
public function testGetName(): void
32+
{
33+
self::assertSame('Dragonwize/variable_name_case', $this->fixer->getName());
34+
}
35+
36+
public function testIsCandidateReturnsTrueWhenVariableTokenPresent(): void
37+
{
38+
$tokens = Tokens::fromCode('<?php $foo = 1;');
39+
40+
self::assertTrue($this->fixer->isCandidate($tokens));
41+
}
42+
43+
public function testIsCandidateReturnsFalseForCodeWithNoVariables(): void
44+
{
45+
$tokens = Tokens::fromCode('<?php echo "hello";');
46+
47+
self::assertFalse($this->fixer->isCandidate($tokens));
48+
}
49+
50+
// -------------------------------------------------------------------------
51+
// fix() – snake_case to camelCase conversion
52+
// -------------------------------------------------------------------------
53+
54+
#[DataProvider('provideSnakeCaseToCamelCaseCases')]
55+
public function testFixConvertsSnakeCaseToCamelCase(string $input, string $expected): void
56+
{
57+
$tokens = Tokens::fromCode($input);
58+
$this->fixer->fix(new \SplFileInfo(__FILE__), $tokens);
59+
60+
self::assertSame($expected, $tokens->generateCode());
61+
}
62+
63+
/**
64+
* @return iterable<string, array{string, string}>
65+
*/
66+
public static function provideSnakeCaseToCamelCaseCases(): iterable
67+
{
68+
// The $ prefix acts as a non-delimiter boundary so the first letter after $
69+
// is NOT uppercased; only letters following an underscore are uppercased.
70+
// Result is lowerCamelCase (e.g. $my_variable → $myVariable).
71+
72+
yield 'single underscore prefix word' => [
73+
'<?php $my_variable = 1;',
74+
'<?php $myVariable = 1;',
75+
];
76+
77+
yield 'multiple underscore segments' => [
78+
'<?php $first_second_third = true;',
79+
'<?php $firstSecondThird = true;',
80+
];
81+
82+
yield 'assignment and usage' => [
83+
'<?php $some_value = 10; echo $some_value;',
84+
'<?php $someValue = 10; echo $someValue;',
85+
];
86+
87+
yield 'multiple distinct variables' => [
88+
'<?php $foo_bar = 1; $baz_qux = 2;',
89+
'<?php $fooBar = 1; $bazQux = 2;',
90+
];
91+
92+
yield 'variable in function argument' => [
93+
'<?php function test($my_param) { return $my_param; }',
94+
'<?php function test($myParam) { return $myParam; }',
95+
];
96+
97+
yield 'variable in foreach' => [
98+
'<?php foreach ($items as $item_key => $item_value) {}',
99+
'<?php foreach ($items as $itemKey => $itemValue) {}',
100+
];
101+
}
102+
103+
// -------------------------------------------------------------------------
104+
// fix() – already-correct variables are left unchanged
105+
// -------------------------------------------------------------------------
106+
107+
#[DataProvider('provideAlreadyCamelCaseCases')]
108+
public function testFixDoesNotModifyAlreadyCamelCaseVariables(string $code): void
109+
{
110+
$tokens = Tokens::fromCode($code);
111+
$this->fixer->fix(new \SplFileInfo(__FILE__), $tokens);
112+
113+
self::assertSame($code, $tokens->generateCode());
114+
}
115+
116+
/**
117+
* @return iterable<string, array{string}>
118+
*/
119+
public static function provideAlreadyCamelCaseCases(): iterable
120+
{
121+
yield 'simple camelCase' => ['<?php $myVariable = 1;'];
122+
yield 'single word lowercase' => ['<?php $foo = 1;'];
123+
yield 'single word uppercase first letter' => ['<?php $Foo = 1;'];
124+
yield 'long camelCase' => ['<?php $someVeryLongVariableName = true;'];
125+
}
126+
127+
// -------------------------------------------------------------------------
128+
// fix() – $this is never renamed
129+
// -------------------------------------------------------------------------
130+
131+
public function testFixDoesNotRenameThis(): void
132+
{
133+
$code = '<?php class Foo { public function bar() { return $this->baz; } }';
134+
$tokens = Tokens::fromCode($code);
135+
$this->fixer->fix(new \SplFileInfo(__FILE__), $tokens);
136+
137+
self::assertSame($code, $tokens->generateCode());
138+
}
139+
140+
// -------------------------------------------------------------------------
141+
// fix() – superglobals are never renamed
142+
// -------------------------------------------------------------------------
143+
144+
#[DataProvider('provideSuperglobalCases')]
145+
public function testFixDoesNotRenameSuperglobals(string $code): void
146+
{
147+
$tokens = Tokens::fromCode($code);
148+
$this->fixer->fix(new \SplFileInfo(__FILE__), $tokens);
149+
150+
self::assertSame($code, $tokens->generateCode());
151+
}
152+
153+
/**
154+
* Variables that exist as keys in $GLOBALS are skipped by the fixer.
155+
* In CLI (PHPUnit) context, the available superglobals in $GLOBALS are:
156+
* _GET, _POST, _COOKIE, _FILES, _SERVER, argv, argc.
157+
*
158+
* @return iterable<string, array{string}>
159+
*/
160+
public static function provideSuperglobalCases(): iterable
161+
{
162+
yield '$_GET' => ['<?php $value = $_GET["key"];'];
163+
yield '$_POST' => ['<?php $value = $_POST["key"];'];
164+
yield '$_SERVER' => ['<?php $host = $_SERVER["HTTP_HOST"];'];
165+
yield '$_COOKIE' => ['<?php $c = $_COOKIE["name"];'];
166+
yield '$_FILES' => ['<?php $f = $_FILES["upload"];'];
167+
yield '$GLOBALS' => ['<?php $GLOBALS["foo"] = 1;'];
168+
}
169+
170+
// -------------------------------------------------------------------------
171+
// fix() – empty token stream does nothing
172+
// -------------------------------------------------------------------------
173+
174+
public function testFixOnEmptyTokensDoesNothing(): void
175+
{
176+
// Tokens::fromCode with a non-PHP string produces a near-empty stream
177+
// We verify no exception is thrown and nothing breaks.
178+
$tokens = new Tokens();
179+
180+
// Should not throw.
181+
$this->fixer->fix(new \SplFileInfo(__FILE__), $tokens);
182+
183+
self::assertSame(0, $tokens->count());
184+
}
185+
186+
// -------------------------------------------------------------------------
187+
// fix() – code without any variables is unchanged
188+
// -------------------------------------------------------------------------
189+
190+
public function testFixDoesNotModifyCodeWithNoVariables(): void
191+
{
192+
$code = '<?php echo "hello world";';
193+
$tokens = Tokens::fromCode($code);
194+
$this->fixer->fix(new \SplFileInfo(__FILE__), $tokens);
195+
196+
self::assertSame($code, $tokens->generateCode());
197+
}
198+
199+
// -------------------------------------------------------------------------
200+
// fix() – mixed snake_case and camelCase in same file
201+
// -------------------------------------------------------------------------
202+
203+
public function testFixHandlesMixedVariablesInSameFile(): void
204+
{
205+
$input = '<?php $already_camel = 1; $alreadyCamel = 2;';
206+
$tokens = Tokens::fromCode($input);
207+
$this->fixer->fix(new \SplFileInfo(__FILE__), $tokens);
208+
209+
// $already_camel becomes $alreadyCamel (lowerCamelCase); the existing $alreadyCamel stays unchanged.
210+
self::assertSame('<?php $alreadyCamel = 1; $alreadyCamel = 2;', $tokens->generateCode());
211+
}
212+
}

0 commit comments

Comments
 (0)