Skip to content

Conversation

@AlexSkrypnyk
Copy link
Member

@AlexSkrypnyk AlexSkrypnyk commented Nov 23, 2025

DrevOps.TestingPractices.DataProviderPrefix
DrevOps.TestingPractices.DataProviderMatchesTestName
DrevOps.TestingPractices.DataProviderOrder

DrevOps.TestingPractices.DataProviderPrefix

Purpose

Enforces naming conventions for PHPUnit data provider methods. This sniff ensures that data provider methods start with a configurable prefix (default: dataProvider). It provides auto-fixing capabilities to rename methods and update all references.

Configuration

  • prefix: The required prefix for data provider methods (default: dataProvider)

Examples

✓ Valid:

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderValidCases
     */
    public function testSomething($input, $expected): void {
        $this->assertEquals($expected, $input);
    }

    public function dataProviderValidCases(): array {
        return [['input1', 'expected1']];
    }
}

✗ Invalid:

class UserTest extends TestCase {
    /**
     * @dataProvider providerInvalidCases
     */
    public function testSomething($input, $expected): void {
        $this->assertEquals($expected, $input);
    }

    // ERROR: Data provider method "providerInvalidCases" should start with 
    // prefix "dataProvider", suggested name: "dataProviderInvalidCases"
    public function providerInvalidCases(): array {
        return [['input1', 'expected1']];
    }

    /**
     * @dataProvider casesForAnotherTest
     */
    public function testAnother($value): void {
        $this->assertNotEmpty($value);
    }

    // ERROR: Data provider method "casesForAnotherTest" should start with 
    // prefix "dataProvider", suggested name: "dataProviderForAnotherTest"
    public function casesForAnotherTest(): array {
        return [['value1']];
    }

    /**
     * @dataProvider provideDataForTest
     */
    public function testWithProvide($data): void {
        $this->assertIsString($data);
    }

    // ERROR: Data provider method "provideDataForTest" should start with 
    // prefix "dataProvider", suggested name: "dataProviderDataForTest"
    public function provideDataForTest(): array {
        return [['data1']];
    }
}

DrevOps.TestingPractices.DataProviderMatchesTestName

Purpose

Enforces that data provider method names match the test method names they serve. The provider name must end with the exact test name (after removing the "test" prefix). This improves code readability and maintains a clear relationship between tests and their data providers.

Configuration

None

Examples

✓ Valid:

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderUserLogin
     */
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    public function dataProviderUserLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider providerEmailValidation
     */
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }

    // Different prefix is OK, as long as it ends with "EmailValidation"
    public function providerEmailValidation(): array {
        return [['test@example.com']];
    }

    /**
     * @dataProvider AuthenticationScenarios
     */
    public function testAuthenticationScenarios($scenario): void {
        $this->assertIsString($scenario);
    }

    // No prefix is OK, as long as it matches exactly
    public function AuthenticationScenarios(): array {
        return [['scenario1']];
    }

    #[DataProvider('dataProviderPasswordValidation')]
    public function testPasswordValidation($password): void {
        $this->assertNotEmpty($password);
    }

    // Works with PHP 8 attributes too
    public function dataProviderPasswordValidation(): array {
        return [['password123']];
    }
}

✗ Invalid:

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderLogin
     */
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    // ERROR: Data provider method "dataProviderLogin" does not match test 
    // method "testUserLogin". Expected provider name to end with "UserLogin"
    public function dataProviderLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider dataProviderEmailValidationCases
     */
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }

    // ERROR: Data provider method "dataProviderEmailValidationCases" does not 
    // match test method "testEmailValidation". Expected provider name to end 
    // with "EmailValidation"
    public function dataProviderEmailValidationCases(): array {
        return [['test@example.com']];
    }

    /**
     * @dataProvider providerAuth
     */
    public function testAuthenticationScenarios($scenario): void {
        $this->assertIsString($scenario);
    }

    // ERROR: Data provider method "providerAuth" does not match test method 
    // "testAuthenticationScenarios". Expected provider name to end with 
    // "AuthenticationScenarios"
    public function providerAuth(): array {
        return [['scenario1']];
    }

    #[DataProvider('dataProviderPass')]
    public function testPasswordValidation($password): void {
        $this->assertNotEmpty($password);
    }

    // ERROR: Data provider method "dataProviderPass" does not match test 
    // method "testPasswordValidation". Expected provider name to end with 
    // "PasswordValidation"
    public function dataProviderPass(): array {
        return [['password123']];
    }
}

DrevOps.TestingPractices.DataProviderOrder

Purpose

Enforces ordering of data provider and test methods in test files. This ensures structural organization and improves code readability. By default, data providers should be defined after their test methods. Helper methods between tests and providers are allowed.

Configuration

  • providerPosition: Expected position of data provider relative to test (default: after)
    • after: Data providers should appear after their test methods (default)
    • before: Data providers should appear before their test methods

Examples

✓ Valid (providerPosition="after"):

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderUserLogin
     */
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    // Provider after test - CORRECT
    public function dataProviderUserLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider dataProviderEmailValidation
     */
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }

    // Helper methods between test and provider are allowed
    private function helperValidate(): void {
        // Helper logic
    }

    // Provider still after test - CORRECT
    public function dataProviderEmailValidation(): array {
        return [['test@example.com']];
    }

    #[DataProvider('dataProviderAuthentication')]
    public function testAuthentication($scenario): void {
        $this->assertIsString($scenario);
    }

    // Works with PHP 8 attributes too
    public function dataProviderAuthentication(): array {
        return [['scenario1']];
    }
}

✗ Invalid (providerPosition="after"):

class UserTest extends TestCase {
    // Provider before test - WRONG ORDER
    public function dataProviderUserLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider dataProviderUserLogin
     */
    // ERROR: Data provider method "dataProviderUserLogin" (line 10) appears 
    // before test method "testUserLogin" (line 17). Providers should be 
    // defined after their test methods
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    // Another provider before test - WRONG ORDER
    public function dataProviderEmailValidation(): array {
        return [['test@example.com']];
    }

    /**
     * @dataProvider dataProviderEmailValidation
     */
    // ERROR: Data provider method "dataProviderEmailValidation" (line 24) 
    // appears before test method "testEmailValidation" (line 31). Providers 
    // should be defined after their test methods
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }
}

Note: All three rules:

  • Work with both @dataProvider annotations and PHP 8 #[DataProvider] attributes
  • Skip external providers referenced as ClassName::methodName
  • Only apply to test classes (classes ending with Test or TestCase, or extending TestCase)

@AlexSkrypnyk
Copy link
Member Author

@coderabbitai
create a description with examples for these 3 new rules

@coderabbitai
Copy link

coderabbitai bot commented Nov 23, 2025

📝 Walkthrough

Walkthrough

Adds three new PHP_CodeSniffer sniffs enforcing PHPUnit data-provider conventions (prefix, provider name matching test name, provider/test ordering), updates ruleset to enable them, and introduces extensive unit/functional tests plus fixtures demonstrating valid and invalid cases.

Changes

Cohort / File(s) Summary
Sniff implementations
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php, src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php, src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php
Three new sniffs: DataProviderPrefixSniff (enforces configurable prefix), DataProviderMatchesTestNameSniff (ensures provider name ends with corresponding test name), and DataProviderOrderSniff (enforces provider position relative to test methods). Each registers appropriate tokens and emits PHP_CodeSniffer errors.
Ruleset
src/DrevOps/ruleset.xml
Registers the three new sniffs and configures properties: prefix="dataProvider" for DataProviderPrefix and providerPosition="after" for DataProviderOrder.
Valid fixtures
tests/Fixtures/DataProviderValid.php, tests/Fixtures/DataProviderMatchingValid.php, tests/Fixtures/DataProviderOrderValid.php
New fixtures demonstrating compliant provider names, matching provider/test name patterns (including attributes), and correct provider/test ordering.
Invalid fixtures
tests/Fixtures/DataProviderInvalid.php, tests/Fixtures/DataProviderMatchingInvalid.php, tests/Fixtures/DataProviderOrderInvalid.php
New fixtures containing deliberate violations: wrong/missing prefixes, mismatched provider names, and providers placed before tests.
Functional tests
tests/Functional/DataProviderPrefixSniffFunctionalTest.php, tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php, tests/Functional/DataProviderOrderSniffFunctionalTest.php
Integration tests that run each sniff against the fixtures and assert expected violations (messages, sources, fixable flags).
Unit tests
tests/Unit/DataProviderPrefixSniffTest.php, tests/Unit/DataProviderMatchesTestNameSniffTest.php, tests/Unit/DataProviderOrderSniffTest.php
Comprehensive unit tests exercising registration, helper logic, annotation/attribute parsing, edge cases, and error messages (many tests use reflection to access private helpers).

Sequence Diagram(s)

sequenceDiagram
    participant PHPCS as PHP_CodeSniffer
    participant Sniff as DataProvider Sniff (Prefix/Match/Order)
    participant File as File (token stream)
    participant Reporter as PHP_CodeSniffer Reporter

    PHPCS->>Sniff: register() → tokens (T_FUNCTION / T_CLASS)
    PHPCS->>File: Tokenize source

    loop for each registered token
        PHPCS->>Sniff: process(File, tokenPtr)
        Sniff->>File: Determine if inside test class / is test method
        alt test method detected
            Sniff->>File: Read docblock `@dataProvider` / attribute DataProvider(...)
            alt provider found and local
                Sniff->>Sniff: Validate (prefix / name endsWith testName / ordering)
                alt validation fails
                    Sniff->>Reporter: addError(code, message, position)
                else
                    Sniff->>Sniff: no error
                end
            else no provider or external -> skip
            end
        else not test context -> skip
        end
    end

    PHPCS->>Reporter: Collect & output violations
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to:
    • Annotation and PHP 8 attribute parsing logic in each sniff.
    • Correct identification of test classes (name suffix vs extends TestCase).
    • Ordering logic and line-number comparisons in DataProviderOrderSniff.
    • Fixer logic in DataProviderPrefixSniff (rename method + update docblocks) and its safety.
    • Thoroughness and accuracy of unit/functional test expectations (messages, sources, fixable flags).

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Added 3 new checks' is vague and lacks specificity about what the checks do or which domain they target. Consider revising to something like 'Add DataProvider naming and ordering checks for PHPUnit tests' to better convey the purpose and scope of the changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 98.14% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/data-provider

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link

codecov-commenter commented Nov 23, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (fb78c59) to head (1ebe62a).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##              main        #7    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            3         6     +3     
  Lines          164       370   +206     
==========================================
+ Hits           164       370   +206     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@coderabbitai
Copy link

coderabbitai bot commented Nov 23, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the three sniff files
fd -e php "DataProviderPrefix|DataProviderMatchesTestName|DataProviderOrder"

# Also search for any test files
fd -e php "DataProvider" | head -20

Length of output: 1383


🏁 Script executed:

#!/bin/bash
# Read the three sniff implementation files
echo "=== DataProviderPrefixSniff.php ==="
cat src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php

echo -e "\n\n=== DataProviderMatchesTestNameSniff.php ==="
cat src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php

echo -e "\n\n=== DataProviderOrderSniff.php ==="
cat src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php

Length of output: 31150


🏁 Script executed:

#!/bin/bash
# Read the test fixture files to understand examples better
echo "=== DataProviderValid.php ==="
cat tests/Fixtures/DataProviderValid.php

