Skip to content
Merged
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
2 changes: 2 additions & 0 deletions launchable/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from launchable.app import Application

from .commands.compare import compare
from .commands.inspect import inspect
from .commands.record import record
from .commands.split_subset import split_subset
Expand Down Expand Up @@ -89,6 +90,7 @@ def main(ctx, log_level, plugin_dir, dry_run, skip_cert_verification):
main.add_command(verify)
main.add_command(inspect)
main.add_command(stats)
main.add_command(compare)

if __name__ == '__main__':
main()
13 changes: 13 additions & 0 deletions launchable/commands/compare/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import click

from launchable.utils.click import GroupWithAlias

from .subsets import subsets


@click.group(cls=GroupWithAlias)
def compare():
pass


compare.add_command(subsets)
50 changes: 50 additions & 0 deletions launchable/commands/compare/subsets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from typing import List, Tuple, Union

import click
from tabulate import tabulate


@click.command()
@click.argument('file_before', type=click.Path(exists=True))
@click.argument('file_after', type=click.Path(exists=True))
def subsets(file_before, file_after):
"""
Compare two subset files and display changes in test order positions
"""

# Read files and map test paths to their indices
with open(file_before, 'r') as f:
before_tests = f.read().splitlines()
before_index_map = {test: idx for idx, test in enumerate(before_tests)}

with open(file_after, 'r') as f:
after_tests = f.read().splitlines()
after_index_map = {test: idx for idx, test in enumerate(after_tests)}

# List of tuples representing test order changes (before, after, diff, test)
rows: List[Tuple[Union[int, str], Union[int, str], Union[int, str], str]] = []

# Calculate order difference and add each test in file_after to changes
for after_idx, test in enumerate(after_tests):
if test in before_index_map:
before_idx = before_index_map[test]
diff = after_idx - before_idx
rows.append((before_idx + 1, after_idx + 1, diff, test))
else:
rows.append(('-', after_idx + 1, 'NEW', test))

# Add all deleted tests to changes
for before_idx, test in enumerate(before_tests):
if test not in after_index_map:
rows.append((before_idx + 1, '-', 'DELETED', test))

# Sort changes by the order diff
rows.sort(key=lambda x: (0 if isinstance(x[2], str) else 1, x[2]))

# Display results in a tabular format
headers = ["Before", "After", "After - Before", "Test"]
tabular_data = [
(before, after, f"{diff:+}" if isinstance(diff, int) else diff, test)
for before, after, diff, test in rows
]
click.echo(tabulate(tabular_data, headers=headers, tablefmt="github"))
File renamed without changes.
155 changes: 155 additions & 0 deletions tests/commands/compare/test_subsets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import os
from unittest import mock

from tests.cli_test_case import CliTestCase


class SubsetsTest(CliTestCase):

@mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
def test_subsets(self):
# Create subset-before.txt
with open("subset-before.txt", "w") as f:
f.write("\n".join([
"src/test/java/example/DivTest.java",
"src/test/java/example/DB1Test.java",
"src/test/java/example/MulTest.java",
"src/test/java/example/Add2Test.java",
"src/test/java/example/File1Test.java",
"src/test/java/example/File0Test.java",
"src/test/java/example/SubTest.java",
"src/test/java/example/DB0Test.java",
"src/test/java/example/AddTest.java",
]))

# Create subset-after.txt
with open("subset-after.txt", "w") as f:
f.write("\n".join([
"src/test/java/example/Add2Test.java",
"src/test/java/example/MulTest.java",
"src/test/java/example/AddTest.java",
"src/test/java/example/File1Test.java",
"src/test/java/example/DivTest.java",
"src/test/java/example/File0Test.java",
"src/test/java/example/DB1Test.java",
"src/test/java/example/DB0Test.java",
"src/test/java/example/SubTest.java",
]))

result = self.cli('compare', 'subsets', "subset-before.txt", "subset-after.txt", mix_stderr=False)
expect = """| Before | After | After - Before | Test |
|----------|---------|------------------|--------------------------------------|
| 9 | 3 | -6 | src/test/java/example/AddTest.java |
| 4 | 1 | -3 | src/test/java/example/Add2Test.java |
| 3 | 2 | -1 | src/test/java/example/MulTest.java |
| 5 | 4 | -1 | src/test/java/example/File1Test.java |
| 6 | 6 | +0 | src/test/java/example/File0Test.java |
| 8 | 8 | +0 | src/test/java/example/DB0Test.java |
| 7 | 9 | +2 | src/test/java/example/SubTest.java |
| 1 | 5 | +4 | src/test/java/example/DivTest.java |
| 2 | 7 | +5 | src/test/java/example/DB1Test.java |
"""

