Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e4de6f4
[tagpr] prepare for the next release
github-actions[bot] Sep 17, 2025
839f5eb
fix: an error message in a test
ninjinkun Jun 24, 2025
220d360
fix: no color on tests
ninjinkun Jun 24, 2025
7c9014b
fix: test
ninjinkun Jun 25, 2025
b917b58
test: revive test_click.py as test_typer.py
ninjinkun Jun 27, 2025
7028a92
Groovy lang works on JVM but we haven't supported it as jvm_test_pattern
Konboi Jun 19, 2025
b5baced
support groovy file in the maven profile
Konboi Jun 19, 2025
53c5ebb
install latest version
Konboi Jun 23, 2025
e266046
from junitparser v4.0.0 returns JUnitXml instead of testsuite
Konboi Jun 23, 2025
4400e9d
[LCHIB-612] Add a workaround for handling timezone abbreviations in d…
ono-max Jun 13, 2025
92f81d3
Use assertEqual instead of assertTrue and assertIn
ono-max Jun 25, 2025
9164968
Add comment
ono-max Jun 25, 2025
55de230
test: revive test_click.py as test_typer.py
ninjinkun Jun 27, 2025
47c19ce
feature: replace LAUNCHABLE_ env in Java code
ninjinkun Jul 4, 2025
59cd141
test: fix tests
ninjinkun Jul 4, 2025
05f898a
Enhance PytestJSONReportParser to handle user properties as JSON
ono-max Jul 25, 2025
5ea15f1
Follow up fix to f85f624d3816716a220bcd4123edad30ce88babc
kohsuke Aug 5, 2025
e9506e9
Merge pull request #1044 from launchableinc/renovate/actions-attest-b…
kohsuke Aug 5, 2025
3e652d5
refactor: remove intermidiate documents
ninjinkun Aug 19, 2025
d639ea6
fix: import paths
ninjinkun Aug 20, 2025
cdf48cc
test: add test_typer_types.py
ninjinkun Aug 20, 2025
c8d6b7c
fix: test
ninjinkun Aug 20, 2025
34c4831
fix: convert value
ninjinkun Aug 20, 2025
549be0a
fix: a test file
ninjinkun Aug 21, 2025
f547bac
fix: a test file
ninjinkun Aug 21, 2025
df81318
fix: tracking event count for reducing traking event in SmartTests CLI
ninjinkun Aug 21, 2025
3ee791d
add util method to print error message and exit as 1
Konboi Sep 4, 2025
a8a5f00
[chore] report an error in subset call
kohsuke Sep 5, 2025
e32bfba
Add tests
ono-max Sep 10, 2025
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
6 changes: 3 additions & 3 deletions launchable/commands/subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,17 +491,17 @@ def get_payload(
if target is not None:
payload["goal"] = {
"type": "subset-by-percentage",
"percentage": target,
"percentage": float(target),
}
elif duration is not None:
payload["goal"] = {
"type": "subset-by-absolute-time",
"duration": duration,
"duration": float(duration),
}
elif confidence is not None:
payload["goal"] = {
"type": "subset-by-confidence",
"percentage": confidence
"percentage": float(confidence)
}
elif goal_spec is not None:
payload["goal"] = {
Expand Down
12 changes: 12 additions & 0 deletions launchable/utils/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# TODO: add cli-specific custom exceptions
import sys

import typer

from smart_tests.utils.tracking import Tracking, TrackingClient


class ParseSessionException(Exception):
def __init__(
Expand All @@ -20,3 +26,9 @@ def __init__(
self.filename = filename
self.message = "{message}: {filename}".format(message=message, filename=self.filename)
super().__init__(self.message)


def print_error_and_die(msg: str, tracking_client: TrackingClient, event: Tracking.ErrorEvent):
typer.secho(msg, fg=typer.colors.RED, err=True)
tracking_client.send_error_event(event_name=event, stack_trace=msg)
sys.exit(1)
8 changes: 4 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[metadata]
name = launchable
name = smart-tests
author = Launchable, Inc.
author_email = info@launchableinc.com
license = Apache Software License v2
description = Launchable CLI
description = Smart Tests CLI
url = https://launchableinc.com/
long_description = file: README.md
long_description_content_type = text/markdown
Expand All @@ -30,7 +30,7 @@ setup_requires =
setuptools-scm

[options.entry_points]
console_scripts = launchable = launchable.__main__:main
console_scripts = smart-tests = smart_tests.__main__:main

[options.package_data]
launchable = jar/exe_deploy.jar
smart_tests = jar/exe_deploy.jar
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,18 @@ public void setNoCommitMessage(boolean b) {
private void parseConfiguration() throws CmdLineException {
String apiToken = launchableToken;
if (launchableToken == null) {
apiToken = System.getenv("LAUNCHABLE_TOKEN");
apiToken = System.getenv("SMART_TESTS_TOKEN");
}
if (apiToken == null || apiToken.isEmpty()) {
if (System.getenv("GITHUB_ACTIONS") != null) {
String o = System.getenv("LAUNCHABLE_ORGANIZATION");
String o = System.getenv("SMART_TESTS_ORGANIZATION");
if (org == null && o == null) {
throw new CmdLineException("LAUNCHABLE_ORGANIZATION env variable is not set");
throw new CmdLineException("SMART_TESTS_ORGANIZATION env variable is not set");
}

String w = System.getenv("LAUNCHABLE_WORKSPACE");
String w = System.getenv("SMART_TESTS_WORKSPACE");
if (ws == null && w == null) {
throw new CmdLineException("LAUNCHABLE_WORKSPACE env variable is not set");
throw new CmdLineException("SMART_TESTS_WORKSPACE env variable is not set");
}

if (org == null) {
Expand All @@ -118,7 +118,7 @@ private void parseConfiguration() throws CmdLineException {
return;
}

throw new CmdLineException("LAUNCHABLE_TOKEN env variable is not set");
throw new CmdLineException("SMART_TESTS_TOKEN env variable is not set");
}

this.parseLaunchableToken(apiToken);
Expand Down Expand Up @@ -163,11 +163,11 @@ private void parseLaunchableToken(String token) throws CmdLineException {
if (token.startsWith("v1:")) {
String[] v = token.split(":");
if (v.length != 3) {
throw new IllegalStateException("Malformed LAUNCHABLE_TOKEN");
throw new IllegalStateException("Malformed SMART_TESTS_TOKEN");
}
v = v[1].split("/");
if (v.length != 2) {
throw new IllegalStateException("Malformed LAUNCHABLE_TOKEN");
throw new IllegalStateException("Malformed SMART_TESTS_TOKEN");
}

// for backward compatibility, allow command line options to take precedence
Expand Down
12 changes: 11 additions & 1 deletion tests/cli_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,17 @@ def cli(self, *args, **kwargs) -> click.testing.Result:
mix_stderr = kwargs['mix_stderr']
del kwargs['mix_stderr']

return CliRunner(mix_stderr=mix_stderr).invoke(main, args, catch_exceptions=False, **kwargs)
# Disable rich colors for testing by setting the environment variable
import os
old_no_color = os.environ.get('NO_COLOR')
os.environ['NO_COLOR'] = '1'
try:
return CliRunner(mix_stderr=mix_stderr).invoke(main, args, catch_exceptions=False, **kwargs)
finally:
if old_no_color is None:
os.environ.pop('NO_COLOR', None)
else:
os.environ['NO_COLOR'] = old_no_color

def assert_success(self, result: click.testing.Result):
self.assert_exit_code(result, 0)
Expand Down
22 changes: 12 additions & 10 deletions tests/commands/record/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,24 +191,26 @@ def test_commit_option_and_build_option(self):
}, payload)
responses.calls.reset()

# case --commit option and --branch option but another one
# case --commit option and --repo-branch-map option with invalid repo
result = self.cli(
"record",
"build",
"--build",
self.build_name,
"--no-commit-collection",
"--commit",
"A=abc12",
"--branch",
"B=feature-yyy",
"--name",
self.build_name)
"main",
"--repo-branch-map",
"B=feature-yyy")
self.assert_success(result)

payload = json.loads(responses.calls[1].request.body.decode())
self.assert_json_orderless_equal(
{
"buildNumber": "123",
"lineage": None,
"lineage": "main",
"commitHashes": [
{
"repositoryName": "A",
Expand All @@ -226,17 +228,17 @@ def test_commit_option_and_build_option(self):
result = self.cli(
"record",
"build",
"--build",
self.build_name,
"--no-commit-collection",
"--commit",
"A=abc12",
"--branch",
"--repo-branch-map",
"B=feature-yyy",
"--commit",
"B=56cde",
"--branch",
"A=feature-xxx",
"--name",
self.build_name)
"--repo-branch-map",
"A=feature-xxx")
self.assert_success(result)

payload = json.loads(responses.calls[1].request.body.decode())
Expand Down
91 changes: 86 additions & 5 deletions tests/commands/record/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,18 @@ class SessionTest(CliTestCase):
'LANG': 'C.UTF-8',
}, clear=True)
def test_run_session_without_flavor(self):
result = self.cli("record", "session", "--build", self.build_name)
# Mock session name check
responses.add(
responses.GET,
"{}/intake/organizations/{}/workspaces/{}/builds/{}/test_sessions/{}".format(
get_base_url(),
self.organization,
self.workspace,
self.build_name,
self.session_name),
status=404)

result = self.cli("record", "session", "--build", self.build_name, "--session", self.session_name)
self.assert_success(result)

payload = json.loads(responses.calls[1].request.body.decode())
Expand All @@ -44,7 +55,19 @@ def test_run_session_without_flavor(self):
'LANG': 'C.UTF-8',
}, clear=True)
def test_run_session_with_flavor(self):
# Mock session name check
responses.add(
responses.GET,
"{}/intake/organizations/{}/workspaces/{}/builds/{}/test_sessions/{}".format(
get_base_url(),
self.organization,
self.workspace,
self.build_name,
self.session_name),
status=404)

result = self.cli("record", "session", "--build", self.build_name,
"--session", self.session_name,
"--flavor", "key=value", "--flavor", "k:v", "--flavor", "k e y = v a l u e")
self.assert_success(result)

Expand All @@ -63,17 +86,39 @@ def test_run_session_with_flavor(self):
"timestamp": None,
}, payload)

result = self.cli("record", "session", "--build", self.build_name, "--flavor", "only-key")
# Mock session name check for second call
responses.add(
responses.GET,
"{}/intake/organizations/{}/workspaces/{}/builds/{}/test_sessions/{}".format(
get_base_url(),
self.organization,
self.workspace,
self.build_name,
self.session_name),
status=404)

result = self.cli("record", "session", "--build", self.build_name, "--session", self.session_name, "--flavor", "only-key")
self.assert_exit_code(result, 2)
self.assertIn("Expected a key-value pair formatted as --option key=value", result.output)
self.assertIn("but got 'only-key'", result.output)

@responses.activate
@mock.patch.dict(os.environ, {
"LAUNCHABLE_TOKEN": CliTestCase.launchable_token,
'LANG': 'C.UTF-8',
}, clear=True)
def test_run_session_with_observation(self):
result = self.cli("record", "session", "--build", self.build_name, "--observation")
# Mock session name check
responses.add(
responses.GET,
"{}/intake/organizations/{}/workspaces/{}/builds/{}/test_sessions/{}".format(
get_base_url(),
self.organization,
self.workspace,
self.build_name,
self.session_name),
status=404)

result = self.cli("record", "session", "--build", self.build_name, "--session", self.session_name, "--observation")
self.assert_success(result)

payload = json.loads(responses.calls[1].request.body.decode())
Expand Down Expand Up @@ -116,7 +161,7 @@ def test_run_session_with_session_name(self):
result = self.cli("record", "session", "--build", self.build_name, "--session-name", self.session_name)
self.assert_success(result)

payload = json.loads(responses.calls[5].request.body.decode())
payload = json.loads(responses.calls[3].request.body.decode())
self.assert_json_orderless_equal({
"flavors": {},
"isObservation": False,
Expand All @@ -133,7 +178,19 @@ def test_run_session_with_session_name(self):
'LANG': 'C.UTF-8',
}, clear=True)
def test_run_session_with_lineage(self):
# Mock session name check
responses.add(
responses.GET,
"{}/intake/organizations/{}/workspaces/{}/builds/{}/test_sessions/{}".format(
get_base_url(),
self.organization,
self.workspace,
self.build_name,
self.session_name),
status=404)

result = self.cli("record", "session", "--build", self.build_name,
"--session", self.session_name,
"--lineage", "example-lineage")
self.assert_success(result)

Expand All @@ -154,7 +211,19 @@ def test_run_session_with_lineage(self):
'LANG': 'C.UTF-8',
}, clear=True)
def test_run_session_with_test_suite(self):
# Mock session name check
responses.add(
responses.GET,
"{}/intake/organizations/{}/workspaces/{}/builds/{}/test_sessions/{}".format(
get_base_url(),
self.organization,
self.workspace,
self.build_name,
self.session_name),
status=404)

result = self.cli("record", "session", "--build", self.build_name,
"--session", self.session_name,
"--test-suite", "example-test-suite")
self.assert_success(result)

Expand All @@ -175,7 +244,19 @@ def test_run_session_with_test_suite(self):
'LANG': 'C.UTF-8',
}, clear=True)
def test_run_session_with_timestamp(self):
# Mock session name check
responses.add(
responses.GET,
"{}/intake/organizations/{}/workspaces/{}/builds/{}/test_sessions/{}".format(
get_base_url(),
self.organization,
self.workspace,
self.build_name,
self.session_name),
status=404)

result = self.cli("record", "session", "--build", self.build_name,
"--session", self.session_name,
"--timestamp", "2023-10-01T12:00:00Z")
self.assert_success(result)

Expand Down
Loading