echo -e "\n\n=== DataProviderInvalid.php ==="
cat tests/Fixtures/DataProviderInvalid.php

echo -e "\n\n=== DataProviderMatchingValid.php ==="
cat tests/Fixtures/DataProviderMatchingValid.php

echo -e "\n\n=== DataProviderMatchingInvalid.php ==="
cat tests/Fixtures/DataProviderMatchingInvalid.php

echo -e "\n\n=== DataProviderOrderValid.php ==="
cat tests/Fixtures/DataProviderOrderValid.php

echo -e "\n\n=== DataProviderOrderInvalid.php ==="
cat tests/Fixtures/DataProviderOrderInvalid.php

Length of output: 12469


@AlexSkrypnyk, here are the descriptions with examples for the three new rules:


DrevOps.TestingPractices.DataProviderPrefix

Purpose

Enforces naming conventions for PHPUnit data provider methods. This sniff ensures that data provider methods start with a configurable prefix (default: dataProvider). It provides auto-fixing capabilities to rename methods and update all references.

Configuration

  • prefix: The required prefix for data provider methods (default: dataProvider)

Examples

✓ Valid:

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderValidCases
     */
    public function testSomething($input, $expected): void {
        $this->assertEquals($expected, $input);
    }

    public function dataProviderValidCases(): array {
        return [['input1', 'expected1']];
    }
}

✗ Invalid:

class UserTest extends TestCase {
    /**
     * @dataProvider providerInvalidCases
     */
    public function testSomething($input, $expected): void {
        $this->assertEquals($expected, $input);
    }

    // ERROR: Data provider method "providerInvalidCases" should start with 
    // prefix "dataProvider", suggested name: "dataProviderInvalidCases"
    public function providerInvalidCases(): array {
        return [['input1', 'expected1']];
    }

    /**
     * @dataProvider casesForAnotherTest
     */
    public function testAnother($value): void {
        $this->assertNotEmpty($value);
    }

    // ERROR: Data provider method "casesForAnotherTest" should start with 
    // prefix "dataProvider", suggested name: "dataProviderForAnotherTest"
    public function casesForAnotherTest(): array {
        return [['value1']];
    }

    /**
     * @dataProvider provideDataForTest
     */
    public function testWithProvide($data): void {
        $this->assertIsString($data);
    }

    // ERROR: Data provider method "provideDataForTest" should start with 
    // prefix "dataProvider", suggested name: "dataProviderDataForTest"
    public function provideDataForTest(): array {
        return [['data1']];
    }
}

DrevOps.TestingPractices.DataProviderMatchesTestName

Purpose

Enforces that data provider method names match the test method names they serve. The provider name must end with the exact test name (after removing the "test" prefix). This improves code readability and maintains a clear relationship between tests and their data providers.

Configuration

None

Examples

✓ Valid:

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderUserLogin
     */
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    public function dataProviderUserLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider providerEmailValidation
     */
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }

    // Different prefix is OK, as long as it ends with "EmailValidation"
    public function providerEmailValidation(): array {
        return [['test@example.com']];
    }

    /**
     * @dataProvider AuthenticationScenarios
     */
    public function testAuthenticationScenarios($scenario): void {
        $this->assertIsString($scenario);
    }

    // No prefix is OK, as long as it matches exactly
    public function AuthenticationScenarios(): array {
        return [['scenario1']];
    }

    #[DataProvider('dataProviderPasswordValidation')]
    public function testPasswordValidation($password): void {
        $this->assertNotEmpty($password);
    }

    // Works with PHP 8 attributes too
    public function dataProviderPasswordValidation(): array {
        return [['password123']];
    }
}

✗ Invalid:

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderLogin
     */
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    // ERROR: Data provider method "dataProviderLogin" does not match test 
    // method "testUserLogin". Expected provider name to end with "UserLogin"
    public function dataProviderLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider dataProviderEmailValidationCases
     */
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }

    // ERROR: Data provider method "dataProviderEmailValidationCases" does not 
    // match test method "testEmailValidation". Expected provider name to end 
    // with "EmailValidation"
    public function dataProviderEmailValidationCases(): array {
        return [['test@example.com']];
    }

    /**
     * @dataProvider providerAuth
     */
    public function testAuthenticationScenarios($scenario): void {
        $this->assertIsString($scenario);
    }

    // ERROR: Data provider method "providerAuth" does not match test method 
    // "testAuthenticationScenarios". Expected provider name to end with 
    // "AuthenticationScenarios"
    public function providerAuth(): array {
        return [['scenario1']];
    }

    #[DataProvider('dataProviderPass')]
    public function testPasswordValidation($password): void {
        $this->assertNotEmpty($password);
    }

    // ERROR: Data provider method "dataProviderPass" does not match test 
    // method "testPasswordValidation". Expected provider name to end with 
    // "PasswordValidation"
    public function dataProviderPass(): array {
        return [['password123']];
    }
}

DrevOps.TestingPractices.DataProviderOrder

Purpose

Enforces ordering of data provider and test methods in test files. This ensures structural organization and improves code readability. By default, data providers should be defined after their test methods. Helper methods between tests and providers are allowed.

Configuration

  • providerPosition: Expected position of data provider relative to test (default: after)
    • after: Data providers should appear after their test methods (default)
    • before: Data providers should appear before their test methods

Examples

✓ Valid (providerPosition="after"):

class UserTest extends TestCase {
    /**
     * @dataProvider dataProviderUserLogin
     */
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    // Provider after test - CORRECT
    public function dataProviderUserLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider dataProviderEmailValidation
     */
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }

    // Helper methods between test and provider are allowed
    private function helperValidate(): void {
        // Helper logic
    }

    // Provider still after test - CORRECT
    public function dataProviderEmailValidation(): array {
        return [['test@example.com']];
    }

    #[DataProvider('dataProviderAuthentication')]
    public function testAuthentication($scenario): void {
        $this->assertIsString($scenario);
    }

    // Works with PHP 8 attributes too
    public function dataProviderAuthentication(): array {
        return [['scenario1']];
    }
}

✗ Invalid (providerPosition="after"):

class UserTest extends TestCase {
    // Provider before test - WRONG ORDER
    public function dataProviderUserLogin(): array {
        return [['user1', 'pass1']];
    }

    /**
     * @dataProvider dataProviderUserLogin
     */
    // ERROR: Data provider method "dataProviderUserLogin" (line 10) appears 
    // before test method "testUserLogin" (line 17). Providers should be 
    // defined after their test methods
    public function testUserLogin($user, $pass): void {
        $this->assertNotEmpty($user);
    }

    // Another provider before test - WRONG ORDER
    public function dataProviderEmailValidation(): array {
        return [['test@example.com']];
    }

    /**
     * @dataProvider dataProviderEmailValidation
     */
    // ERROR: Data provider method "dataProviderEmailValidation" (line 24) 
    // appears before test method "testEmailValidation" (line 31). Providers 
    // should be defined after their test methods
    public function testEmailValidation($email): void {
        $this->assertNotEmpty($email);
    }
}

Note: All three rules:

  • Work with both @dataProvider annotations and PHP 8 #[DataProvider] attributes
  • Skip external providers referenced as ClassName::methodName
  • Only apply to test classes (classes ending with Test or TestCase, or extending TestCase)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb78c59 and 7c3e2d3.

📒 Files selected for processing (16)
  • src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (1 hunks)
  • src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (1 hunks)
  • src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (1 hunks)
  • src/DrevOps/ruleset.xml (1 hunks)
  • tests/Fixtures/DataProviderInvalid.php (1 hunks)
  • tests/Fixtures/DataProviderMatchingInvalid.php (1 hunks)
  • tests/Fixtures/DataProviderMatchingValid.php (1 hunks)
  • tests/Fixtures/DataProviderOrderInvalid.php (1 hunks)
  • tests/Fixtures/DataProviderOrderValid.php (1 hunks)
  • tests/Fixtures/DataProviderValid.php (1 hunks)
  • tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php (1 hunks)
  • tests/Functional/DataProviderOrderSniffFunctionalTest.php (1 hunks)
  • tests/Functional/DataProviderPrefixSniffFunctionalTest.php (1 hunks)
  • tests/Unit/DataProviderMatchesTestNameSniffTest.php (1 hunks)
  • tests/Unit/DataProviderOrderSniffTest.php (1 hunks)
  • tests/Unit/DataProviderPrefixSniffTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
tests/Fixtures/DataProviderValid.php (1)
tests/Fixtures/DataProviderInvalid.php (2)
  • testSomething (19-21)
  • testAnother (40-42)
tests/Fixtures/DataProviderMatchingValid.php (3)
tests/Fixtures/DataProviderMatchingInvalid.php (2)
  • DataProvider (81-84)
  • DataProvider (101-104)
tests/Fixtures/DataProviderOrderInvalid.php (1)
  • DataProvider (70-73)
tests/Fixtures/DataProviderOrderValid.php (1)
  • DataProvider (66-69)
tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php (2)
tests/Functional/DataProviderOrderSniffFunctionalTest.php (2)
  • CoversNothing (13-62)
  • Group (21-24)
tests/Functional/FunctionalTestCase.php (2)
  • FunctionalTestCase (17-142)
  • runPhpcs (62-87)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (1)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (3)
  • register (35-37)
  • process (42-82)
  • isTestClass (95-133)
src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (2)
  • register (35-37)
  • isTestClass (95-133)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (2)
  • register (51-53)
  • isTestClass (124-162)
tests/Functional/DataProviderOrderSniffFunctionalTest.php (2)
tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php (2)
  • CoversNothing (13-72)
  • Group (21-24)
tests/Functional/FunctionalTestCase.php (2)
  • FunctionalTestCase (17-142)
  • runPhpcs (62-87)
tests/Functional/DataProviderPrefixSniffFunctionalTest.php (1)
tests/Functional/FunctionalTestCase.php (2)
  • FunctionalTestCase (17-142)
  • runPhpcs (62-87)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (3)
  • register (49-51)
  • process (56-81)
  • isTestClass (94-124)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (3)
  • register (51-53)
  • process (58-111)
  • isTestClass (124-162)
tests/Fixtures/DataProviderOrderValid.php (1)
tests/Fixtures/DataProviderOrderInvalid.php (6)
  • DataProvider (70-73)
  • testUserLogin (32-34)
  • dataProviderUserLogin (20-25)
  • testEmailValidation (52-54)
  • dataProviderEmailValidation (41-45)
  • dataProviderAuthentication (61-65)
tests/Fixtures/DataProviderOrderInvalid.php (1)
tests/Fixtures/DataProviderOrderValid.php (6)
  • DataProvider (66-69)
  • dataProviderUserLogin (29-34)
  • testUserLogin (20-22)
  • dataProviderEmailValidation (57-61)
  • testEmailValidation (41-43)
  • dataProviderAuthentication (76-80)
tests/Fixtures/DataProviderMatchingInvalid.php (1)
tests/Fixtures/DataProviderMatchingValid.php (5)
  • DataProvider (81-84)
  • DataProvider (101-104)
  • testUserLogin (20-22)
  • testEmailValidation (41-43)
  • testAuthenticationScenarios (62-64)
tests/Unit/DataProviderMatchesTestNameSniffTest.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (2)
  • DataProviderMatchesTestNameSniff (25-296)
  • register (35-37)
tests/Unit/UnitTestCase.php (3)
  • UnitTestCase (18-152)
  • processCode (46-53)
  • findFunctionToken (85-93)