self.assertEqual(result.stdout, expect)

@mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
def test_subsets_when_new_tests(self):
# Create subset-before.txt
with open("subset-before.txt", "w") as f:
f.write("\n".join([
"src/test/java/example/SubTest.java",
"src/test/java/example/DivTest.java",
"src/test/java/example/Add2Test.java",
"src/test/java/example/File0Test.java",
"src/test/java/example/AddTest.java",
"src/test/java/example/File1Test.java",
"src/test/java/example/MulTest.java",
"src/test/java/example/DB0Test.java",
"src/test/java/example/DB1Test.java"
]))

# Create subset-after.txt (which includes additional test path NewTest.java)
with open("subset-after.txt", "w") as f:
f.write("\n".join([
"src/test/java/example/NewTest.java",
"src/test/java/example/SubTest.java",
"src/test/java/example/File0Test.java",
"src/test/java/example/DB1Test.java",
"src/test/java/example/DivTest.java",
"src/test/java/example/MulTest.java",
"src/test/java/example/File1Test.java",
"src/test/java/example/DB0Test.java",
"src/test/java/example/Add2Test.java",
"src/test/java/example/AddTest.java"
]))

result = self.cli('compare', 'subsets', "subset-before.txt", "subset-after.txt", mix_stderr=False)
expect = """| Before | After | After - Before | Test |
|----------|---------|------------------|--------------------------------------|
| - | 1 | NEW | src/test/java/example/NewTest.java |
| 9 | 4 | -5 | src/test/java/example/DB1Test.java |
| 4 | 3 | -1 | src/test/java/example/File0Test.java |
| 7 | 6 | -1 | src/test/java/example/MulTest.java |
| 8 | 8 | +0 | src/test/java/example/DB0Test.java |
| 1 | 2 | +1 | src/test/java/example/SubTest.java |
| 6 | 7 | +1 | src/test/java/example/File1Test.java |
| 2 | 5 | +3 | src/test/java/example/DivTest.java |
| 5 | 10 | +5 | src/test/java/example/AddTest.java |
| 3 | 9 | +6 | src/test/java/example/Add2Test.java |
"""

self.assertEqual(result.stdout, expect)

@mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
def test_subsets_when_deleted_tests(self):
# Create subset-before.txt
with open("subset-before.txt", "w") as f:
f.write("\n".join([
"src/test/java/example/NewTest.java",
"src/test/java/example/SubTest.java",
"src/test/java/example/File0Test.java",
"src/test/java/example/DB1Test.java",
"src/test/java/example/DivTest.java",
"src/test/java/example/MulTest.java",
"src/test/java/example/File1Test.java",
"src/test/java/example/DB0Test.java",
"src/test/java/example/Add2Test.java",
"src/test/java/example/AddTest.java"
]))

# Create subset-after.txt (which doesn't include NewTest.java)
with open("subset-after.txt", "w") as f:
f.write("\n".join([
"src/test/java/example/DB1Test.java",
"src/test/java/example/DB0Test.java",
"src/test/java/example/File1Test.java",
"src/test/java/example/SubTest.java",
"src/test/java/example/AddTest.java",
"src/test/java/example/MulTest.java",
"src/test/java/example/File0Test.java",
"src/test/java/example/Add2Test.java",
"src/test/java/example/DivTest.java"
]))

result = self.cli('compare', 'subsets', "subset-before.txt", "subset-after.txt", mix_stderr=False)
expect = """| Before | After | After - Before | Test |
|----------|---------|------------------|--------------------------------------|
| 1 | - | DELETED | src/test/java/example/NewTest.java |
| 8 | 2 | -6 | src/test/java/example/DB0Test.java |
| 10 | 5 | -5 | src/test/java/example/AddTest.java |
| 7 | 3 | -4 | src/test/java/example/File1Test.java |
| 4 | 1 | -3 | src/test/java/example/DB1Test.java |
| 9 | 8 | -1 | src/test/java/example/Add2Test.java |
| 6 | 6 | +0 | src/test/java/example/MulTest.java |
| 2 | 4 | +2 | src/test/java/example/SubTest.java |
| 3 | 7 | +4 | src/test/java/example/File0Test.java |
| 5 | 9 | +4 | src/test/java/example/DivTest.java |
"""

self.assertEqual(result.stdout, expect)

def tearDown(self):
if os.path.exists("subset-before.txt"):
os.remove("subset-before.txt")
if os.path.exists("subset-after.txt"):
os.remove("subset-after.txt")
Loading