Coverage for lobster/tools/trlc/trlc_tool.py: 88%

67 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2025-08-27 13:02 +0000

1#!/usr/bin/env python3 

2# 

3# LOBSTER - Lightweight Open BMW Software Traceability Evidence Report 

4# Copyright (C) 2023-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 

5# 

6# This program is free software: you can redistribute it and/or modify 

7# it under the terms of the GNU Affero General Public License as 

8# published by the Free Software Foundation, either version 3 of the 

9# License, or (at your option) any later version. 

10# 

11# This program is distributed in the hope that it will be useful, but 

12# WITHOUT ANY WARRANTY; without even the implied warranty of 

13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

14# Affero General Public License for more details. 

15# 

16# You should have received a copy of the GNU Affero General Public 

17# License along with this program. If not, see 

18# <https://www.gnu.org/licenses/>. 

19 

20import argparse 

21import os 

22import sys 

23from typing import Iterable, Optional, Sequence 

24 

25from yamale import YamaleError 

26 

27from trlc.errors import Message_Handler, TRLC_Error 

28from trlc.trlc import Source_Manager 

29 

30from lobster.common.errors import PathError 

31from lobster.common.io import lobster_write 

32from lobster.common.items import Requirement 

33from lobster.common.multi_file_input_tool import create_worklist, MultiFileInputTool 

34 

35from lobster.tools.trlc.converter import Converter 

36from lobster.tools.trlc.errors import ( 

37 InvalidConversionRuleError, 

38 RecordObjectComponentError, 

39 TrlcFailure, 

40 TupleToStringFailedError, 

41 TupleToStringMissingError, 

42) 

43from lobster.tools.trlc.lobster_trlc_config import LobsterTrlcConfig 

44 

45 

46class LOBSTER_Trlc(MultiFileInputTool): 

47 def __init__(self): 

48 super().__init__( 

49 name = "trlc", 

50 description = "Extract tracing data from TRLC files.", 

51 extensions = ["rsl", "trlc"], 

52 official = True, 

53 ) 

54 

55 def _run_impl(self, options: argparse.Namespace): 

56 try: 

57 self._execute(options) 

58 return 0 

59 except YamaleError as e: 

60 print( 

61 f"{self.name}: The configuration file does not " 

62 f"conform to the YAML schema. {e}", 

63 file=sys.stderr, 

64 ) 

65 except TRLC_Error as e: 

66 print( 

67 f"{self.name}: An error occurred during processing: {e}", 

68 file=sys.stderr, 

69 ) 

70 except FileNotFoundError as e: 

71 print( 

72 f"{self.name}: File or directory not found: {e}", 

73 file=sys.stderr, 

74 ) 

75 except PathError as e: 

76 print( 

77 f"{self.name}: {e}", 

78 file=sys.stderr, 

79 ) 

80 except TrlcFailure as e: 

81 print( 

82 f"{self.name}: TRLC processing failed: {e}", 

83 file=sys.stderr, 

84 ) 

85 except (InvalidConversionRuleError, RecordObjectComponentError) as e: 

86 print( 

87 f"{self.name}: Invalid conversion rule defined in {options.config}: " 

88 f"{e}", 

89 file=sys.stderr, 

90 ) 

91 except (TupleToStringMissingError, TupleToStringFailedError) as e: 

92 print( 

93 f"{self.name}: error in 'to-string-rules' in {options.config}: " 

94 f"{e}", 

95 file=sys.stderr, 

96 ) 

97 

98 return 1 

99 

100 @staticmethod 

101 def _register_trlc_files(sm: Source_Manager, work_list: Iterable[str]): 

102 for item in work_list: 

103 ok = True 

104 if os.path.isfile(item): 104 ↛ 106line 104 didn't jump to line 106 because the condition on line 104 was always true

105 ok = sm.register_file(item) 

106 elif os.path.isdir(item): 

107 ok = sm.register_directory(item) 

108 else: 

109 raise FileNotFoundError(item) 

110 if not ok: 110 ↛ 111line 110 didn't jump to line 111 because the condition on line 110 was never true

111 raise PathError(f"Failed to register file or directory '{item}'") 

112 

113 def _execute(self, options: argparse.Namespace) -> None: 

114 config = LobsterTrlcConfig.from_file(options.config) 

115 work_list = create_worklist(config, options.dir_or_files) 

116 trlc_mh = Message_Handler() 

117 sm = Source_Manager(trlc_mh) 

118 self._register_trlc_files(sm, work_list) 

119 symbol_table = sm.process() 

120 if not symbol_table: 

121 raise TrlcFailure("aborting due to TRLC error") 

122 

123 items = [] 

124 converter = Converter( 

125 conversion_rules=config.conversion_rules, 

126 to_string_rules=config.to_string_rules, 

127 symbol_table=symbol_table, 

128 ) 

129 for n_obj in symbol_table.iter_record_objects(): 

130 item = converter.generate_lobster_object(n_obj) 

131 if item: 

132 items.append(item) 

133 

134 with open(options.out, "w", encoding="UTF-8") as fd: 

135 # lobster-trace: trlc_req.Output_File 

136 lobster_write( 

137 fd=fd, 

138 kind=Requirement, 

139 generator="lobster-trlc", 

140 items=items, 

141 ) 

142 print(f"lobster-trlc: successfully wrote {len(items)} items to {options.out}") 

143 

144 

145def main(args: Optional[Sequence[str]] = None) -> int: 

146 return LOBSTER_Trlc().run(args)