Skip to content

Commit c3144f7

Browse files
committed
test: add an integration test and relevant compare script
Signed-off-by: behnazh-w <behnaz.hassanshahi@oracle.com>
1 parent 21c3bbf commit c3144f7

File tree

9 files changed

+369
-16
lines changed

9 files changed

+369
-16
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
macaron.build\_spec\_generator.common\_spec package
2+
===================================================
3+
4+
.. automodule:: macaron.build_spec_generator.common_spec
5+
:members:
6+
:show-inheritance:
7+
:undoc-members:
8+
9+
Submodules
10+
----------
11+
12+
macaron.build\_spec\_generator.common\_spec.base\_spec module
13+
-------------------------------------------------------------
14+
15+
.. automodule:: macaron.build_spec_generator.common_spec.base_spec
16+
:members:
17+
:show-inheritance:
18+
:undoc-members:
19+
20+
macaron.build\_spec\_generator.common\_spec.core module
21+
-------------------------------------------------------
22+
23+
.. automodule:: macaron.build_spec_generator.common_spec.core
24+
:members:
25+
:show-inheritance:
26+
:undoc-members:
27+
28+
macaron.build\_spec\_generator.common\_spec.jdk\_finder module
29+
--------------------------------------------------------------
30+
31+
.. automodule:: macaron.build_spec_generator.common_spec.jdk_finder
32+
:members:
33+
:show-inheritance:
34+
:undoc-members:
35+
36+
macaron.build\_spec\_generator.common\_spec.jdk\_version\_normalizer module
37+
---------------------------------------------------------------------------
38+
39+
.. automodule:: macaron.build_spec_generator.common_spec.jdk_version_normalizer
40+
:members:
41+
:show-inheritance:
42+
:undoc-members:
43+
44+
macaron.build\_spec\_generator.common\_spec.maven\_spec module
45+
--------------------------------------------------------------
46+
47+
.. automodule:: macaron.build_spec_generator.common_spec.maven_spec
48+
:members:
49+
:show-inheritance:
50+
:undoc-members:
51+
52+
macaron.build\_spec\_generator.common\_spec.pypi\_spec module
53+
-------------------------------------------------------------
54+
55+
.. automodule:: macaron.build_spec_generator.common_spec.pypi_spec
56+
:members:
57+
:show-inheritance:
58+
:undoc-members:

docs/source/pages/developers_guide/apidoc/macaron.build_spec_generator.rst

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Subpackages
1313
:maxdepth: 1
1414

1515
macaron.build_spec_generator.cli_command_parser
16+
macaron.build_spec_generator.common_spec
1617
macaron.build_spec_generator.reproducible_central
1718

1819
Submodules
@@ -34,22 +35,6 @@ macaron.build\_spec\_generator.build\_spec\_generator module
3435
:show-inheritance:
3536
:undoc-members:
3637

