From 2de1b17b9da4f93287af1b68debf7ce906a9a912 Mon Sep 17 00:00:00 2001 From: Alex Gandy Date: Wed, 21 Jan 2026 21:33:44 -0500 Subject: [PATCH 1/4] Add coverage examples for Cobertura, JaCoCo and Phpunit + Clover. --- .github/workflows/java.yml | 67 +++++ .github/workflows/phpunit.yml | 86 +++++++ .github/workflows/pytest.yml | 24 +- java-example/.gitignore | 22 ++ java-example/README.md | 46 ++++ java-example/pom.xml | 85 +++++++ .../src/main/java/com/example/Calculator.java | 83 ++++++ .../test/java/com/example/CalculatorTest.java | 236 ++++++++++++++++++ php-example/.gitignore | 5 + php-example/README.md | 50 ++++ php-example/composer.json | 26 ++ php-example/phpunit.xml | 28 +++ php-example/src/Calculator.php | 72 ++++++ php-example/tests/CalculatorTest.php | 154 ++++++++++++ pytest-example/requirements.txt | 1 + 15 files changed, 982 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/java.yml create mode 100644 .github/workflows/phpunit.yml create mode 100644 java-example/.gitignore create mode 100644 java-example/README.md create mode 100644 java-example/pom.xml create mode 100644 java-example/src/main/java/com/example/Calculator.java create mode 100644 java-example/src/test/java/com/example/CalculatorTest.java create mode 100644 php-example/.gitignore create mode 100644 php-example/README.md create mode 100644 php-example/composer.json create mode 100644 php-example/phpunit.xml create mode 100644 php-example/src/Calculator.php create mode 100644 php-example/tests/CalculatorTest.php diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml new file mode 100644 index 0000000..689f090 --- /dev/null +++ b/.github/workflows/java.yml @@ -0,0 +1,67 @@ +name: Java Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +defaults: + run: + working-directory: java-example + +jobs: + test: + name: JUnit 5 + JaCoCo Coverage + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "temurin" + cache: "maven" + + - name: Run tests with coverage + run: mvn clean test + continue-on-error: true + + # Upload JUnit test results to Gaffer + # Skip on Dependabot PRs (no access to secrets) + - name: Upload JUnit results to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.4.0 + with: + gaffer_upload_token: ${{ secrets.GAFFER_UPLOAD_TOKEN }} + report_path: java-example/target/surefire-reports/ + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.head_ref || github.ref_name }} + test_framework: junit + test_suite: junit-results + + # Upload JaCoCo coverage to Gaffer + - name: Upload JaCoCo coverage to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.4.0 + with: + gaffer_upload_token: ${{ secrets.GAFFER_UPLOAD_TOKEN }} + report_path: java-example/target/site/jacoco/jacoco.xml + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.head_ref || github.ref_name }} + test_framework: junit + test_suite: jacoco-coverage + + # Store artifacts for parser development + - name: Upload test artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: java-reports-${{ github.sha }} + path: | + java-example/target/surefire-reports/ + java-example/target/site/jacoco/ + retention-days: 7 diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml new file mode 100644 index 0000000..acdc1c7 --- /dev/null +++ b/.github/workflows/phpunit.yml @@ -0,0 +1,86 @@ +name: PHPUnit Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +defaults: + run: + working-directory: php-example + +jobs: + test: + name: PHPUnit + Clover Coverage + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.2" + coverage: pcov + tools: composer:v2 + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('php-example/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Create reports directory + run: mkdir -p reports + + - name: Run tests with coverage + run: | + vendor/bin/phpunit \ + --coverage-clover reports/clover.xml \ + --coverage-html reports/htmlcov \ + --log-junit reports/phpunit-results.xml + continue-on-error: true + + # Upload PHPUnit test results to Gaffer + # Skip on Dependabot PRs (no access to secrets) + - name: Upload PHPUnit results to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.4.0 + with: + gaffer_upload_token: ${{ secrets.GAFFER_UPLOAD_TOKEN }} + report_path: php-example/reports/phpunit-results.xml + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.head_ref || github.ref_name }} + test_framework: phpunit + test_suite: phpunit-results + + # Upload Clover coverage to Gaffer + - name: Upload Clover coverage to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.4.0 + with: + gaffer_upload_token: ${{ secrets.GAFFER_UPLOAD_TOKEN }} + report_path: php-example/reports/clover.xml + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.head_ref || github.ref_name }} + test_framework: phpunit + test_suite: phpunit-coverage + + # Store artifacts for parser development + - name: Upload test artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: phpunit-reports-${{ github.sha }} + path: php-example/reports/ + retention-days: 7 diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index eb96f7c..9f6b630 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -32,8 +32,14 @@ jobs: - name: Create reports directory run: mkdir -p reports - - name: Run tests with HTML output - run: pytest --html=reports/pytest-report.html --self-contained-html + - name: Run tests with HTML output and coverage + run: | + pytest \ + --html=reports/pytest-report.html \ + --self-contained-html \ + --cov=src \ + --cov-report=xml:reports/coverage.xml \ + --cov-report=html:reports/htmlcov continue-on-error: true # Upload via GitHub Action (recommended) @@ -49,6 +55,18 @@ jobs: test_framework: pytest test_suite: pytest-html + # Upload Cobertura coverage report + - name: Upload coverage to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.4.0 + with: + gaffer_upload_token: ${{ secrets.GAFFER_UPLOAD_TOKEN }} + report_path: pytest-example/reports/coverage.xml + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.head_ref || github.ref_name }} + test_framework: pytest + test_suite: pytest-coverage + # Alternative: Upload via curl (for documentation/reference) # - name: Upload via curl (example) # if: always() && github.actor != 'dependabot[bot]' @@ -71,4 +89,4 @@ jobs: with: name: pytest-reports-${{ github.sha }} path: pytest-example/reports/ - retention-days: 30 + retention-days: 7 diff --git a/java-example/.gitignore b/java-example/.gitignore new file mode 100644 index 0000000..dfb3f40 --- /dev/null +++ b/java-example/.gitignore @@ -0,0 +1,22 @@ +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +# IDE +.idea/ +*.iml +*.ipr +*.iws +.project +.classpath +.settings/ +.vscode/ +*.swp diff --git a/java-example/README.md b/java-example/README.md new file mode 100644 index 0000000..696ee07 --- /dev/null +++ b/java-example/README.md @@ -0,0 +1,46 @@ +# Java Example (JUnit 5 + JaCoCo Coverage) + +This example demonstrates how to integrate Gaffer with JUnit 5 test reports and JaCoCo coverage reports. + +## Overview + +This project uses: +- **JUnit 5** (Jupiter) for testing +- **JaCoCo** for code coverage +- **Maven** as the build tool + +## Requirements + +- Java 17+ +- Maven 3.8+ + +## Local Development + +```bash +# Run tests with coverage +mvn clean test + +# View coverage report +open target/site/jacoco/index.html +``` + +## Test Reports + +After running tests, reports are generated in: +- `target/surefire-reports/*.xml` - JUnit XML test results +- `target/site/jacoco/jacoco.xml` - JaCoCo coverage XML +- `target/site/jacoco/index.html` - HTML coverage report + +## CI Integration + +The GitHub Actions workflow (`.github/workflows/java.yml`): +1. Runs Maven test with JaCoCo coverage +2. Generates JUnit test results and JaCoCo coverage +3. Uploads both reports to Gaffer + +## Coverage Formats + +JaCoCo generates multiple formats: +- **JaCoCo XML** - Parsed by Gaffer for coverage metrics +- **JaCoCo CSV** - Machine-readable summary +- **HTML** - Human-readable coverage browser diff --git a/java-example/pom.xml b/java-example/pom.xml new file mode 100644 index 0000000..7b39b23 --- /dev/null +++ b/java-example/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + com.example + gaffer-java-example + 1.0.0 + jar + + Gaffer Java Example + Gaffer JUnit 5 example with JaCoCo coverage reporting + + + 17 + 17 + UTF-8 + 5.10.2 + 0.8.12 + + + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.12.1 + + 17 + 17 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + ${project.build.directory}/surefire-reports + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + prepare-agent + + prepare-agent + + + + + report + test + + report + + + ${project.build.directory}/site/jacoco + + + + + + + diff --git a/java-example/src/main/java/com/example/Calculator.java b/java-example/src/main/java/com/example/Calculator.java new file mode 100644 index 0000000..404a35d --- /dev/null +++ b/java-example/src/main/java/com/example/Calculator.java @@ -0,0 +1,83 @@ +package com.example; + +/** + * Simple calculator class for demonstrating JUnit testing and JaCoCo coverage. + */ +public class Calculator { + + /** + * Add two numbers. + */ + public double add(double a, double b) { + return a + b; + } + + /** + * Subtract second number from first. + */ + public double subtract(double a, double b) { + return a - b; + } + + /** + * Multiply two numbers. + */ + public double multiply(double a, double b) { + return a * b; + } + + /** + * Divide first number by second. + * + * @throws IllegalArgumentException if divisor is zero + */ + public double divide(double a, double b) { + if (b == 0) { + throw new IllegalArgumentException("Cannot divide by zero"); + } + return a / b; + } + + /** + * Calculate the power of a number. + */ + public double power(double base, int exponent) { + return Math.pow(base, exponent); + } + + /** + * Calculate the factorial of a non-negative integer. + * + * @throws IllegalArgumentException if n is negative + */ + public long factorial(int n) { + if (n < 0) { + throw new IllegalArgumentException("Factorial is not defined for negative numbers"); + } + if (n == 0 || n == 1) { + return 1; + } + return n * factorial(n - 1); + } + + /** + * Check if a number is prime. + */ + public boolean isPrime(int n) { + if (n <= 1) { + return false; + } + if (n <= 3) { + return true; + } + if (n % 2 == 0 || n % 3 == 0) { + return false; + } + for (int i = 5; i * i <= n; i += 6) { + if (n % i == 0 || n % (i + 2) == 0) { + return false; + } + } + return true; + } +} diff --git a/java-example/src/test/java/com/example/CalculatorTest.java b/java-example/src/test/java/com/example/CalculatorTest.java new file mode 100644 index 0000000..ca789d3 --- /dev/null +++ b/java-example/src/test/java/com/example/CalculatorTest.java @@ -0,0 +1,236 @@ +package com.example; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for Calculator class. + */ +class CalculatorTest { + + private Calculator calculator; + + @BeforeEach + void setUp() { + calculator = new Calculator(); + } + + // ==================== Addition Tests ==================== + + @Nested + @DisplayName("Addition Tests") + class AdditionTests { + @Test + @DisplayName("should add positive numbers") + void testAddPositiveNumbers() { + assertEquals(5.0, calculator.add(2.0, 3.0)); + } + + @Test + @DisplayName("should add negative numbers") + void testAddNegativeNumbers() { + assertEquals(-5.0, calculator.add(-2.0, -3.0)); + } + + @Test + @DisplayName("should add with zero") + void testAddWithZero() { + assertEquals(5.0, calculator.add(5.0, 0.0)); + } + + @Test + @DisplayName("should add floats") + void testAddFloats() { + assertEquals(5.5, calculator.add(2.5, 3.0), 0.001); + } + } + + // ==================== Subtraction Tests ==================== + + @Nested + @DisplayName("Subtraction Tests") + class SubtractionTests { + @Test + @DisplayName("should subtract positive numbers") + void testSubtractPositiveNumbers() { + assertEquals(2.0, calculator.subtract(5.0, 3.0)); + } + + @Test + @DisplayName("should handle negative result") + void testSubtractResultingInNegative() { + assertEquals(-2.0, calculator.subtract(3.0, 5.0)); + } + + @Test + @DisplayName("should subtract zero") + void testSubtractWithZero() { + assertEquals(5.0, calculator.subtract(5.0, 0.0)); + } + } + + // ==================== Multiplication Tests ==================== + + @Nested + @DisplayName("Multiplication Tests") + class MultiplicationTests { + @Test + @DisplayName("should multiply positive numbers") + void testMultiplyPositiveNumbers() { + assertEquals(15.0, calculator.multiply(3.0, 5.0)); + } + + @Test + @DisplayName("should multiply by zero") + void testMultiplyWithZero() { + assertEquals(0.0, calculator.multiply(5.0, 0.0)); + } + + @Test + @DisplayName("should multiply negative numbers") + void testMultiplyNegativeNumbers() { + assertEquals(15.0, calculator.multiply(-3.0, -5.0)); + } + + @Test + @DisplayName("should multiply mixed signs") + void testMultiplyMixedSigns() { + assertEquals(-15.0, calculator.multiply(3.0, -5.0)); + } + } + + // ==================== Division Tests ==================== + + @Nested + @DisplayName("Division Tests") + class DivisionTests { + @Test + @DisplayName("should divide positive numbers") + void testDividePositiveNumbers() { + assertEquals(2.0, calculator.divide(10.0, 5.0)); + } + + @Test + @DisplayName("should return float result") + void testDivideResultingInFloat() { + assertEquals(2.5, calculator.divide(5.0, 2.0), 0.001); + } + + @Test + @DisplayName("should throw on divide by zero") + void testDivideByZeroThrowsException() { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> calculator.divide(10.0, 0.0) + ); + assertEquals("Cannot divide by zero", exception.getMessage()); + } + } + + // ==================== Power Tests ==================== + + @Nested + @DisplayName("Power Tests") + class PowerTests { + @Test + @DisplayName("should calculate positive exponent") + void testPowerPositiveExponent() { + assertEquals(8.0, calculator.power(2.0, 3)); + } + + @Test + @DisplayName("should handle zero exponent") + void testPowerZeroExponent() { + assertEquals(1.0, calculator.power(5.0, 0)); + } + + @Test + @DisplayName("should handle negative exponent") + void testPowerNegativeExponent() { + assertEquals(0.25, calculator.power(2.0, -2), 0.001); + } + } + + // ==================== Factorial Tests ==================== + + @Nested + @DisplayName("Factorial Tests") + class FactorialTests { + @Test + @DisplayName("factorial of 0 should be 1") + void testFactorialOfZero() { + assertEquals(1L, calculator.factorial(0)); + } + + @Test + @DisplayName("factorial of 1 should be 1") + void testFactorialOfOne() { + assertEquals(1L, calculator.factorial(1)); + } + + @Test + @DisplayName("factorial of 5 should be 120") + void testFactorialOfFive() { + assertEquals(120L, calculator.factorial(5)); + } + + @Test + @DisplayName("should throw on negative input") + void testFactorialOfNegativeThrowsException() { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> calculator.factorial(-1) + ); + assertEquals("Factorial is not defined for negative numbers", exception.getMessage()); + } + } + + // ==================== Prime Tests ==================== + + @Nested + @DisplayName("Prime Tests") + class PrimeTests { + @Test + @DisplayName("2 should be prime") + void testTwoIsPrime() { + assertTrue(calculator.isPrime(2)); + } + + @Test + @DisplayName("17 should be prime") + void testSeventeenIsPrime() { + assertTrue(calculator.isPrime(17)); + } + + @Test + @DisplayName("4 should not be prime") + void testFourIsNotPrime() { + assertFalse(calculator.isPrime(4)); + } + + @Test + @DisplayName("1 should not be prime") + void testOneIsNotPrime() { + assertFalse(calculator.isPrime(1)); + } + + @Test + @DisplayName("negative numbers should not be prime") + void testNegativeIsNotPrime() { + assertFalse(calculator.isPrime(-5)); + } + } + + // ==================== Intentional Failure (for demo) ==================== + + @Test + @DisplayName("Intentional failure for demo purposes") + void testIntentionalFailure() { + // This assertion is intentionally wrong to show failure reporting + assertEquals(42.0, calculator.add(1.0, 1.0), "This test is intentionally failing"); + } +} diff --git a/php-example/.gitignore b/php-example/.gitignore new file mode 100644 index 0000000..104842e --- /dev/null +++ b/php-example/.gitignore @@ -0,0 +1,5 @@ +/vendor/ +/reports/ +composer.lock +.phpunit.cache/ +.phpunit.result.cache diff --git a/php-example/README.md b/php-example/README.md new file mode 100644 index 0000000..ab7c153 --- /dev/null +++ b/php-example/README.md @@ -0,0 +1,50 @@ +# PHP Example (PHPUnit + Clover Coverage) + +This example demonstrates how to integrate Gaffer with PHPUnit test reports and Clover coverage reports. + +## Overview + +This project uses: +- **PHPUnit 10** for testing +- **Clover XML** format for coverage reporting +- **JUnit XML** format for test results + +## Requirements + +- PHP 8.1+ +- Composer +- Xdebug or PCOV for coverage (CI uses PCOV) + +## Local Development + +```bash +# Install dependencies +composer install + +# Run tests +composer test + +# Run tests with coverage (requires Xdebug or PCOV) +composer test:coverage +``` + +## Test Reports + +After running tests with coverage, reports are generated in: +- `reports/phpunit-results.xml` - JUnit-style test results +- `reports/clover.xml` - Clover coverage format +- `reports/htmlcov/` - HTML coverage report + +## CI Integration + +The GitHub Actions workflow (`.github/workflows/phpunit.yml`): +1. Runs PHPUnit tests with coverage enabled +2. Generates JUnit test results and Clover coverage +3. Uploads both reports to Gaffer + +## Coverage Formats + +PHPUnit can generate multiple coverage formats: +- **Clover XML** - Used by many CI tools, parsed by Gaffer +- **Cobertura** - Alternative XML format (use `--coverage-cobertura`) +- **HTML** - Human-readable coverage browser diff --git a/php-example/composer.json b/php-example/composer.json new file mode 100644 index 0000000..ab35d5c --- /dev/null +++ b/php-example/composer.json @@ -0,0 +1,26 @@ +{ + "name": "gaffer/php-example", + "description": "Gaffer PHPUnit example with Clover coverage reporting", + "type": "project", + "license": "MIT", + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "autoload": { + "psr-4": { + "GafferExample\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "GafferExample\\Tests\\": "tests/" + } + }, + "scripts": { + "test": "phpunit", + "test:coverage": "phpunit --coverage-clover reports/clover.xml --coverage-html reports/htmlcov" + } +} diff --git a/php-example/phpunit.xml b/php-example/phpunit.xml new file mode 100644 index 0000000..4f74028 --- /dev/null +++ b/php-example/phpunit.xml @@ -0,0 +1,28 @@ + + + + + + tests + + + + + + src + + + + + + + diff --git a/php-example/src/Calculator.php b/php-example/src/Calculator.php new file mode 100644 index 0000000..5f9911f --- /dev/null +++ b/php-example/src/Calculator.php @@ -0,0 +1,72 @@ +factorial($n - 1); + } +} diff --git a/php-example/tests/CalculatorTest.php b/php-example/tests/CalculatorTest.php new file mode 100644 index 0000000..1c9ee79 --- /dev/null +++ b/php-example/tests/CalculatorTest.php @@ -0,0 +1,154 @@ +calculator = new Calculator(); + } + + // ==================== Addition Tests ==================== + + public function testAddPositiveNumbers(): void + { + $this->assertEquals(5, $this->calculator->add(2, 3)); + } + + public function testAddNegativeNumbers(): void + { + $this->assertEquals(-5, $this->calculator->add(-2, -3)); + } + + public function testAddWithZero(): void + { + $this->assertEquals(5, $this->calculator->add(5, 0)); + } + + public function testAddFloats(): void + { + $this->assertEqualsWithDelta(5.5, $this->calculator->add(2.5, 3.0), 0.001); + } + + // ==================== Subtraction Tests ==================== + + public function testSubtractPositiveNumbers(): void + { + $this->assertEquals(2, $this->calculator->subtract(5, 3)); + } + + public function testSubtractResultingInNegative(): void + { + $this->assertEquals(-2, $this->calculator->subtract(3, 5)); + } + + public function testSubtractWithZero(): void + { + $this->assertEquals(5, $this->calculator->subtract(5, 0)); + } + + // ==================== Multiplication Tests ==================== + + public function testMultiplyPositiveNumbers(): void + { + $this->assertEquals(15, $this->calculator->multiply(3, 5)); + } + + public function testMultiplyWithZero(): void + { + $this->assertEquals(0, $this->calculator->multiply(5, 0)); + } + + public function testMultiplyNegativeNumbers(): void + { + $this->assertEquals(15, $this->calculator->multiply(-3, -5)); + } + + public function testMultiplyMixedSigns(): void + { + $this->assertEquals(-15, $this->calculator->multiply(3, -5)); + } + + // ==================== Division Tests ==================== + + public function testDividePositiveNumbers(): void + { + $this->assertEquals(2, $this->calculator->divide(10, 5)); + } + + public function testDivideResultingInFloat(): void + { + $this->assertEqualsWithDelta(2.5, $this->calculator->divide(5, 2), 0.001); + } + + public function testDivideByZeroThrowsException(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot divide by zero'); + $this->calculator->divide(10, 0); + } + + // ==================== Power Tests ==================== + + public function testPowerPositiveExponent(): void + { + $this->assertEquals(8, $this->calculator->power(2, 3)); + } + + public function testPowerZeroExponent(): void + { + $this->assertEquals(1, $this->calculator->power(5, 0)); + } + + public function testPowerNegativeExponent(): void + { + $this->assertEqualsWithDelta(0.25, $this->calculator->power(2, -2), 0.001); + } + + // ==================== Factorial Tests ==================== + + public function testFactorialOfZero(): void + { + $this->assertEquals(1, $this->calculator->factorial(0)); + } + + public function testFactorialOfOne(): void + { + $this->assertEquals(1, $this->calculator->factorial(1)); + } + + public function testFactorialOfFive(): void + { + $this->assertEquals(120, $this->calculator->factorial(5)); + } + + public function testFactorialOfNegativeThrowsException(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Factorial is not defined for negative numbers'); + $this->calculator->factorial(-1); + } + + // ==================== Intentional Failure (for demo) ==================== + + /** + * This test intentionally fails to demonstrate test failure reporting. + * Comment out or fix in production. + */ + public function testIntentionalFailure(): void + { + // This assertion is intentionally wrong to show failure reporting + $this->assertEquals(42, $this->calculator->add(1, 1), 'This test is intentionally failing'); + } +} diff --git a/pytest-example/requirements.txt b/pytest-example/requirements.txt index 6404995..5d75aba 100644 --- a/pytest-example/requirements.txt +++ b/pytest-example/requirements.txt @@ -1,2 +1,3 @@ pytest>=8.0.0 pytest-html>=4.1.1 +pytest-cov>=4.1.0 From 6ab4ab62d1255980aa85bfea1a79ca21b6f243cb Mon Sep 17 00:00:00 2001 From: Alex Gandy Date: Wed, 21 Jan 2026 21:39:43 -0500 Subject: [PATCH 2/4] Fix JaCoCo report generation by using verify phase --- .github/workflows/java.yml | 2 +- java-example/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 689f090..290c513 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -27,7 +27,7 @@ jobs: cache: "maven" - name: Run tests with coverage - run: mvn clean test + run: mvn clean verify continue-on-error: true # Upload JUnit test results to Gaffer diff --git a/java-example/pom.xml b/java-example/pom.xml index 7b39b23..e7c3115 100644 --- a/java-example/pom.xml +++ b/java-example/pom.xml @@ -70,7 +70,7 @@ report - test + verify report From 2005a397adf341066f8d008c4e6f54be3ad3f303 Mon Sep 17 00:00:00 2001 From: Alex Gandy Date: Wed, 21 Jan 2026 21:43:31 -0500 Subject: [PATCH 3/4] Fix Maven to continue past test failures --- .github/workflows/java.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 290c513..805ea69 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -27,8 +27,7 @@ jobs: cache: "maven" - name: Run tests with coverage - run: mvn clean verify - continue-on-error: true + run: mvn clean verify -Dmaven.test.failure.ignore=true # Upload JUnit test results to Gaffer # Skip on Dependabot PRs (no access to secrets) From 31dbc14942bf23fb4338a001a61079eeab3d96a1 Mon Sep 17 00:00:00 2001 From: Alex Gandy Date: Thu, 22 Jan 2026 08:16:20 -0500 Subject: [PATCH 4/4] README change for integration tests --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db4ddbe..b30a63b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Example test projects demonstrating [Gaffer](https://gaffer.sh) integration for various test frameworks. -Parser Check: December 25 2025 - 1:10PM - Merry Christmas +Parser Check: January 22 2026 - 8:16am ## Examples