tests/Unit/DataProviderOrderSniffTest.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (2)
  • DataProviderOrderSniff (27-421)
  • register (49-51)
tests/Unit/UnitTestCase.php (4)
  • UnitTestCase (18-152)
  • processCode (46-53)
  • findClassToken (104-112)
  • findFunctionToken (85-93)
tests/Unit/DataProviderPrefixSniffTest.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (2)
  • DataProviderPrefixSniff (17-292)
  • register (51-53)
tests/Unit/UnitTestCase.php (3)
  • UnitTestCase (18-152)
  • processCode (46-53)
  • findFunctionToken (85-93)
🪛 GitHub Actions: Test PHP
tests/Unit/DataProviderPrefixSniffTest.php

[error] 513-513: PHPUnit error: The data provider specified for DrevOps\PhpcsStandard\Tests\Unit\DataProviderPrefixSniffTest::testFindDataProvidersWithEmptyMethodName is invalid. Method DrevOps\PhpcsStandard\Tests\Unit\DataProviderPrefixSniffTest::without method name.() does not exist.


[warning] 1-1: PHPUnit deprecation: Metadata found in doc-comment for method DrevOps\PhpcsStandard\Tests\Unit\DataProviderPrefixSniffTest::testFindDataProvidersWithEmptyMethodName(). Metadata in doc-comments is deprecated and will no longer be supported in PHPUnit 12. Update your test code to use attributes instead.


[error] 1-1: ERROR: Script phpunit --no-coverage handling the test event returned with error code 2

🪛 PHPMD (2.15.0)
tests/Fixtures/DataProviderMatchingValid.php

20-20: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)


71-76: The method AuthenticationScenarios is not named in camelCase. (undefined)

(CamelCaseMethodName)


111-116: The method TokenGeneration is not named in camelCase. (undefined)

(CamelCaseMethodName)

tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php

14-72: Avoid excessively long class names like DataProviderMatchesTestNameSniffFunctionalTest. Keep class name length under 40. (undefined)

(LongClassName)

src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php

78-78: The variable $function_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


86-86: The variable $function_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


99-99: The variable $suggested_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


128-128: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


134-134: The variable $class_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


142-142: The variable $class_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


150-150: The variable $extends_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


152-152: The variable $parent_class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


154-154: The variable $parent_class is not named in camelCase. (undefined)

(CamelCaseVariableName)


188-188: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


193-193: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


232-232: The variable $common_prefixes is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $common_prefix is not named in camelCase. (undefined)

(CamelCaseVariableName)


266-290: The method fixProviderName() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 10. (undefined)

(CyclomaticComplexity)


274-274: The variable $prev_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


284-284: The variable $prev_tag is not named in camelCase. (undefined)

(CamelCaseVariableName)

src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php

27-421: The class DataProviderOrderSniff has an overall complexity of 50 which is very high. The configured complexity threshold is 50. (undefined)

(ExcessiveClassComplexity)


65-65: The variable $class_start is not named in camelCase. (undefined)

(CamelCaseVariableName)


66-66: The variable $class_end is not named in camelCase. (undefined)

(CamelCaseVariableName)


98-98: The variable $class_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


104-104: The variable $class_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


112-112: The variable $extends_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


114-114: The variable $parent_class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


116-116: The variable $parent_class is not named in camelCase. (undefined)

(CamelCaseVariableName)


145-145: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


148-148: The variable $method_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


154-154: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


162-162: The variable $provider_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


173-173: The variable $test_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


202-202: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


205-205: The variable $method_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


211-211: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


230-301: The method validateOrder() has a Cyclomatic Complexity of 14. The configured cyclomatic complexity threshold is 10. (undefined)

(CyclomaticComplexity)


230-301: The method validateOrder() has an NPath complexity of 361. The configured NPath complexity threshold is 200. (undefined)

(NPathComplexity)


232-232: The variable $checked_providers is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $test_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $test_info is not named in camelCase. (undefined)

(CamelCaseVariableName)


235-235: The variable $provider_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


236-236: The variable $test_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


258-258: The variable $provider_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


261-261: The variable $has_violation is not named in camelCase. (undefined)

(CamelCaseVariableName)


262-262: The variable $error_code is not named in camelCase. (undefined)

(CamelCaseVariableName)


263-263: The variable $error_message is not named in camelCase. (undefined)

(CamelCaseVariableName)


288-288: The variable $error_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


318-318: The variable $comment_end is not named in camelCase. (undefined)

(CamelCaseVariableName)


323-323: The variable $comment_start is not named in camelCase. (undefined)

(CamelCaseVariableName)


341-341: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


347-347: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


377-377: The variable $attribute_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


389-389: The variable $name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


396-396: The variable $open_paren is not named in camelCase. (undefined)

(CamelCaseVariableName)


403-403: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


410-410: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)

src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php

49-49: The variable $method_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


55-55: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


63-63: The variable $test_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


66-66: The variable $provider_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


99-99: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


105-105: The variable $class_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


111-111: The variable $class_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


120-120: The variable $extends_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


122-122: The variable $parent_class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


124-124: The variable $parent_class is not named in camelCase. (undefined)

(CamelCaseVariableName)


177-177: The variable $comment_end is not named in camelCase. (undefined)

(CamelCaseVariableName)


182-182: The variable $comment_start is not named in camelCase. (undefined)

(CamelCaseVariableName)


200-200: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


206-206: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


236-236: The variable $attribute_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


248-248: The variable $name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


255-255: The variable $open_paren is not named in camelCase. (undefined)

(CamelCaseVariableName)


262-262: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


269-269: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)

tests/Fixtures/DataProviderOrderValid.php

20-20: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)


48-50: Avoid unused private methods such as 'helperValidate'. (undefined)

(UnusedPrivateMethod)

tests/Fixtures/DataProviderOrderInvalid.php

32-32: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)

tests/Fixtures/DataProviderMatchingInvalid.php

20-20: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)


111-116: The method TokenGenerationCases is not named in camelCase. (undefined)

(CamelCaseMethodName)

tests/Unit/DataProviderMatchesTestNameSniffTest.php

15-649: The class DataProviderMatchesTestNameSniffTest has 28 non-getter- and setter-methods. Consider refactoring DataProviderMatchesTestNameSniffTest to keep number of methods under 25. (undefined)

(TooManyMethods)


15-649: The class DataProviderMatchesTestNameSniffTest has 28 public methods. Consider refactoring DataProviderMatchesTestNameSniffTest to keep number of public methods under 10. (undefined)

(TooManyPublicMethods)


46-46: Missing class import via use statement (line '46', column '23'). (undefined)

(MissingImport)


58-58: Missing class import via use statement (line '58', column '23'). (undefined)

(MissingImport)


76-76: Missing class import via use statement (line '76', column '23'). (undefined)

(MissingImport)


91-91: Missing class import via use statement (line '91', column '23'). (undefined)

(MissingImport)


109-109: Missing class import via use statement (line '109', column '23'). (undefined)

(MissingImport)


138-138: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


140-140: Missing class import via use statement (line '140', column '23'). (undefined)

(MissingImport)


163-163: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


165-165: Missing class import via use statement (line '165', column '23'). (undefined)

(MissingImport)


185-185: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


187-187: Missing class import via use statement (line '187', column '23'). (undefined)

(MissingImport)


237-237: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


238-238: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


239-239: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


302-302: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


304-304: Missing class import via use statement (line '304', column '23'). (undefined)

(MissingImport)


324-324: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


326-326: Missing class import via use statement (line '326', column '23'). (undefined)

(MissingImport)


346-346: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


348-348: Missing class import via use statement (line '348', column '23'). (undefined)

(MissingImport)


369-369: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


371-371: Missing class import via use statement (line '371', column '23'). (undefined)

(MissingImport)


396-396: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


398-398: Missing class import via use statement (line '398', column '23'). (undefined)

(MissingImport)


421-421: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


423-423: Missing class import via use statement (line '423', column '23'). (undefined)

(MissingImport)


444-444: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


446-446: Missing class import via use statement (line '446', column '23'). (undefined)

(MissingImport)


466-466: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


468-468: Missing class import via use statement (line '468', column '23'). (undefined)

(MissingImport)


494-494: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


496-496: Missing class import via use statement (line '496', column '23'). (undefined)

(MissingImport)


518-518: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


520-520: Missing class import via use statement (line '520', column '23'). (undefined)

(MissingImport)


541-541: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


543-543: Missing class import via use statement (line '543', column '23'). (undefined)

(MissingImport)


564-564: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


566-566: Missing class import via use statement (line '566', column '23'). (undefined)

(MissingImport)


587-587: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


589-589: Missing class import via use statement (line '589', column '23'). (undefined)

(MissingImport)


635-635: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


636-636: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


637-637: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)

tests/Unit/DataProviderOrderSniffTest.php

15-524: The class DataProviderOrderSniffTest has 22 public methods. Consider refactoring DataProviderOrderSniffTest to keep number of public methods under 10. (undefined)

(TooManyPublicMethods)


89-89: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


90-90: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


91-91: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


237-237: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


239-239: Missing class import via use statement (line '239', column '23'). (undefined)

(MissingImport)


259-259: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


261-261: Missing class import via use statement (line '261', column '23'). (undefined)

(MissingImport)


281-281: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


283-283: Missing class import via use statement (line '283', column '23'). (undefined)

(MissingImport)


303-303: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


305-305: Missing class import via use statement (line '305', column '23'). (undefined)

(MissingImport)


341-341: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


343-343: Missing class import via use statement (line '343', column '23'). (undefined)

(MissingImport)


368-368: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


370-370: Missing class import via use statement (line '370', column '23'). (undefined)

(MissingImport)


393-393: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


395-395: Missing class import via use statement (line '395', column '23'). (undefined)

(MissingImport)


421-421: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


423-423: Missing class import via use statement (line '423', column '23'). (undefined)

(MissingImport)


445-445: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


447-447: Missing class import via use statement (line '447', column '23'). (undefined)

(MissingImport)


468-468: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


470-470: Missing class import via use statement (line '470', column '23'). (undefined)

(MissingImport)


491-491: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


493-493: Missing class import via use statement (line '493', column '23'). (undefined)

(MissingImport)


514-514: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


516-516: Missing class import via use statement (line '516', column '23'). (undefined)

(MissingImport)

tests/Unit/DataProviderPrefixSniffTest.php

15-574: The class DataProviderPrefixSniffTest has 25 public methods. Consider refactoring DataProviderPrefixSniffTest to keep number of public methods under 10. (undefined)

(TooManyPublicMethods)


54-54: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


56-56: Missing class import via use statement (line '56', column '23'). (undefined)

(MissingImport)


76-76: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


78-78: Missing class import via use statement (line '78', column '23'). (undefined)

(MissingImport)


99-99: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


101-101: Missing class import via use statement (line '101', column '23'). (undefined)

(MissingImport)


121-121: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


123-123: Missing class import via use statement (line '123', column '23'). (undefined)

(MissingImport)


160-160: Missing class import via use statement (line '160', column '23'). (undefined)

(MissingImport)


194-194: Missing class import via use statement (line '194', column '23'). (undefined)

(MissingImport)


210-210: Missing class import via use statement (line '210', column '23'). (undefined)

(MissingImport)


222-222: Missing class import via use statement (line '222', column '23'). (undefined)

(MissingImport)


236-236: Missing class import via use statement (line '236', column '23'). (undefined)

(MissingImport)


248-248: Missing class import via use statement (line '248', column '23'). (undefined)

