Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 79 additions & 15 deletions launchable/test_runners/maven.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import glob
import os
import re
import xml.etree.ElementTree as ET
from typing import Dict, List, Optional, Tuple

import click
Expand Down Expand Up @@ -46,6 +47,33 @@ def is_file(f: str) -> bool:
return False


def parse_surefire_reports() -> List[Dict[str, str]]:
"""Parse Maven Surefire XML reports from target/surefire-reports/."""
test_paths = []
report_pattern = '**/target/surefire-reports/TEST-*.xml'
report_files = glob.glob(report_pattern, recursive=True)

if not report_files:
return []

click.echo(f"Found {len(report_files)} surefire report(s)", err=True)

for report_file in report_files:
try:
tree = ET.parse(report_file)
root = tree.getroot()
classname = root.get('name')

if classname:
test_paths.append({"type": "class", "name": classname})

except ET.ParseError as e:
click.secho(f"Warning: Could not parse {report_file}: {e}", fg='yellow', err=True)

click.echo(f"Total tests discovered: {len(test_paths)}", err=True)
return test_paths


@click.option(
'--test-compile-created-file',
'test_compile_created_file',
Expand All @@ -61,6 +89,12 @@ def is_file(f: str) -> bool:
is_flag=True,
help="Scan testCompile/default-testCompile/createdFiles.lst for *.lst files generated by `mvn compile` and use them as test inputs.", # noqa: E501
)
@click.option(
'--scan-dryrun-results',
'is_scan_dryrun_results',
is_flag=True,
help="Scan surefire reports generated by mvn test -DdryRun=true",
)
@click.option(
'--exclude',
'exclude_rules',
Expand All @@ -70,7 +104,14 @@ def is_file(f: str) -> bool:
)
@click.argument('source_roots', required=False, nargs=-1)
@launchable.subset
def subset(client, source_roots, test_compile_created_file, is_scan_test_compile_lst, exclude_rules: Tuple[str, ...]):
def subset(
client,
source_roots,
test_compile_created_file,
is_scan_test_compile_lst,
is_scan_dryrun_results,
exclude_rules: Tuple[str, ...]
):

# Compile exclude rules
compiled_exclude_rules = []
Expand Down Expand Up @@ -100,23 +141,45 @@ def file2test(f: str) -> Optional[List]:
else:
return None

files_to_read = list(test_compile_created_file)
if is_scan_test_compile_lst:
if len(test_compile_created_file) > 0:
click.echo(click.style(
"Warning: --test-compile-created-file is overridden by --scan-test-compile-lst", fg="yellow"),
err=True)
if is_scan_dryrun_results:
click.echo("Scanning Maven dry-run results...", err=True)
tests = parse_surefire_reports()

if not tests:
click.secho(
"Warning: No surefire reports found. Did you run 'mvn test -DdryRun=true'?",
fg='yellow',
err=True
)
click.secho(
"Please run: mvn test -DdryRun=true",
fg='cyan',
err=True
)
return

# Add tests to client (each test path must be a list)
for test in tests:
client.test_paths.append([test])

pattern = os.path.join('**', 'createdFiles.lst')
files_to_read = glob.glob(pattern, recursive=True)
elif is_scan_test_compile_lst or test_compile_created_file:
if is_scan_test_compile_lst:
if len(test_compile_created_file) > 0:
click.echo(click.style(
"Warning: --test-compile-created-file is overridden by --scan-test-compile-lst", fg="yellow"),
err=True)

if not files_to_read:
click.echo(click.style(
"Warning: No .lst files. Please run after executing `mvn test-compile`", fg="yellow"),
err=True)
return
pattern = os.path.join('**', 'createdFiles.lst')
files_to_read = glob.glob(pattern, recursive=True)

if not files_to_read:
click.echo(click.style(
"Warning: No .lst files. Please run after executing `mvn test-compile`", fg="yellow"),
err=True)
return
elif test_compile_created_file:
files_to_read = list(test_compile_created_file)

if files_to_read:
for file in files_to_read:
with open(file, 'r') as f:
lines = f.readlines()
Expand All @@ -132,6 +195,7 @@ def file2test(f: str) -> Optional[List]:
path = file2test(l)
if path:
client.test_paths.append(path)

else:
for root in source_roots:
client.scan(root, '**/*', file2test)
Expand Down
69 changes: 69 additions & 0 deletions tests/data/maven/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Maven Test Data

This directory contains test fixtures for the Maven test runner, including sample Maven Surefire reports used to test the `--scan-dryrun-results` feature.

## Directory Structure

- `dryrun-test/` - Test data for the `--scan-dryrun-results` feature
- `target/surefire-reports/` - Sample Maven Surefire reports (XML and TXT formats)
- `reports/` - Sample test result XML files for various test scenarios

## How to Test Manually with Your Own Project

To manually test the `--scan-dryrun-results` feature with your own Maven project:

```bash
# 1. Navigate to your Maven project
cd /path/to/your/maven-project

# 2. Run Maven dry-run to generate reports
mvn test -DdryRun=true

# 3. Verify reports were created
ls -la target/surefire-reports/TEST-*.xml

# 4. Set up environment (if not already set)
export LAUNCHABLE_TOKEN='v1:your-org/your-workspace:your-actual-token'

# 5. Record build
launchable record build --name 'test-build-name'

# 6. Create a session
launchable record session --build 'test-build-name'
# Copy the session ID from output, e.g., builds/test-build-name/test_sessions/123

# 7. Run subset with --scan-dryrun-results
launchable subset maven \
--scan-dryrun-results \
--session builds/test-build-name/test_sessions/123 \
--target 10%
```

## Creating Test Files for `--scan-dryrun-results`

The test files in `dryrun-test/target/surefire-reports/` are fixtures that simulate Maven Surefire reports generated by running tests with Maven's dry-run mode.

### How to Create/Update These Files

To create or update test fixture files, run Maven tests from within the `dryrun-test` directory:

```bash
cd tests/data/maven/dryrun-test

# Run Maven dry-run to generate Surefire reports
mvn test -DdryRun=true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is it correct? pom.xml file and other files don't exist. And I've faced this error

$ mvn test -DdryRun=true
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.023 s
[INFO] Finished at: 2026-04-16T17:30:14+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] The goal you specified requires a project to execute but there is no POM in this directory (/Users/ryabuki/src/github.com/cloudbees-oss/smart-tests-cli-wt/review-1278/tests/data/maven/dryrun-test). Please verify you invoked Maven from the correct directory. -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MissingProjectException

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes without pom.xml . will get this error. This is fine right.


# The reports will be automatically generated in target/surefire-reports/
# - XML files: TEST-*.xml (detailed test execution results)
# - TXT files: *.txt (summary information for each test class)
```

The generated reports in `target/surefire-reports/` are used directly as test fixtures.

### Purpose

These fixtures are used to test:
- Parsing of Maven Surefire XML reports
- The `--scan-dryrun-results` flag functionality
- Integration with Launchable's subset API
- Handling of filtered/excluded tests
Loading
Loading