Skip to content

Commit b7517df

Browse files
Use the MultiFileInputTool class in pkg tool (#513)
Using the predefined arguments in MultiFileInputTool class and modify the system/unit tests accordingly
1 parent ac381bf commit b7517df

File tree

6 files changed

+147
-182
lines changed

6 files changed

+147
-182
lines changed

lobster/tools/pkg/pkg.py

Lines changed: 78 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,20 @@
1616
# You should have received a copy of the GNU Affero General Public
1717
# License along with this program. If not, see
1818
# <https://www.gnu.org/licenses/>.
19-
import os.path
2019
from pathlib import Path
2120
import sys
2221
import xml.etree.ElementTree as ET
2322
import json
2423
import re
25-
from typing import Dict, List, Optional, Sequence
24+
from typing import Dict, Optional, Sequence
2625
from xml.dom import minidom
2726
from argparse import Namespace
2827

28+
from lobster.common.multi_file_input_config import Config
29+
from lobster.common.multi_file_input_tool import create_worklist, MultiFileInputTool
2930
from lobster.common.exceptions import LOBSTER_Exception
30-
from lobster.common.io import lobster_write
3131
from lobster.common.items import Activity, Tracing_Tag
3232
from lobster.common.location import File_Reference
33-
from lobster.common.meta_data_tool_base import MetaDataToolBase
3433

3534
NS = {
3635
"ecu": "http://www.tracetronic.de/xml/ecu-test",
@@ -39,30 +38,6 @@
3938
TSBLOCK = "TsBlock"
4039

4140

42-
def get_valid_files(
43-
file_dir: List[str]
44-
) -> List[str]:
45-
file_list = []
46-
for item in file_dir:
47-
if os.path.isfile(item):
48-
file_list.append(item)
49-
elif os.path.isdir(item):
50-
for path, _, files in os.walk(item):
51-
for filename in files:
52-
_, ext = os.path.splitext(filename)
53-
if ext in (".pkg", ".ta"):
54-
file_list.append(os.path.join(path, filename))
55-
else:
56-
raise FileNotFoundError("%s is not a file or directory" % item)
57-
return file_list
58-
59-
60-
def write_to_file(options: Namespace, data: Dict[str, Activity]) -> None:
61-
with open(options.out, "w", encoding="UTF-8") as file:
62-
lobster_write(file, Activity, "lobster-pkg", data.values())
63-
print("Written output for %u items to %s" % (len(data), options.out))
64-
65-
6641
def create_raw_entry(
6742
data: Dict[str, Activity], file_name: str, trace_list: list
6843
) -> None:
@@ -284,87 +259,93 @@ def extract_lobster_traces_from_trace_analysis(tree, filename):
284259
return valid_traces, misplaced_traces
285260

286261

287-
def lobster_pkg(options):
288-
"""
289-
The main function to parse tracing information from .pkg files for LOBSTER.
262+
class PkgTool(MultiFileInputTool):
263+
def __init__(self):
264+
super().__init__(
265+
name="pkg",
266+
description="Extract tracing tags from pkg files for LOBSTER",
267+
extensions=["pkg", "ta"],
268+
official=True,
269+
)
290270

291-
This function processes the input files or directories specified in 'options.files',
292-
extracts tracing tags and activities from XML content (including both standard and
293-
TRACE-ANALYSIS blocks), and writes the results to an output file
271+
def _add_config_argument(self):
272+
# This tool does not use a config file
273+
pass
274+
275+
def lobster_pkg(self, options):
276+
"""
277+
The main function to parse tracing information from .pkg files for LOBSTER.
278+
279+
This function processes the input files or directories specified in
280+
'options.files', extracts tracing tags and activities from XML content
281+
(including both standard and TRACE-ANALYSIS blocks), and writes the
282+
results to an output file
283+
284+
Parameters
285+
----------
286+
options (Namespace): Parsed command-line arguments with at least:
287+
- dir_or_files: list of file or directory paths to process
288+
- out: output file path (optional; if not set, output is report.lobster)
289+
"""
290+
config = Config(
291+
inputs=None,
292+
inputs_from_file=None,
293+
extensions=self._extensions,
294+
exclude_patterns=None,
295+
schema=Activity,
296+
)
297+
file_list = create_worklist(config, options.dir_or_files)
298+
if not file_list:
299+
raise ValueError("No input files found to process!")
294300

295-
Parameters
296-
----------
297-
options (Namespace): Parsed command-line arguments with at least:
298-
- files: list of file or directory paths to process
299-
- out: output file path (optional; if not set, output is report.lobster)
300-
"""
301-
file_list = get_valid_files(options.files)
302-
data = {}
301+
data = {}
303302

304-
for file_path in file_list:
305-
filename = Path(file_path).name
306-
with open(file_path, "r", encoding="UTF-8") as file:
307-
try:
308-
file_content = file.read()
303+
for file_path in file_list:
304+
filename = Path(file_path).name
305+
with open(file_path, "r", encoding="UTF-8") as file:
306+
try:
307+
file_content = file.read()
309308

310-
tree = ET.fromstring(file_content)
309+
tree = ET.fromstring(file_content)
311310

312-
getvalues = xml_parser(file_content, filename)
311+
getvalues = xml_parser(file_content, filename)
313312

314-
# Also extract from TRACE-ANALYSIS blocks
315-
valid_traces, misplaced_traces = (
316-
extract_lobster_traces_from_trace_analysis(
317-
tree, filename
313+
# Also extract from TRACE-ANALYSIS blocks
314+
valid_traces, misplaced_traces = (
315+
extract_lobster_traces_from_trace_analysis(
316+
tree, filename
317+
)
318318
)
319-
)
320-
getvalues.extend(valid_traces)
321-
for msg in misplaced_traces:
322-
print(msg)
323-
324-
if getvalues:
325-
create_raw_entry(data, file.name, json.dumps(getvalues))
326-
else:
327-
create_default_activity(file_content, filename, data)
328-
329-
except ET.ParseError as err:
330-
print(f"Error parsing XML file '{filename}' : {err}")
331-
raise
332-
except LOBSTER_Exception as err:
333-
err.dump()
334-
raise
335-
336-
# Set default output file if not specified
337-
output_file = getattr(options, "out", None)
338-
if not output_file:
339-
options.out = "report.lobster"
340-
341-
write_to_file(options, data)
342-
343-
return 0
344-
345-
346-
class PkgTool(MetaDataToolBase):
347-
def __init__(self):
348-
super().__init__(
349-
name="pkg",
350-
description="Extract tracing tags from pkg files for LOBSTER",
351-
official=True,
352-
)
353-
self._argument_parser.add_argument(
354-
"files",
355-
nargs="+",
356-
metavar="FILE|DIR",
357-
help="Path to pkg file or directory.",
319+
getvalues.extend(valid_traces)
320+
for msg in misplaced_traces:
321+
print(msg)
322+
323+
if getvalues:
324+
create_raw_entry(data, file.name, json.dumps(getvalues))
325+
else:
326+
create_default_activity(file_content, filename, data)
327+
328+
except ET.ParseError as err:
329+
print(f"Error parsing XML file '{filename}' : {err}")
330+
raise
331+
except LOBSTER_Exception as err:
332+
err.dump()
333+
raise
334+
335+
items = (
336+
list(data.values())
337+
if not isinstance(data.values(), list)
338+
else data.values()
358339
)
359-
self._argument_parser.add_argument(
360-
"--out",
361-
required=True,
362-
help="write output to this file; otherwise output is report.lobster",
340+
self._write_output(
341+
schema=config.schema,
342+
out_file=options.out,
343+
items=items,
363344
)
364345

365346
def _run_impl(self, options: Namespace) -> int:
366347
try:
367-
lobster_pkg(options)
348+
self.lobster_pkg(options)
368349
return 0
369350
except (ValueError, FileNotFoundError,
370351
LOBSTER_Exception, ET.ParseError) as exception:

tests_system/lobster_pkg/lobster_pkg_asserter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
class LobsterPkgAsserter(Asserter):
1111
def assertStdOutNumAndFile(self, num_items: int, out_file: str):
1212
self.assertStdOutText(
13-
f"Written output for {num_items} items to {out_file}\n"
13+
f"lobster-pkg: wrote {num_items} items to {out_file}\n"
1414
)

tests_system/lobster_pkg/test_invalid_scenario.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ def test_missing_input_parameter(self):
3333

3434
completed_process = self._test_runner.run_tool_test()
3535
asserter = LobsterPkgAsserter(self, completed_process, self._test_runner)
36-
asserter.assertInStdErr('the following arguments are required: FILE|DIR')
37-
asserter.assertExitCode(2)
36+
asserter.assertInStdErr('lobster-pkg: No input files found to process!\n')
37+
asserter.assertExitCode(1)
3838

3939
def test_not_existing_output_path(self):
4040
"""Test that a missing output path causes non-zero exit code"""
@@ -66,7 +66,7 @@ def test_misplaced_lobster_trace_file(self):
6666
expected_output = (
6767
'WARNING: misplaced lobster-trace in misplaced_lobster_trace.pkg: '
6868
'lobster-trace: misplaced.req1,misplaced.req2\n'
69-
'Written output for 1 items to report.lobster\n'
69+
'lobster-pkg: wrote 1 items to report.lobster\n'
7070
)
7171
asserter.assertStdOutText(expected_output)
7272
asserter.assertExitCode(0)
@@ -108,18 +108,6 @@ def test_invalid_xml_file(self):
108108
asserter.assertStdOutText(expected_output)
109109
asserter.assertExitCode(1)
110110

111-
def test_invalid_input_pkg_file_without_output_argument(self):
112-
self._test_runner.cmd_args.files = [
113-
str(self._data_directory / "valid_file1.pkg")
114-
]
115-
116-
completed_process = self._test_runner.run_tool_test()
117-
118-
asserter = LobsterPkgAsserter(self, completed_process, self._test_runner)
119-
120-
asserter.assertExitCode(2)
121-
asserter.assertInStdErr("the following arguments are required: --out")
122-
123111

124112
if __name__ == "__main__":
125113
unittest.main()

tests_system/lobster_pkg/test_valid_scenario.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def test_valid_ta_file_with_misplaced_traces(self):
7676
'lobster-trace:12345678\n'
7777
'WARNING: misplaced lobster-trace in with_misplaced_traces.ta: '
7878
'lobster-trace:98765432\n'
79-
'Written output for 1 items to with_misplaced_traces.lobster\n'
79+
'lobster-pkg: wrote 1 items to with_misplaced_traces.lobster\n'
8080
)
8181
asserter.assertExitCode(0)
8282

@@ -103,7 +103,7 @@ def test_valid_pkg_file_with_misplaced_traces(self):
103103
'lobster-trace: misplaced.req1,misplaced.req2\n'
104104
'WARNING: misplaced lobster-trace in with_misplaced_traces.pkg: '
105105
'lobster-trace: misplaced.req3,misplaced.req4\n'
106-
'Written output for 1 items to valid_file1.lobster\n'
106+
'lobster-pkg: wrote 1 items to valid_file1.lobster\n'
107107
)
108108
asserter.assertExitCode(0)
109109

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<PACKAGE format-rev="7" prog-version="2023.4.3.143937+fa3cbd256512" xmlns="http://www.tracetronic.de/xml/ecu-test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.tracetronic.de/xml/ecu-test package.xsd">
3+
<INFORMATION format-rev="2" xsi:type="packageInfo">
4+
<TAGS>
5+
<TAG xsi:type="string">TESTCASE</TAG>
6+
</TAGS>
7+
<DESCRIPTION xsi:type="string">&quot;RequirementTrace&quot;: [
8+
&quot;banana.reqE&quot;,
9+
&quot;banana.reqF&quot;]</DESCRIPTION>
10+
<VERSION xsi:type="string"/>
11+
<ALTERNATE-CALL-REPRESENTATION-ACTION-FIELD-TEMPLATE xsi:type="string"/>
12+
<ALTERNATE-CALL-REPRESENTATION-EXPECTATION-FIELD-TEMPLATE xsi:type="string"/>
13+
</INFORMATION>
14+
<TESTSTEPS xsi:type="testCase">
15+
<TESTSTEP format-rev="3" id="1" name="TsBlock" xsi:type="utility-11111111111">
16+
<VALUE xsi:type="string">lobster-trace: banana.reqA,banana.reqB</VALUE>
17+
<TESTSTEP format-rev="3" id="2" name="TsBlock" xsi:type="utility-22222222222">
18+
<VALUE xsi:type="string">lobster-trace: allowed.req1,allowed.req2</VALUE>
19+
<TESTSTEP format-rev="3" id="3" name="TsBlock" xsi:type="utility-33333333333">
20+
<VALUE xsi:type="string">lobster-trace: misplaced.req1,misplaced.req2</VALUE>
21+
</TESTSTEP>
22+
</TESTSTEP>
23+
</TESTSTEP>
24+
</TESTSTEPS>
25+
<TRACE-ANALYSIS>
26+
<!-- Valid: DESCRIPTION is direct child of ANALYSISITEM (type=episode) -->
27+
<ANALYSISITEM xsi:type="episode">
28+
<NAME>Episode1</NAME>
29+
<DESCRIPTION>lobster-trace: valid.req1,valid.req2</DESCRIPTION>
30+
</ANALYSISITEM>
31+
<!-- Misplaced: DESCRIPTION is nested inside another ANALYSISITEM -->
32+
<ANALYSISITEM xsi:type="episode">
33+
<NAME>Episode2</NAME>
34+
<ANALYSISITEM xsi:type="other">
35+
<DESCRIPTION>lobster-trace: misplaced.req1,misplaced.req2</DESCRIPTION>
36+
</ANALYSISITEM>
37+
</ANALYSISITEM>
38+
</TRACE-ANALYSIS>
39+
</PACKAGE>

0 commit comments

Comments
 (0)