(MissingImport)


260-260: Missing class import via use statement (line '260', column '23'). (undefined)

(MissingImport)


272-272: Missing class import via use statement (line '272', column '23'). (undefined)

(MissingImport)


284-284: Missing class import via use statement (line '284', column '23'). (undefined)

(MissingImport)


296-296: Missing class import via use statement (line '296', column '23'). (undefined)

(MissingImport)


354-354: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


355-355: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


356-356: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


427-427: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


428-428: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


429-429: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


473-473: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


475-475: Missing class import via use statement (line '475', column '23'). (undefined)

(MissingImport)


501-501: Missing class import via use statement (line '501', column '23'). (undefined)

(MissingImport)


526-526: Missing class import via use statement (line '526', column '23'). (undefined)

(MissingImport)


539-539: Missing class import via use statement (line '539', column '23'). (undefined)

(MissingImport)

🔇 Additional comments (7)
tests/Fixtures/DataProviderMatchingValid.php (1)

13-116: Valid matching fixture looks consistent with sniff rules

Each test’s provider name ends with the expected test name (including attribute-based and no‑prefix cases), and providers are placed after their tests, so this fixture accurately models “valid” scenarios for the new sniffs. Static-analysis warnings about unused parameters and method-name casing are acceptable here as this is test-only fixture code.

tests/Fixtures/DataProviderValid.php (1)

12-84: Fixture correctly exercises valid provider naming and reuse

The tests and their providers are wired up coherently, including a shared provider used by multiple tests, which is useful for exercising both prefix and ordering sniffs. No functional issues spotted.

tests/Fixtures/DataProviderMatchingInvalid.php (1)

13-116: Invalid matching fixture correctly covers negative scenarios

All providers here intentionally violate the “ends with test name” rule (partial matches, extra suffixes, different prefixes, and attribute-based cases), which is exactly what the negative fixture should do. No code issues from the fixture itself.

tests/Unit/DataProviderOrderSniffTest.php (1)

14-524: Unit coverage for DataProviderOrderSniff is comprehensive and aligned

The tests exercise all key paths: correct/incorrect ordering (including attributes and helpers), shared providers, external providers, non-test classes, isTestClass variants, providerPosition defaults/overrides, and annotation/attribute edge cases. They appear consistent with the sniff’s implementation and should guard against regressions effectively.

tests/Unit/DataProviderMatchesTestNameSniffTest.php (3)

1-32: LGTM: Well-structured test class setup.

The class structure, imports, and setUp method are correctly implemented. The configuration properly isolates the DataProviderMatchesTestName sniff for testing.


37-121: LGTM: Comprehensive unit tests for private methods.

The reflection-based tests appropriately validate the behavior of private helper methods with good coverage of both valid and invalid inputs.


126-595: LGTM: Excellent edge case coverage.

The tests comprehensively cover:

  • Both annotation (@dataProvider) and attribute (#[DataProvider]) styles
  • External provider detection and skipping
  • Various malformed or missing provider declarations
  • Class hierarchy validation

This thorough coverage ensures the sniff behaves correctly across diverse scenarios.

Comment on lines +173 to +278
private function findDataProviderAnnotation(File $phpcsFile, int $functionPtr): ?string {
$tokens = $phpcsFile->getTokens();

// Search backward for docblock before function.
$comment_end = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $functionPtr - 1);
if ($comment_end === FALSE) {
return NULL;
}

$comment_start = $tokens[$comment_end]['comment_opener'] ?? NULL;
// @codeCoverageIgnoreStart
if ($comment_start === NULL) {
return NULL;
}
// @codeCoverageIgnoreEnd
// Look for @dataProvider tag in the docblock.
for ($i = $comment_start; $i <= $comment_end; $i++) {
// @codeCoverageIgnoreStart
if ($tokens[$i]['code'] !== T_DOC_COMMENT_TAG) {
continue;
}

if ($tokens[$i]['content'] !== '@dataProvider') {
continue;
}
// @codeCoverageIgnoreEnd
// Find the method name after the tag.
$string_ptr = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $i + 1, $i + 3);
// @codeCoverageIgnoreStart
if ($string_ptr === FALSE) {
continue;
}
// @codeCoverageIgnoreEnd
$method_name = trim($tokens[$string_ptr]['content']);

// Skip external providers (ClassName::methodName).
if (strpos($method_name, '::') !== FALSE) {
return NULL;
}

return $method_name;
}

// @codeCoverageIgnoreStart
return NULL;
// @codeCoverageIgnoreEnd
}

/**
* Finds data provider from #[DataProvider] attribute.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile
* The file being scanned.
* @param int $functionPtr
* The position of the function token.
*
* @return string|null
* The provider method name, or NULL if not found or external.
*/
private function findDataProviderAttribute(File $phpcsFile, int $functionPtr): ?string {
$tokens = $phpcsFile->getTokens();

// Search backward for attribute before function.
$attribute_ptr = $phpcsFile->findPrevious(T_ATTRIBUTE, $functionPtr - 1);
if ($attribute_ptr === FALSE) {
return NULL;
}

// Check if attribute is close enough to function (within 10 tokens).
// @codeCoverageIgnoreStart
if ($functionPtr - $attribute_ptr > 10) {
return NULL;
}
// @codeCoverageIgnoreEnd
// Find the attribute name.
$name_ptr = $phpcsFile->findNext(T_STRING, $attribute_ptr + 1, $functionPtr);
// @codeCoverageIgnoreStart
if ($name_ptr === FALSE || $tokens[$name_ptr]['content'] !== 'DataProvider') {
return NULL;
}
// @codeCoverageIgnoreEnd
// Find the opening parenthesis of attribute.
$open_paren = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $name_ptr + 1, $functionPtr);
// @codeCoverageIgnoreStart
if ($open_paren === FALSE) {
return NULL;
}
// @codeCoverageIgnoreEnd
// Find the string inside attribute (provider method name).
$string_ptr = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $open_paren + 1, $functionPtr);
// @codeCoverageIgnoreStart
if ($string_ptr === FALSE) {
return NULL;
}
// @codeCoverageIgnoreEnd
// Extract method name from string (remove quotes).
$method_name = trim($tokens[$string_ptr]['content'], '\'"');

// Skip external providers (ClassName::methodName).
// @codeCoverageIgnoreStart
if (strpos($method_name, '::') !== FALSE) {
return NULL;
}
// @codeCoverageIgnoreEnd
return $method_name;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Provider extraction logic duplicated with DataProviderOrderSniff

findDataProviderAnnotation() and findDataProviderAttribute() here are effectively the same as in DataProviderOrderSniff. That’s fine functionally, but it does mean any future change to how providers are discovered (e.g., new attribute forms, different “closeness” rules, or external-provider handling) must be kept in sync manually.

If you plan to evolve these sniffs further, consider extracting this logic into a shared helper/trait so both sniffs stay aligned with a single implementation.

🧰 Tools
🪛 PHPMD (2.15.0)

177-177: The variable $comment_end is not named in camelCase. (undefined)

(CamelCaseVariableName)


182-182: The variable $comment_start is not named in camelCase. (undefined)

(CamelCaseVariableName)


200-200: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


206-206: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


236-236: The variable $attribute_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


248-248: The variable $name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


255-255: The variable $open_paren is not named in camelCase. (undefined)

(CamelCaseVariableName)


262-262: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


269-269: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)

🤖 Prompt for AI Agents
In src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php
lines 173-278 the provider-extraction logic in findDataProviderAnnotation() and
findDataProviderAttribute() duplicates the same code in DataProviderOrderSniff;
extract this logic into a single shared helper (either a new utility class or a
trait, e.g. src/DrevOps/Utils/DataProviderResolver.php or
src/DrevOps/Sniffs/TestingPractices/DataProviderTrait.php) that exposes a method
like resolveProviderName(File $phpcsFile, int $functionPtr): ?string and move
the annotation/attribute parsing into it, then update both sniffs to call that
shared method and remove the duplicated implementations; ensure the helper
preserves existing behavior (docblock and attribute parsing, proximity checks,
external provider filtering) and update imports/namespace references
accordingly.

Comment on lines +173 to +205
private function findDataProviders(File $phpcsFile): array {
$tokens = $phpcsFile->getTokens();
$providers = [];

// Search for @dataProvider annotations in doc comments.
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['code'] !== T_DOC_COMMENT_TAG) {
continue;
}

if ($tokens[$i]['content'] !== '@dataProvider') {
continue;
}

// Find the method name after the tag.
$string_ptr = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $i + 1, $i + 3);
if ($string_ptr === FALSE) {
continue;
}

$method_name = trim($tokens[$string_ptr]['content']);

// Handle method names that might include class references.
// E.g., "ClassName::methodName" - we only want "methodName".
if (strpos($method_name, '::') !== FALSE) {
continue;
}

$providers[$method_name] = TRUE;
}

return $providers;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add attribute-aware provider discovery.

findDataProviders() only looks at @dataProvider tags, so any provider referenced via PHPUnit attributes (now the preferred metadata vehicle) is never checked. Because PHPUnit reads attributes before docblocks, the sniff silently misses every attribute-only provider, letting non-compliant names through and undermining the new rule.(docs.phpunit.de)

@@
-    // Search for @dataProvider annotations in doc comments.
-    for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
+    // Search for @dataProvider annotations in doc comments.
+    for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
       if ($tokens[$i]['code'] !== T_DOC_COMMENT_TAG) {
         continue;
       }
@@
       $providers[$method_name] = TRUE;
     }
 
+    // Search for #[DataProvider(...)] attributes.
+    for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
+      if ($tokens[$i]['code'] !== T_ATTRIBUTE) {
+        continue;
+      }
+
+      $attribute_closer = $tokens[$i]['attribute_closer'] ?? NULL;
+      if ($attribute_closer === NULL) {
+        continue;
+      }
+
+      $attribute_name_ptr = $phpcsFile->findNext(T_STRING, $i + 1, $attribute_closer);
+      if ($attribute_name_ptr === FALSE) {
+        continue;
+      }
+
+      $attribute_name = $tokens[$attribute_name_ptr]['content'];
+      if (!in_array($attribute_name, ['DataProvider', 'DataProviderExternal'], TRUE)) {
+        continue;
+      }
+
+      $string_ptr = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $attribute_name_ptr + 1, $attribute_closer);
+      if ($string_ptr === FALSE) {
+        continue;
+      }
+
+      $method_name = trim($tokens[$string_ptr]['content'], '\'"');
+      if (strpos($method_name, '::') !== FALSE) {
+        continue;
+      }
+
+      $providers[$method_name] = TRUE;
+    }
+
     return $providers;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private function findDataProviders(File $phpcsFile): array {
$tokens = $phpcsFile->getTokens();
$providers = [];
// Search for @dataProvider annotations in doc comments.
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['code'] !== T_DOC_COMMENT_TAG) {
continue;
}
if ($tokens[$i]['content'] !== '@dataProvider') {
continue;
}
// Find the method name after the tag.
$string_ptr = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $i + 1, $i + 3);
if ($string_ptr === FALSE) {
continue;
}
$method_name = trim($tokens[$string_ptr]['content']);
// Handle method names that might include class references.
// E.g., "ClassName::methodName" - we only want "methodName".
if (strpos($method_name, '::') !== FALSE) {
continue;
}
$providers[$method_name] = TRUE;
}
return $providers;
}
private function findDataProviders(File $phpcsFile): array {
$tokens = $phpcsFile->getTokens();
$providers = [];
// Search for @dataProvider annotations in doc comments.
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['code'] !== T_DOC_COMMENT_TAG) {
continue;
}
if ($tokens[$i]['content'] !== '@dataProvider') {
continue;
}
// Find the method name after the tag.
$string_ptr = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $i + 1, $i + 3);
if ($string_ptr === FALSE) {
continue;
}
$method_name = trim($tokens[$string_ptr]['content']);
// Handle method names that might include class references.
// E.g., "ClassName::methodName" - we only want "methodName".
if (strpos($method_name, '::') !== FALSE) {
continue;
}
$providers[$method_name] = TRUE;
}
// Search for #[DataProvider(...)] attributes.
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['code'] !== T_ATTRIBUTE) {
continue;
}
$attribute_closer = $tokens[$i]['attribute_closer'] ?? NULL;
if ($attribute_closer === NULL) {
continue;
}
$attribute_name_ptr = $phpcsFile->findNext(T_STRING, $i + 1, $attribute_closer);
if ($attribute_name_ptr === FALSE) {
continue;
}
$attribute_name = $tokens[$attribute_name_ptr]['content'];
if (!in_array($attribute_name, ['DataProvider', 'DataProviderExternal'], TRUE)) {
continue;
}
$string_ptr = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $attribute_name_ptr + 1, $attribute_closer);
if ($string_ptr === FALSE) {
continue;
}
$method_name = trim($tokens[$string_ptr]['content'], '\'"');
if (strpos($method_name, '::') !== FALSE) {
continue;
}
$providers[$method_name] = TRUE;
}
return $providers;
}
🧰 Tools
🪛 PHPMD (2.15.0)