37-
macaron.build\_spec\_generator.jdk\_finder module
38-
-------------------------------------------------
39-
40-
.. automodule:: macaron.build_spec_generator.jdk_finder
41-
:members:
42-
:show-inheritance:
43-
:undoc-members:
44-
45-
macaron.build\_spec\_generator.jdk\_version\_normalizer module
46-
--------------------------------------------------------------
47-
48-
.. automodule:: macaron.build_spec_generator.jdk_version_normalizer
49-
:members:
50-
:show-inheritance:
51-
:undoc-members:
52-
5338
macaron.build\_spec\_generator.macaron\_db\_extractor module
5439
------------------------------------------------------------
5540

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
3+
4+
"""Script to compare a generated default buildspec."""
5+
6+
7+
import argparse
8+
import json
9+
import logging
10+
from collections.abc import Callable
11+
12+
logger = logging.getLogger(__name__)
13+
logger.setLevel(logging.DEBUG)
14+
logging.basicConfig(format="[%(filename)s:%(lineno)s %(tag)s] %(message)s")
15+
16+
17+
def log_with_tag(tag: str) -> Callable[[str], None]:
18+
"""Generate a log function that prints the name of the file and a tag at the beginning of each line."""
19+
20+
def log_fn(msg: str) -> None:
21+
logger.info(msg, extra={"tag": tag})
22+
23+
return log_fn
24+
25+
26+
log_info = log_with_tag("INFO")
27+
log_err = log_with_tag("ERROR")
28+
log_passed = log_with_tag("PASSED")
29+
log_failed = log_with_tag("FAILED")
30+
31+
32+
def log_diff(name: str, result: object, expected: object) -> None:
33+
"""Pretty-print the diff of two Python objects."""
34+
output = [
35+
f"'{name}'",
36+
*("---- Result ---", json.dumps(result, indent=4)),
37+
*("---- Expected ---", json.dumps(expected, indent=4)),
38+
"-----------------",
39+
]
40+
log_info("\n".join(output))
41+
42+
43+
CompareFn = Callable[[object, object], bool]
44+
45+
46+
def skip_compare(_result: object, _expected: object) -> bool:
47+
"""Return ``True`` always.
48+
49+
This compare function is used when we want to skip comparing a field.
50+
"""
51+
return True
52+
53+
54+
def compare_json(
55+
result: object,
56+
expected: object,
57+
compare_fn_map: dict[str, CompareFn],
58+
name: str = "",
59+
) -> bool:
60+
"""Compare two JSON values.
61+
62+
This function should not try to return immediately when it encounters a mismatch.
63+
Rather, it should try to report as many mismatches as possible.
64+
65+
Parameters
66+
----------
67+
result : object
68+
The result value.
69+
expected : object
70+
The expected value.
71+
compare_fn_map : dict[str, CompareFn]
72+
A map from field name to corresponding compare function.
73+
name : str
74+
The name of the field.
75+
Field names must follow the following rules:
76+
- At the top level: empty string ""
77+
- A subfield "bar" in an object field with name ".foo" has the name ".foo.bar".
78+
- A subfield "baz" in an object field with name ".foo.bar" has the name ".foo.bar.baz".
79+
- The ith element in an array field with name ".foo" have the name ".foo[i]".
80+
81+
Returns
82+
-------
83+
bool
84+
``True`` if the comparison is successful, ``False`` otherwise.
85+
"""
86+
if name in compare_fn_map:
87+
return compare_fn_map[name](result, expected)
88+
89+
if isinstance(expected, list):
90+
if not isinstance(result, list):
91+
log_err(f"Expected '{name}' to be a JSON array.")
92+
log_diff(name, result, expected)
93+
# Nothing else to check.
94+
return False
95+
return compare_list(result, expected, compare_fn_map, name)
96+
if isinstance(expected, dict):
97+
if not isinstance(result, dict):
98+
log_err(f"Expected '{name}' to be a JSON object.")
99+
log_diff(name, result, expected)
100+
# Nothing else to check.
101+
return False
102+
return compare_dict(result, expected, compare_fn_map, name)
103+
104+
if result != expected:
105+
log_err(f"Mismatch found in '{name}'")
106+
log_diff(name, result, expected)
107+
return False
108+
109+
return True
110+
111+
112+
def compare_list(
113+
result: list,
114+
expected: list,
115+
compare_fn_map: dict[str, CompareFn],
116+
name: str,
117+
) -> bool:
118+
"""Compare two JSON arrays.
119+
120+
Parameters
121+
----------
122+
result : list
123+
The result array.
124+
expected : list
125+
The expected array.
126+
compare_fn_map : dict[str, CompareFn]
127+
A map from field name to corresponding compare function.
128+
name : str
129+
The name of the field whose value is being compared in this function.
130+
131+
Returns
132+
-------
133+
bool
134+
``True`` if the comparison is successful, ``False`` otherwise.
135+
"""
136+
if len(result) != len(expected):
137+
log_err(f"Expected field '{name}' of length {len(result)} in result to have length {len(expected)}")
138+
log_diff(name, result, expected)
139+
# Nothing else to compare.
140+
return False
141+
142+
equal = True
143+
144+
for i, (result_element, expected_element) in enumerate(zip(result, expected)):
145+
equal &= compare_json(
146+
result=result_element,
147+
expected=expected_element,
148+
compare_fn_map=compare_fn_map,
149+
name=f"{name}[{i}]",
150+
)
151+
152+
return equal
153+
154+
155+
def compare_dict(
156+
result: dict,
157+
expected: dict,
158+
compare_fn_map: dict[str, CompareFn],
159+
name: str,
160+
) -> bool:
161+
"""Compare two JSON objects.
162+
163+
Parameters
164+
----------
165+
result : dict
166+
The result object.
167+
expected : dict
168+
The expected object.
169+
compare_fn_map : dict[str, CompareFn]
170+
A map from field name to corresponding compare function.
171+
name : str
172+
The name of the field whose value is being compared in this function.
173+
174+
Returns
175+
-------
176+
bool
177+
``True`` if the comparison is successful, ``False`` otherwise.
178+
"""
179+
result_keys_only = result.keys() - expected.keys()
180+
expected_keys_only = expected.keys() - result.keys()
181+
182+
equal = True
183+
184+
if len(result_keys_only) > 0:
185+
log_err(f"'{name}' in result has the following extraneous fields: {result_keys_only}")
186+
equal = False
187+
188+
if len(expected_keys_only) > 0:
189+
log_err(f"'{name}' in result does not contain these expected fields: {expected_keys_only}")
190+
equal = False
191+
192+
common_keys = set(result.keys()).intersection(set(expected.keys()))
193+
194+
for key in common_keys:
195+
equal &= compare_json(
196+
result=result[key],
197+
expected=expected[key],
198+
name=f"{name}.{key}",
199+
compare_fn_map=compare_fn_map,
200+
)
201+
202+
return equal
203+
204+
205+
def main() -> int:
206+
"""Compare a Macaron generated default buildspec.
207+
208+
Returns
209+
-------
210+
int
211+
0 if the generated buildspec matches the expected output, or non-zero otherwise.
212+
"""
213+
parser = argparse.ArgumentParser()
214+
parser.add_argument("result_file", help="the result buildspec file")
215+
parser.add_argument("expected_buildspec_file", help="the expected buildspec file")
216+
args = parser.parse_args()
217+
218+
with open(args.result_file, encoding="utf-8") as file:
219+
buildspec = json.load(file)
220+
221+
with open(args.expected_buildspec_file, encoding="utf-8") as file:
222+
expected_buildspec = json.load(file)
223+
224+
log_info(
225+
f"Comparing the buildspec file {args.result_file} with the expected output file {args.expected_buildspec_file}"
226+
)
227+
228+
equal = compare_json(
229+
result=buildspec,
230+
expected=expected_buildspec,
231+
compare_fn_map={
232+
# We should not compare Macaron version against the snapshot test buildspec because the test will
233+
# fail once we bump the Macaron version in the next release, failing the automatic release altogether!
234+
".macaron_version": skip_compare,
235+
},
236+
)
237+
238+
if not equal:
239+
log_failed("The generated buildspec does not match the expected output.")
240+
return 1
241+
242+
log_passed("The generated buildspec matches the expected output")
243+
return 0
244+
245+
246+
if __name__ == "__main__":
247+
raise SystemExit(main())
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"macaron_version": "0.18.0", "group_id": "org.apache.hugegraph", "artifact_id": "computer-k8s", "version": "1.0.0", "git_repo": "https://github.com/apache/hugegraph-computer", "git_tag": "d2b95262091d6572cc12dcda57d89f9cd44ac88b", "newline": "lf", "language_version": "11", "ecosystem": "maven", "purl": "pkg:maven/org.apache.hugegraph/computer-k8s@1.0.0", "language": "java", "build_tool": "maven", "build_commands": [["mvn", "-DskipTests=true", "-Dmaven.test.skip=true", "-Dmaven.site.skip=true", "-Drat.skip=true", "-Dmaven.javadoc.skip=true", "clean", "package"]]}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
3+
4+
description: |
5+
Generate a buildspec for this PURL and validate the buildspec content.
6+
7+
tags:
8+
- macaron-python-package
9+
- macaron-gen-build-spec
10+
- tutorial
11+
12+
steps:
13+
- name: Run macaron analyze
14+
kind: analyze
15+
options:
16+
command_args:
17+
- -purl
18+
- pkg:maven/org.apache.hugegraph/computer-k8s@1.0.0
19+
- name: Run the default buildspec generation
20+
kind: gen-build-spec
21+
options:
22+
command_args:
23+
- -purl
24+
- pkg:maven/org.apache.hugegraph/computer-k8s@1.0.0
25+
- name: Compare Buildspec.
26+
kind: compare
27+
options:
28+
kind: default_build_spec
29+
result: output/buildspec/maven/org_apache_hugegraph/computer-k8s/macaron.buildspec
30+
expected: expected_default.buildspec
31+
- name: Run the buildspec generation explicitly providing the default output format option.
32+
kind: gen-build-spec
33+
options:
34+
command_args:
35+
- -purl
36+
- pkg:maven/org.apache.hugegraph/computer-k8s@1.0.0
37+
- --output-format
38+
- default-buildspec
39+
- name: Compare Buildspec.
40+
kind: compare
41+
options:
42+
kind: default_build_spec
43+
result: output/buildspec/maven/org_apache_hugegraph/computer-k8s/macaron.buildspec
44+
expected: expected_default.buildspec
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"macaron_version": "0.18.0", "group_id": null, "artifact_id": "toga", "version": "0.5.1", "git_repo": "https://github.com/beeware/toga", "git_tag": "ef1912b0a1b5c07793f9aa372409f5b9d36f2604", "newline": "lf", "language_version": "", "ecosystem": "pypi", "purl": "pkg:pypi/toga@0.5.1", "language": "python", "build_tool": "pip", "build_commands": [["pip", "install", "-U", "pip"]]}

0 commit comments

Comments
 (0)