188-188: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


193-193: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)

Comment on lines +266 to +289
private function fixProviderName(File $phpcsFile, string $oldName, string $newName): void {
$tokens = $phpcsFile->getTokens();

// Fix the method declaration.
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
// Fix method name in function declaration.
if ($tokens[$i]['code'] === T_STRING && $tokens[$i]['content'] === $oldName) {
// Check if this is a function declaration.
$prev_ptr = $phpcsFile->findPrevious(T_WHITESPACE, $i - 1, NULL, TRUE);
if ($prev_ptr !== FALSE && $tokens[$prev_ptr]['code'] === T_FUNCTION) {
$phpcsFile->fixer->replaceToken($i, $newName);
}
}

// Fix @dataProvider annotations.
if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
$content = $tokens[$i]['content'];
// Check if this string is part of @dataProvider annotation.
$prev_tag = $phpcsFile->findPrevious(T_DOC_COMMENT_TAG, $i - 1, $i - 3);
if ($prev_tag !== FALSE && $tokens[$prev_tag]['content'] === '@dataProvider' && trim($content) === $oldName) {
$phpcsFile->fixer->replaceToken($i, $newName);
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fixer leaves attribute references stale.

When the fixer renames a provider, any #[DataProvider('oldName')] attribute still points at the old method. PHPUnit will then look up the stale identifier and crash the test run, turning a fixable violation into a runtime failure.(docs.phpunit.de)

@@
-      // Fix @dataProvider annotations.
+      // Fix @dataProvider annotations.
       if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
         $content = $tokens[$i]['content'];
         // Check if this string is part of @dataProvider annotation.
         $prev_tag = $phpcsFile->findPrevious(T_DOC_COMMENT_TAG, $i - 1, $i - 3);
         if ($prev_tag !== FALSE && $tokens[$prev_tag]['content'] === '@dataProvider' && trim($content) === $oldName) {
           $phpcsFile->fixer->replaceToken($i, $newName);
         }
       }
+
+      // Fix #[DataProvider('...')] attributes.
+      if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING) {
+        $clean = trim($tokens[$i]['content'], '\'"');
+        if ($clean !== $oldName) {
+          continue;
+        }
+
+        $attribute_ptr = $phpcsFile->findPrevious(T_ATTRIBUTE, $i);
+        $attribute_closer = $attribute_ptr !== FALSE ? ($tokens[$attribute_ptr]['attribute_closer'] ?? NULL) : NULL;
+        if ($attribute_ptr === FALSE || $attribute_closer === NULL || $attribute_closer < $i) {
+          continue;
+        }
+
+        $attribute_name_ptr = $phpcsFile->findNext(T_STRING, $attribute_ptr + 1, $attribute_closer);
+        if ($attribute_name_ptr === FALSE) {
+          continue;
+        }
+
+        $attribute_name = $tokens[$attribute_name_ptr]['content'];
+        if (!in_array($attribute_name, ['DataProvider', 'DataProviderExternal'], TRUE)) {
+          continue;
+        }
+
+        $quote = $tokens[$i]['content'][0];
+        if ($quote === '\'' || $quote === '"') {
+          $phpcsFile->fixer->replaceToken($i, $quote . $newName . $quote);
+        }
+      }
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private function fixProviderName(File $phpcsFile, string $oldName, string $newName): void {
$tokens = $phpcsFile->getTokens();
// Fix the method declaration.
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
// Fix method name in function declaration.
if ($tokens[$i]['code'] === T_STRING && $tokens[$i]['content'] === $oldName) {
// Check if this is a function declaration.
$prev_ptr = $phpcsFile->findPrevious(T_WHITESPACE, $i - 1, NULL, TRUE);
if ($prev_ptr !== FALSE && $tokens[$prev_ptr]['code'] === T_FUNCTION) {
$phpcsFile->fixer->replaceToken($i, $newName);
}
}
// Fix @dataProvider annotations.
if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
$content = $tokens[$i]['content'];
// Check if this string is part of @dataProvider annotation.
$prev_tag = $phpcsFile->findPrevious(T_DOC_COMMENT_TAG, $i - 1, $i - 3);
if ($prev_tag !== FALSE && $tokens[$prev_tag]['content'] === '@dataProvider' && trim($content) === $oldName) {
$phpcsFile->fixer->replaceToken($i, $newName);
}
}
}
private function fixProviderName(File $phpcsFile, string $oldName, string $newName): void {
$tokens = $phpcsFile->getTokens();
// Fix the method declaration.
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
// Fix method name in function declaration.
if ($tokens[$i]['code'] === T_STRING && $tokens[$i]['content'] === $oldName) {
// Check if this is a function declaration.
$prev_ptr = $phpcsFile->findPrevious(T_WHITESPACE, $i - 1, NULL, TRUE);
if ($prev_ptr !== FALSE && $tokens[$prev_ptr]['code'] === T_FUNCTION) {
$phpcsFile->fixer->replaceToken($i, $newName);
}
}
// Fix @dataProvider annotations.
if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
$content = $tokens[$i]['content'];
// Check if this string is part of @dataProvider annotation.
$prev_tag = $phpcsFile->findPrevious(T_DOC_COMMENT_TAG, $i - 1, $i - 3);
if ($prev_tag !== FALSE && $tokens[$prev_tag]['content'] === '@dataProvider' && trim($content) === $oldName) {
$phpcsFile->fixer->replaceToken($i, $newName);
}
}
// Fix #[DataProvider('...')] attributes.
if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING) {
$clean = trim($tokens[$i]['content'], '\'"');
if ($clean !== $oldName) {
continue;
}
$attribute_ptr = $phpcsFile->findPrevious(T_ATTRIBUTE, $i);
$attribute_closer = $attribute_ptr !== FALSE ? ($tokens[$attribute_ptr]['attribute_closer'] ?? NULL) : NULL;
if ($attribute_ptr === FALSE || $attribute_closer === NULL || $attribute_closer < $i) {
continue;
}
$attribute_name_ptr = $phpcsFile->findNext(T_STRING, $attribute_ptr + 1, $attribute_closer);
if ($attribute_name_ptr === FALSE) {
continue;
}
$attribute_name = $tokens[$attribute_name_ptr]['content'];
if (!in_array($attribute_name, ['DataProvider', 'DataProviderExternal'], TRUE)) {
continue;
}
$quote = $tokens[$i]['content'][0];
if ($quote === '\'' || $quote === '"') {
$phpcsFile->fixer->replaceToken($i, $quote . $newName . $quote);
}
}
}
}
🧰 Tools
🪛 PHPMD (2.15.0)

266-290: The method fixProviderName() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 10. (undefined)

(CyclomaticComplexity)


274-274: The variable $prev_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


284-284: The variable $prev_tag is not named in camelCase. (undefined)

(CamelCaseVariableName)

* Tests isTestMethod with valid test method.
*/
public function testIsTestMethodWithValidTest(): void {
$reflection = new \ReflectionClass($this->sniff);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider importing ReflectionClass.

The \ReflectionClass is used frequently throughout the test. Consider adding it to the imports at the top of the file for cleaner code.

Add to imports:

 use DrevOps\Sniffs\TestingPractices\DataProviderMatchesTestNameSniff;
 use PHP_CodeSniffer\Ruleset;
 use PHPUnit\Framework\Attributes\CoversClass;
+use ReflectionClass;

Then replace inline usage:

-    $reflection = new \ReflectionClass($this->sniff);
+    $reflection = new ReflectionClass($this->sniff);

Also applies to: 58-58, 76-76, 91-91, 109-109, 140-140, 165-165, 187-187, 304-304, 326-326, 348-348, 371-371, 398-398, 423-423, 446-446, 468-468, 496-496, 520-520, 543-543, 566-566, 589-589

🧰 Tools
🪛 PHPMD (2.15.0)

46-46: Missing class import via use statement (line '46', column '23'). (undefined)

(MissingImport)

🤖 Prompt for AI Agents
tests/Unit/DataProviderMatchesTestNameSniffTest.php around lines 46 (and also at
58, 76, 91, 109, 140, 165, 187, 304, 326, 348, 371, 398, 423, 446, 468, 496,
520, 543, 566, 589): import ReflectionClass at the top of the file (add "use
ReflectionClass;") and replace all occurrences of the fully-qualified
"\ReflectionClass" with the short "ReflectionClass" to clean up the code and
avoid repeated global namespace qualifiers.

Comment on lines +237 to +244
$error_messages = [];
foreach ($errors as $line_errors) {
foreach ($line_errors as $column_errors) {
foreach ($column_errors as $error) {
$error_messages[] = $error['message'];
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Extract duplicated error message extraction logic.

The error message extraction code is duplicated in two test methods. Consider extracting this into a helper method to improve maintainability.

Add a helper method to the test class:

/**
 * Extracts error messages from PHPCS file errors.
 *
 * @param array $errors
 *   The errors array from $file->getErrors().
 *
 * @return array
 *   Flat array of error messages.
 */
private function extractErrorMessages(array $errors): array {
  $error_messages = [];
  foreach ($errors as $line_errors) {
    foreach ($line_errors as $column_errors) {
      foreach ($column_errors as $error) {
        $error_messages[] = $error['message'];
      }
    }
  }
  return $error_messages;
}

Then replace the duplicated loops:

-    $error_messages = [];
-    foreach ($errors as $line_errors) {
-      foreach ($line_errors as $column_errors) {
-        foreach ($column_errors as $error) {
-          $error_messages[] = $error['message'];
-        }
-      }
-    }
+    $error_messages = $this->extractErrorMessages($errors);

Also applies to: 635-642

🧰 Tools
🪛 PHPMD (2.15.0)

237-237: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


238-238: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


239-239: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)

🤖 Prompt for AI Agents
In tests/Unit/DataProviderMatchesTestNameSniffTest.php around lines 237-244 (and
similarly 635-642), duplicated nested loops extract error messages from the
PHPCS $file->getErrors() structure; add a private helper method on the test
class named extractErrorMessages(array $errors): array that flattens the
three-level $errors array into a simple array of message strings (iterate the
same nested levels and collect $error['message'] into a result array and return
it), then replace both duplicated loop blocks with a call to
$this->extractErrorMessages($errors).

```
DrevOps.TestingPractices.DataProviderPrefix
DrevOps.TestingPractices.DataProviderMatchesTestName
DrevOps.TestingPractices.DataProviderOrder
```
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (3)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (2)

173-205: Missing support for PHP 8 attributes in provider discovery.

The findDataProviders() method only searches for @dataProvider annotations in docblocks but ignores PHP 8's #[DataProvider(...)] attributes. This means providers referenced via attributes will not be validated by this sniff, creating an incomplete enforcement of the naming convention.

Several fixtures in this PR use attribute syntax (e.g., DataProviderMatchingValid.php lines 81, 101; DataProviderOrderValid.php line 66), highlighting the importance of attribute support.

Based on learnings


266-290: Fixer doesn't update attribute references.

When fixProviderName() renames a provider method, it only updates @dataProvider annotations but leaves #[DataProvider('oldName')] attributes unchanged. This creates a mismatch where attributes still reference the old method name, breaking the test suite at runtime.

Based on learnings

src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (1)

162-278: Provider discovery logic duplicated with DataProviderOrderSniff

findDataProviderAnnotation() and findDataProviderAttribute() here are effectively the same as in DataProviderOrderSniff, which means any future change in how providers are discovered (additional attributes, proximity rules, external‑provider handling) must be applied in two places.

If you expect these sniffs to evolve, consider extracting a shared helper (e.g. a trait or utility class) that encapsulates provider resolution and is reused by both sniffs, keeping behavior in sync.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3e2d3 and 1ebe62a.

📒 Files selected for processing (16)
  • src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (1 hunks)
  • src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (1 hunks)
  • src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (1 hunks)
  • src/DrevOps/ruleset.xml (1 hunks)
  • tests/Fixtures/DataProviderInvalid.php (1 hunks)
  • tests/Fixtures/DataProviderMatchingInvalid.php (1 hunks)
  • tests/Fixtures/DataProviderMatchingValid.php (1 hunks)
  • tests/Fixtures/DataProviderOrderInvalid.php (1 hunks)
  • tests/Fixtures/DataProviderOrderValid.php (1 hunks)
  • tests/Fixtures/DataProviderValid.php (1 hunks)
  • tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php (1 hunks)
  • tests/Functional/DataProviderOrderSniffFunctionalTest.php (1 hunks)
  • tests/Functional/DataProviderPrefixSniffFunctionalTest.php (1 hunks)
  • tests/Unit/DataProviderMatchesTestNameSniffTest.php (1 hunks)
  • tests/Unit/DataProviderOrderSniffTest.php (1 hunks)
  • tests/Unit/DataProviderPrefixSniffTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
tests/Fixtures/DataProviderMatchingValid.php (3)
tests/Fixtures/DataProviderMatchingInvalid.php (2)
  • DataProvider (81-84)
  • DataProvider (101-104)
tests/Fixtures/DataProviderOrderInvalid.php (1)
  • DataProvider (70-73)
tests/Fixtures/DataProviderOrderValid.php (1)
  • DataProvider (66-69)
tests/Fixtures/DataProviderValid.php (1)
tests/Fixtures/DataProviderInvalid.php (2)
  • testSomething (19-21)
  • testAnother (40-42)
tests/Functional/DataProviderPrefixSniffFunctionalTest.php (1)
tests/Functional/FunctionalTestCase.php (2)
  • FunctionalTestCase (17-142)
  • runPhpcs (62-87)
tests/Fixtures/DataProviderInvalid.php (1)
tests/Fixtures/DataProviderValid.php (2)
  • testSomething (19-21)
  • testAnother (40-42)
tests/Fixtures/DataProviderOrderInvalid.php (1)
tests/Fixtures/DataProviderOrderValid.php (6)
  • DataProvider (66-69)
  • dataProviderUserLogin (29-34)
  • testUserLogin (20-22)
  • dataProviderEmailValidation (57-61)
  • testEmailValidation (41-43)
  • dataProviderAuthentication (76-80)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (4)
  • process (56-81)
  • isTestClass (94-124)
  • findDataProviderAnnotation (314-360)
  • findDataProviderAttribute (373-419)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (2)
  • process (58-111)
  • isTestClass (124-162)
tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php (1)
tests/Functional/FunctionalTestCase.php (2)
  • FunctionalTestCase (17-142)
  • runPhpcs (62-87)
tests/Functional/DataProviderOrderSniffFunctionalTest.php (2)
tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php (2)
  • CoversNothing (13-72)
  • Group (21-24)
tests/Functional/FunctionalTestCase.php (2)
  • FunctionalTestCase (17-142)
  • runPhpcs (62-87)
tests/Fixtures/DataProviderMatchingInvalid.php (1)
tests/Fixtures/DataProviderMatchingValid.php (2)
  • DataProvider (81-84)
  • DataProvider (101-104)
src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (1)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (3)
  • isTestClass (95-133)
  • findDataProviderAnnotation (173-219)
  • findDataProviderAttribute (232-278)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (3)
  • register (35-37)
  • process (42-82)
  • isTestClass (95-133)
src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (3)
  • register (49-51)
  • process (56-81)
  • isTestClass (94-124)
tests/Unit/DataProviderPrefixSniffTest.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php (2)
  • DataProviderPrefixSniff (17-292)
  • register (51-53)
tests/Unit/UnitTestCase.php (3)
  • UnitTestCase (18-152)
  • processCode (46-53)
  • findFunctionToken (85-93)
tests/Unit/DataProviderOrderSniffTest.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php (2)
  • DataProviderOrderSniff (27-421)
  • register (49-51)
tests/Unit/UnitTestCase.php (4)
  • UnitTestCase (18-152)
  • processCode (46-53)
  • findClassToken (104-112)
  • findFunctionToken (85-93)
tests/Unit/DataProviderMatchesTestNameSniffTest.php (2)
src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php (2)
  • DataProviderMatchesTestNameSniff (25-296)
  • register (35-37)
tests/Unit/UnitTestCase.php (3)
  • UnitTestCase (18-152)
  • processCode (46-53)
  • findFunctionToken (85-93)
🪛 PHPMD (2.15.0)
tests/Fixtures/DataProviderMatchingValid.php

20-20: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)


71-76: The method AuthenticationScenarios is not named in camelCase. (undefined)

(CamelCaseMethodName)


111-116: The method TokenGeneration is not named in camelCase. (undefined)

(CamelCaseMethodName)

tests/Fixtures/DataProviderOrderInvalid.php

32-32: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)

src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php

49-49: The variable $method_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


55-55: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


63-63: The variable $test_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


66-66: The variable $provider_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


99-99: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


105-105: The variable $class_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


111-111: The variable $class_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


120-120: The variable $extends_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


122-122: The variable $parent_class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


124-124: The variable $parent_class is not named in camelCase. (undefined)

(CamelCaseVariableName)


177-177: The variable $comment_end is not named in camelCase. (undefined)

(CamelCaseVariableName)


182-182: The variable $comment_start is not named in camelCase. (undefined)

(CamelCaseVariableName)


200-200: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


206-206: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


236-236: The variable $attribute_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


248-248: The variable $name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


255-255: The variable $open_paren is not named in camelCase. (undefined)

(CamelCaseVariableName)


262-262: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


269-269: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)

tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php

14-72: Avoid excessively long class names like DataProviderMatchesTestNameSniffFunctionalTest. Keep class name length under 40. (undefined)

(LongClassName)

tests/Fixtures/DataProviderMatchingInvalid.php

20-20: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)


111-116: The method TokenGenerationCases is not named in camelCase. (undefined)

(CamelCaseMethodName)

src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php

27-421: The class DataProviderOrderSniff has an overall complexity of 50 which is very high. The configured complexity threshold is 50. (undefined)

(ExcessiveClassComplexity)


65-65: The variable $class_start is not named in camelCase. (undefined)

(CamelCaseVariableName)


66-66: The variable $class_end is not named in camelCase. (undefined)

(CamelCaseVariableName)


98-98: The variable $class_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


104-104: The variable $class_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


112-112: The variable $extends_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


114-114: The variable $parent_class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


116-116: The variable $parent_class is not named in camelCase. (undefined)

(CamelCaseVariableName)


145-145: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


148-148: The variable $method_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


154-154: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


162-162: The variable $provider_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


173-173: The variable $test_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


202-202: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


205-205: The variable $method_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


211-211: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


230-301: The method validateOrder() has a Cyclomatic Complexity of 14. The configured cyclomatic complexity threshold is 10. (undefined)

(CyclomaticComplexity)


230-301: The method validateOrder() has an NPath complexity of 361. The configured NPath complexity threshold is 200. (undefined)

(NPathComplexity)


232-232: The variable $checked_providers is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $test_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $test_info is not named in camelCase. (undefined)

(CamelCaseVariableName)


235-235: The variable $provider_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


236-236: The variable $test_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


258-258: The variable $provider_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


261-261: The variable $has_violation is not named in camelCase. (undefined)

(CamelCaseVariableName)


262-262: The variable $error_code is not named in camelCase. (undefined)

(CamelCaseVariableName)


263-263: The variable $error_message is not named in camelCase. (undefined)

(CamelCaseVariableName)


288-288: The variable $error_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


318-318: The variable $comment_end is not named in camelCase. (undefined)

(CamelCaseVariableName)


323-323: The variable $comment_start is not named in camelCase. (undefined)

(CamelCaseVariableName)


341-341: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


347-347: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


377-377: The variable $attribute_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


389-389: The variable $name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


396-396: The variable $open_paren is not named in camelCase. (undefined)

(CamelCaseVariableName)


403-403: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


410-410: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)

src/DrevOps/Sniffs/TestingPractices/DataProviderPrefixSniff.php

78-78: The variable $function_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


86-86: The variable $function_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


99-99: The variable $suggested_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


128-128: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


134-134: The variable $class_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


142-142: The variable $class_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


150-150: The variable $extends_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


152-152: The variable $parent_class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


154-154: The variable $parent_class is not named in camelCase. (undefined)

(CamelCaseVariableName)


188-188: The variable $string_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


193-193: The variable $method_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


232-232: The variable $common_prefixes is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $common_prefix is not named in camelCase. (undefined)

(CamelCaseVariableName)


266-290: The method fixProviderName() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 10. (undefined)

(CyclomaticComplexity)


274-274: The variable $prev_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


284-284: The variable $prev_tag is not named in camelCase. (undefined)

(CamelCaseVariableName)

tests/Fixtures/DataProviderOrderValid.php

20-20: Avoid unused parameters such as '$pass'. (undefined)

(UnusedFormalParameter)


48-50: Avoid unused private methods such as 'helperValidate'. (undefined)

(UnusedPrivateMethod)

tests/Unit/DataProviderPrefixSniffTest.php

15-574: The class DataProviderPrefixSniffTest has 25 public methods. Consider refactoring DataProviderPrefixSniffTest to keep number of public methods under 10. (undefined)

(TooManyPublicMethods)


54-54: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


56-56: Missing class import via use statement (line '56', column '23'). (undefined)

(MissingImport)


76-76: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


78-78: Missing class import via use statement (line '78', column '23'). (undefined)

(MissingImport)


99-99: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


101-101: Missing class import via use statement (line '101', column '23'). (undefined)

(MissingImport)


121-121: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


123-123: Missing class import via use statement (line '123', column '23'). (undefined)

(MissingImport)


160-160: Missing class import via use statement (line '160', column '23'). (undefined)

(MissingImport)


194-194: Missing class import via use statement (line '194', column '23'). (undefined)

(MissingImport)


210-210: Missing class import via use statement (line '210', column '23'). (undefined)

(MissingImport)


222-222: Missing class import via use statement (line '222', column '23'). (undefined)

(MissingImport)


236-236: Missing class import via use statement (line '236', column '23'). (undefined)

(MissingImport)


248-248: Missing class import via use statement (line '248', column '23'). (undefined)

(MissingImport)


260-260: Missing class import via use statement (line '260', column '23'). (undefined)

(MissingImport)


272-272: Missing class import via use statement (line '272', column '23'). (undefined)

(MissingImport)


284-284: Missing class import via use statement (line '284', column '23'). (undefined)

(MissingImport)


296-296: Missing class import via use statement (line '296', column '23'). (undefined)

(MissingImport)


354-354: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


355-355: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


356-356: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


427-427: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


428-428: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


429-429: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


473-473: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


475-475: Missing class import via use statement (line '475', column '23'). (undefined)

(MissingImport)


501-501: Missing class import via use statement (line '501', column '23'). (undefined)

(MissingImport)


526-526: Missing class import via use statement (line '526', column '23'). (undefined)

(MissingImport)


539-539: Missing class import via use statement (line '539', column '23'). (undefined)

(MissingImport)

tests/Unit/DataProviderOrderSniffTest.php

15-524: The class DataProviderOrderSniffTest has 22 public methods. Consider refactoring DataProviderOrderSniffTest to keep number of public methods under 10. (undefined)

(TooManyPublicMethods)


89-89: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


90-90: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


91-91: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


237-237: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


239-239: Missing class import via use statement (line '239', column '23'). (undefined)

(MissingImport)


259-259: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


261-261: Missing class import via use statement (line '261', column '23'). (undefined)

(MissingImport)


281-281: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


283-283: Missing class import via use statement (line '283', column '23'). (undefined)

(MissingImport)


303-303: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


305-305: Missing class import via use statement (line '305', column '23'). (undefined)

(MissingImport)


341-341: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


343-343: Missing class import via use statement (line '343', column '23'). (undefined)

(MissingImport)


368-368: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


370-370: Missing class import via use statement (line '370', column '23'). (undefined)

(MissingImport)


393-393: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


395-395: Missing class import via use statement (line '395', column '23'). (undefined)

(MissingImport)


421-421: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


423-423: Missing class import via use statement (line '423', column '23'). (undefined)

(MissingImport)


445-445: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


447-447: Missing class import via use statement (line '447', column '23'). (undefined)

(MissingImport)


468-468: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


470-470: Missing class import via use statement (line '470', column '23'). (undefined)

(MissingImport)


491-491: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


493-493: Missing class import via use statement (line '493', column '23'). (undefined)

(MissingImport)


514-514: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


516-516: Missing class import via use statement (line '516', column '23'). (undefined)

(MissingImport)

tests/Unit/DataProviderMatchesTestNameSniffTest.php

15-649: The class DataProviderMatchesTestNameSniffTest has 28 non-getter- and setter-methods. Consider refactoring DataProviderMatchesTestNameSniffTest to keep number of methods under 25. (undefined)

(TooManyMethods)


15-649: The class DataProviderMatchesTestNameSniffTest has 28 public methods. Consider refactoring DataProviderMatchesTestNameSniffTest to keep number of public methods under 10. (undefined)

(TooManyPublicMethods)


46-46: Missing class import via use statement (line '46', column '23'). (undefined)

(MissingImport)


58-58: Missing class import via use statement (line '58', column '23'). (undefined)

(MissingImport)


76-76: Missing class import via use statement (line '76', column '23'). (undefined)

(MissingImport)


91-91: Missing class import via use statement (line '91', column '23'). (undefined)

(MissingImport)


109-109: Missing class import via use statement (line '109', column '23'). (undefined)

(MissingImport)


138-138: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


140-140: Missing class import via use statement (line '140', column '23'). (undefined)

(MissingImport)


163-163: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


165-165: Missing class import via use statement (line '165', column '23'). (undefined)

(MissingImport)


185-185: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


187-187: Missing class import via use statement (line '187', column '23'). (undefined)

(MissingImport)


237-237: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


238-238: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


239-239: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


302-302: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


304-304: Missing class import via use statement (line '304', column '23'). (undefined)

(MissingImport)


324-324: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


326-326: Missing class import via use statement (line '326', column '23'). (undefined)

(MissingImport)


346-346: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


348-348: Missing class import via use statement (line '348', column '23'). (undefined)

(MissingImport)


369-369: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


371-371: Missing class import via use statement (line '371', column '23'). (undefined)

(MissingImport)


396-396: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


398-398: Missing class import via use statement (line '398', column '23'). (undefined)

(MissingImport)


421-421: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


423-423: Missing class import via use statement (line '423', column '23'). (undefined)

(MissingImport)


444-444: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


446-446: Missing class import via use statement (line '446', column '23'). (undefined)

(MissingImport)


466-466: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


468-468: Missing class import via use statement (line '468', column '23'). (undefined)

(MissingImport)


494-494: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


496-496: Missing class import via use statement (line '496', column '23'). (undefined)

(MissingImport)


518-518: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


520-520: Missing class import via use statement (line '520', column '23'). (undefined)

(MissingImport)


541-541: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


543-543: Missing class import via use statement (line '543', column '23'). (undefined)

(MissingImport)


564-564: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


566-566: Missing class import via use statement (line '566', column '23'). (undefined)

(MissingImport)


587-587: The variable $function_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


589-589: Missing class import via use statement (line '589', column '23'). (undefined)

(MissingImport)


635-635: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


636-636: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


637-637: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)

🔇 Additional comments (13)
src/DrevOps/ruleset.xml (1)

10-26: LGTM! Well-structured ruleset configuration.

The three new rules are properly configured with sensible defaults: dataProvider prefix and after positioning for providers. The XML structure follows PHPCS conventions correctly.

tests/Functional/DataProviderOrderSniffFunctionalTest.php (1)

1-62: LGTM! Comprehensive functional test coverage.

The functional test properly exercises smoke, valid, and invalid ordering scenarios. The test structure follows the established pattern and includes detailed violation assertions with exact error messages and sources.

tests/Fixtures/DataProviderValid.php (1)

1-86: LGTM! Well-designed fixture demonstrating valid patterns.

The fixture comprehensively demonstrates valid data provider usage including the dataProvider prefix convention, proper typing, and shared provider scenarios. This provides good coverage for testing the sniffs.

tests/Fixtures/DataProviderOrderValid.php (1)

1-82: LGTM! Fixture correctly demonstrates valid provider ordering.

The fixture properly illustrates that providers should appear after their tests and that helper methods between them are acceptable. The use of both annotation (@dataProvider) and attribute (#[DataProvider]) syntax is important for comprehensive testing.

Note: The PHPMD warnings about unused parameters and methods are expected for test fixtures and can be safely ignored.

tests/Fixtures/DataProviderMatchingValid.php (1)

1-118: LGTM! Fixture comprehensively covers valid provider/test name matching.

The fixture demonstrates valid matching patterns with various prefixes and both annotation/attribute syntax. The intentional use of non-camelCase provider names (AuthenticationScenarios, TokenGeneration) correctly tests that the matching logic prioritizes name alignment over camelCase conventions.

Note: PHPMD warnings are false positives for this test fixture.

tests/Fixtures/DataProviderOrderInvalid.php (1)

1-75: LGTM! Fixture clearly demonstrates ordering violations.

The fixture properly illustrates invalid ordering by placing providers before their tests. The clear violation markers in comments and the use of both annotation/attribute syntax provide comprehensive coverage for testing the order enforcement logic.

Note: PHPMD warning about unused parameter is expected for test fixtures.

tests/Functional/DataProviderPrefixSniffFunctionalTest.php (1)

1-75: LGTM! Functional tests provide solid coverage for annotation-based providers.

The tests comprehensively validate smoke, valid, and invalid scenarios with detailed violation assertions. The expected violations correctly verify the prefix enforcement and name suggestion logic.

However, these tests currently only cover @dataProvider annotations. Once attribute support is added to the sniff (as flagged in previous comments), consider adding test cases for #[DataProvider(...)] attributes to ensure comprehensive coverage.

tests/Fixtures/DataProviderInvalid.php (1)

14-127: Fixture structure is consistent with prefix-sniff scenarios

The fixture cleanly exercises a variety of non‑dataProvider* prefixes (none, provide, get, shared provider), which matches the intended negative cases for the prefix sniff without introducing extra complexity. No changes needed.

tests/Functional/DataProviderMatchesTestNameSniffFunctionalTest.php (1)

16-69: Functional coverage and expectations look correct

The functional test wires $sniffSource correctly, exercises both the all‑clear path and concrete failure cases, and asserts full message/source/fixable triplets that match the sniff’s error template and the invalid fixture. This should give good confidence the sniff behaves as intended across docblock and attribute providers.

tests/Fixtures/DataProviderMatchingInvalid.php (1)

15-116: Invalid matching scenarios are well‑covered

This fixture systematically exercises the different ways provider names can fail to match their tests (partial, suffix, completely different, and attribute‑based), which aligns with what the matching sniff is validating. Looks good as a negative test fixture.

tests/Unit/DataProviderPrefixSniffTest.php (1)

25-572: Unit tests give thorough coverage of DataProviderPrefixSniff behavior

The test class systematically covers class detection, provider discovery (including external references and missing names), prefix validation/customization, name suggestion edge cases, and process() behavior across valid/invalid/irrelevant scenarios. The previous PHPUnit metadata issue around literal @dataProvider in docblocks is also avoided now. No changes needed.

tests/Unit/DataProviderOrderSniffTest.php (1)

1-524: Excellent comprehensive test coverage!

The test suite thoroughly validates DataProviderOrderSniff behavior across numerous scenarios: correct/incorrect ordering, helper methods, shared providers, external providers, PHP 8 attributes, class detection, and edge cases for annotation/attribute parsing. The use of reflection to test private methods is appropriate for unit testing.

tests/Unit/DataProviderMatchesTestNameSniffTest.php (1)

1-649: Excellent comprehensive test coverage!

The test suite provides thorough validation of DataProviderMatchesTestNameSniff across 28 test methods, covering registration, test method detection, name extraction, provider matching logic, annotation/attribute parsing, process flow scenarios, class detection, and numerous edge cases. The structure mirrors DataProviderOrderSniffTest appropriately.

Note: Past review comments have already identified helpful maintainability improvements (ReflectionClass import, error message extraction helper). These suggestions remain valid and would benefit both test files.

Comment on lines +84 to +133
/**
* Determines if the current file contains a test class.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile
* The file being scanned.
* @param int $stackPtr
* The position of the current token.
*
* @return bool
* TRUE if the file contains a test class, FALSE otherwise.
*/
private function isTestClass(File $phpcsFile, int $stackPtr): bool {
$tokens = $phpcsFile->getTokens();

// Find the class token.
$class_ptr = $phpcsFile->findPrevious(T_CLASS, $stackPtr);
if ($class_ptr === FALSE) {
return FALSE;
}

// Get the class name.
$class_name_ptr = $phpcsFile->findNext(T_STRING, $class_ptr + 1, $class_ptr + 3);
// @codeCoverageIgnoreStart
if ($class_name_ptr === FALSE) {
return FALSE;
}
// @codeCoverageIgnoreEnd
$class_name = $tokens[$class_name_ptr]['content'];

// Check if class name ends with Test or TestCase.
if (preg_match('/Test(Case)?$/', $class_name) === 1) {
return TRUE;
}

// Check if class extends TestCase or similar.
// @codeCoverageIgnoreStart
$extends_ptr = $phpcsFile->findNext(T_EXTENDS, $class_ptr + 1, $tokens[$class_ptr]['scope_opener']);
if ($extends_ptr !== FALSE) {
$parent_class_ptr = $phpcsFile->findNext(T_STRING, $extends_ptr + 1, $tokens[$class_ptr]['scope_opener']);
if ($parent_class_ptr !== FALSE) {
$parent_class = $tokens[$parent_class_ptr]['content'];
if (preg_match('/TestCase$/', $parent_class) === 1) {
return TRUE;
}
}
}

return FALSE;
// @codeCoverageIgnoreEnd
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

isTestClass() can misclassify global test‑style functions as being in a test class

isTestClass() finds the nearest preceding T_CLASS but never checks that the current T_FUNCTION actually lies within that class’s scope. In a file that defines a test class followed by a global function like function testSomething() {}, isTestClass() will return true, so process() will treat that global function as a test method and enforce provider naming on it.

If your intention is to limit this sniff strictly to methods of test classes, consider tightening isTestClass() to ensure $stackPtr is inside the class’s scope_opener/scope_closer range before checking name/extends, or reusing the more robust implementation already used in DataProviderPrefixSniff.

🧰 Tools
🪛 PHPMD (2.15.0)

99-99: The variable $class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


105-105: The variable $class_name_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


111-111: The variable $class_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


120-120: The variable $extends_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


122-122: The variable $parent_class_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)


124-124: The variable $parent_class is not named in camelCase. (undefined)

(CamelCaseVariableName)

🤖 Prompt for AI Agents
In src/DrevOps/Sniffs/TestingPractices/DataProviderMatchesTestNameSniff.php
around lines 84 to 133, isTestClass() currently finds the nearest preceding
T_CLASS but doesn't verify that the current token ($stackPtr) is inside that
class's scope, which causes global test-style functions after a class to be
misclassified; fix by ensuring the located class actually encloses $stackPtr:
after finding $class_ptr, check that the class has 'scope_opener' and
'scope_closer' in $tokens[$class_ptr] and return FALSE if they are missing or if
$stackPtr is not strictly between scope_opener and scope_closer, then proceed
with the existing name/extends checks (or, alternatively, walk previous classes
until you find one that contains $stackPtr or return FALSE if none do).

Comment on lines +230 to +301
private function validateOrder(File $phpcsFile, array $tests, array $providers): void {
// Track which providers we've already checked.
$checked_providers = [];

foreach ($tests as $test_name => $test_info) {
$provider_name = $test_info['provider'];
$test_line = $test_info['line'];

// Ensure provider name is a string.
// @codeCoverageIgnoreStart
if (!is_string($provider_name)) {
continue;
}
// @codeCoverageIgnoreEnd
// Skip if provider already checked (shared provider).
if (isset($checked_providers[$provider_name])) {
continue;
}

// Mark as checked.
$checked_providers[$provider_name] = TRUE;

// Check if provider exists in this class.
// @codeCoverageIgnoreStart
if (!isset($providers[$provider_name])) {
continue;
}
// @codeCoverageIgnoreEnd
$provider_line = $providers[$provider_name];

// Validate ordering based on configuration.
$has_violation = FALSE;
$error_code = '';
$error_message = '';

if ($this->providerPosition === 'after') {
// Provider should be after test.
if ($provider_line < $test_line) {
$has_violation = TRUE;
$error_code = self::CODE_PROVIDER_BEFORE_TEST;
$error_message = 'Data provider method "%s" (line %d) appears before test method "%s" (line %d). Providers should be defined after their test methods';
}
}
// @codeCoverageIgnoreStart
elseif ($this->providerPosition === 'before') {
// Provider should be before test.
if ($provider_line > $test_line) {
$has_violation = TRUE;
$error_code = self::CODE_PROVIDER_AFTER_TEST;
$error_message = 'Data provider method "%s" (line %d) appears after test method "%s" (line %d). Providers should be defined before their test methods';
}
}
// @codeCoverageIgnoreEnd
if ($has_violation) {
$data = [$provider_name, $provider_line, $test_name, $test_line];

// Find the provider function token to report error at.
$tokens = $phpcsFile->getTokens();
$error_ptr = NULL;
foreach ($tokens as $ptr => $token) {
if ($token['line'] === $provider_line && $token['code'] === T_STRING) {
$error_ptr = (int) $ptr;
break;
}
}

if ($error_ptr !== NULL) {
$phpcsFile->addError($error_message, $error_ptr, $error_code, $data);
}
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Tighten error location to the actual provider method name

In validateOrder(), the loop that finds $error_ptr only checks for T_STRING tokens on $provider_line and uses the first match it encounters. On lines that contain other identifiers (e.g., type hints or return types), the error can end up attached to the wrong symbol even though the message mentions the provider.

Consider also checking the token content before assigning $error_ptr, for example:

-        foreach ($tokens as $ptr => $token) {
-          if ($token['line'] === $provider_line && $token['code'] === T_STRING) {
-            $error_ptr = (int) $ptr;
-            break;
-          }
-        }
+        foreach ($tokens as $ptr => $token) {
+          if ($token['line'] === $provider_line
+            && $token['code'] === T_STRING
+            && $token['content'] === $provider_name
+          ) {
+            $error_ptr = (int) $ptr;
+            break;
+          }
+        }

This keeps the error anchored to the provider method itself and avoids surprising highlights when additional identifiers are present on the same line.

🧰 Tools
🪛 PHPMD (2.15.0)

230-301: The method validateOrder() has a Cyclomatic Complexity of 14. The configured cyclomatic complexity threshold is 10. (undefined)

(CyclomaticComplexity)


230-301: The method validateOrder() has an NPath complexity of 361. The configured NPath complexity threshold is 200. (undefined)

(NPathComplexity)


232-232: The variable $checked_providers is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $test_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


234-234: The variable $test_info is not named in camelCase. (undefined)

(CamelCaseVariableName)


235-235: The variable $provider_name is not named in camelCase. (undefined)

(CamelCaseVariableName)


236-236: The variable $test_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


258-258: The variable $provider_line is not named in camelCase. (undefined)

(CamelCaseVariableName)


261-261: The variable $has_violation is not named in camelCase. (undefined)

(CamelCaseVariableName)


262-262: The variable $error_code is not named in camelCase. (undefined)

(CamelCaseVariableName)


263-263: The variable $error_message is not named in camelCase. (undefined)

(CamelCaseVariableName)


288-288: The variable $error_ptr is not named in camelCase. (undefined)

(CamelCaseVariableName)

🤖 Prompt for AI Agents
In src/DrevOps/Sniffs/TestingPractices/DataProviderOrderSniff.php around lines
230-301, the code finds an error pointer by picking the first T_STRING on the
provider line which can be a type or other identifier; change the search to
ensure the token content matches the provider method name and that it is the
function name (e.g. verify the token's content equals $provider_name and confirm
it is the function identifier by checking the nearest preceding significant
token is T_FUNCTION or by locating the T_FUNCTION token and taking its following
T_STRING). Use that matched pointer as $error_ptr so the reported error anchors
to the actual provider method name.

Comment on lines +89 to +96
$error_messages = [];
foreach ($errors as $line_errors) {
foreach ($line_errors as $column_errors) {
foreach ($column_errors as $error) {
$error_messages[] = $error['message'];
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Extract duplicated error message extraction logic.

This nested loop pattern for extracting error messages from PHPCS errors appears in multiple places (here and in DataProviderMatchesTestNameSniffTest). Consider extracting this into a helper method in the base UnitTestCase class for reuse across test files.

Example helper method for UnitTestCase:

/**
 * Extracts error messages from PHPCS file errors.
 *
 * @param array $errors
 *   The errors array from $file->getErrors().
 *
 * @return array
 *   Flat array of error messages.
 */
protected function extractErrorMessages(array $errors): array {
  $error_messages = [];
  foreach ($errors as $line_errors) {
    foreach ($line_errors as $column_errors) {
      foreach ($column_errors as $error) {
        $error_messages[] = $error['message'];
      }
    }
  }
  return $error_messages;
}

Then replace usage:

-    $error_messages = [];
-    foreach ($errors as $line_errors) {
-      foreach ($line_errors as $column_errors) {
-        foreach ($column_errors as $error) {
-          $error_messages[] = $error['message'];
-        }
-      }
-    }
+    $error_messages = $this->extractErrorMessages($errors);
🧰 Tools
🪛 PHPMD (2.15.0)

89-89: The variable $error_messages is not named in camelCase. (undefined)

(CamelCaseVariableName)


90-90: The variable $line_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)


91-91: The variable $column_errors is not named in camelCase. (undefined)

(CamelCaseVariableName)

🤖 Prompt for AI Agents
In tests/Unit/DataProviderOrderSniffTest.php around lines 89 to 96, duplicated
nested loops are used to extract PHPCS error messages; add a protected helper
method extractErrorMessages(array $errors): array to the base UnitTestCase that
flattens the $file->getErrors() structure into a simple array of message
strings, then replace the three-level foreach block in this file (and other
tests like DataProviderMatchesTestNameSniffTest) with a single call to
$this->extractErrorMessages($errors) to return the flat array.

$file = $this->processCode($code);
$class_ptr = $this->findClassToken($file);

$reflection = new \ReflectionClass($this->sniff);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider importing ReflectionClass.

\ReflectionClass is used frequently throughout this test file. Adding it to the imports at the top would improve code readability and consistency.

Add to imports:

 use DrevOps\Sniffs\TestingPractices\DataProviderOrderSniff;
 use PHP_CodeSniffer\Ruleset;
 use PHPUnit\Framework\Attributes\CoversClass;
+use ReflectionClass;

Then replace all instances:

-    $reflection = new \ReflectionClass($this->sniff);
+    $reflection = new ReflectionClass($this->sniff);

Also applies to: 261, 283, 305, 343, 370, 395, 423, 447, 470, 493, 516

🧰 Tools
🪛 PHPMD (2.15.0)

239-239: Missing class import via use statement (line '239', column '23'). (undefined)

(MissingImport)

🤖 Prompt for AI Agents
In tests/Unit/DataProviderOrderSniffTest.php around line 239 (and also at lines
261, 283, 305, 343, 370, 395, 423, 447, 470, 493, 516), the test repeatedly uses
the global class reference \ReflectionClass; add a use statement for
ReflectionClass at the top of the file and then replace each occurrence of
\ReflectionClass with the imported ReflectionClass to improve readability and
consistency across the file.

@AlexSkrypnyk AlexSkrypnyk merged commit ebf70e1 into main Nov 23, 2025
9 checks passed
@AlexSkrypnyk AlexSkrypnyk deleted the feature/data-provider branch November 23, 2025 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants