Coverage for tests/unit_tests/common/test_proto_util.py: 100%

93 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-07 05:08 +0000

1# This file is part of the Apio project 

2# (C) 2016-present FPGAwars 

3# Project authors: 

4# * Jesús Arroyo (Jesus89) (2016-2019) 

5# * Juan Gonzalez (obijuan) (2019-2024) 

6# * The Apio Team (2024-present) 

7# License: GPLv2 

8 

9""" 

10Tests of proto_util.py 

11""" 

12 

13import pytest 

14from tests.conftest import ApioRunner 

15from apio.common.proto.stubs.apio_testing_pb2 import MessageA, MessageB 

16from apio.common.proto_util import ( 

17 proto_from_json_dict, 

18 proto_to_json_dict, 

19 check_is_required, 

20 check_not_required, 

21) 

22 

23 

24def test_proto_from_dict_full(apio_runner: ApioRunner): 

25 """Test parsing of a proto from a json dict with all required and 

26 optional fields included.""" 

27 

28 with apio_runner.in_sandbox(): 

29 

30 json_dict1 = { 

31 "field-b1": { 

32 "field-a1": "aaa", 

33 "field-a2": "bbb", 

34 }, 

35 "field-b2": { 

36 "field-a1": "ccc", 

37 "field-a2": "ddd", 

38 }, 

39 } 

40 

41 # -- Parse proto from json dict and verify 

42 proto_msg = proto_from_json_dict(json_dict1, MessageB) 

43 assert proto_msg is not None 

44 

45 assert proto_msg.field_b1.field_a1 == "aaa" 

46 assert proto_msg.field_b1.field_a2 == "bbb" 

47 assert proto_msg.field_b2.field_a1 == "ccc" 

48 assert proto_msg.field_b2.field_a2 == "ddd" 

49 

50 # -- Convert back to json dict and compare. 

51 json_dict2 = proto_to_json_dict(proto_msg) 

52 

53 assert json_dict2 == json_dict1 

54 

55 

56def test_proto_from_dict_minimal(apio_runner: ApioRunner): 

57 """Test parsing of a proto from a json dict with only required 

58 fields included.""" 

59 

60 with apio_runner.in_sandbox(): 

61 

62 json_dict1 = { 

63 "field-b1": { 

64 "field-a1": "aaa", 

65 }, 

66 } 

67 

68 # -- Parse proto from json dict and verify 

69 proto_msg = proto_from_json_dict(json_dict1, MessageB) 

70 assert proto_msg is not None 

71 

72 assert proto_msg.field_b1.field_a1 == "aaa" 

73 

74 assert not proto_msg.HasField("field_b2") 

75 assert not proto_msg.field_b1.HasField("field_a2") 

76 

77 # -- Convert back to json dict and compare. 

78 json_dict2 = proto_to_json_dict(proto_msg) 

79 

80 assert json_dict2 == json_dict1 

81 

82 

83def test_proto_from_dict_missing_field(apio_runner: ApioRunner): 

84 """Test parsing of a proto from a json dict with a required 

85 field missing.""" 

86 

87 with apio_runner.in_sandbox(): 

88 

89 json_dict1 = { 

90 "field-b1": { 

91 # -- Required field 'field-a1' is missing. 

92 "field-a2": "aaa", 

93 }, 

94 } 

95 

96 with apio_runner.with_logger() as log: 

97 proto_msg = proto_from_json_dict(json_dict1, MessageB) 

98 assert proto_msg is None 

99 assert "Missing required field 'field-b1.field-a1'" in log.out 

100 

101 

102def test_proto_from_dict_unknown_field(apio_runner: ApioRunner): 

103 """Test parsing of a proto from a json dict with a an unknown field.""" 

104 

105 with apio_runner.in_sandbox(): 

106 

107 json_dict1 = { 

108 "field-b1": {"field-a1": "aaa", "no-such-field": "bbb"}, 

109 } 

110 

111 with apio_runner.with_logger() as log: 

112 # with pytest.raises(SystemExit) as e: 

113 proto_msg = proto_from_json_dict( 

114 json_dict1, 

115 MessageB, 

116 ) 

117 assert proto_msg is None 

118 assert "Unknown field 'no-such-field'" in log.out 

119 

120 

121def test_check_is_required(apio_runner: ApioRunner): 

122 """Test test_check_is_required()""" 

123 

124 with apio_runner.in_sandbox(): 

125 

126 msg = MessageB(field_b1=MessageA(field_a1="aaa")) 

127 

128 # -- Check required fields only (success) 

129 check_is_required(msg, "field_b1") 

130 check_is_required(msg.field_b1, "field_a1") 

131 check_is_required(msg, "field_b1.field_a1") 

132 

133 # -- Check with an optional field (failure) 

134 with apio_runner.with_logger() as log: 

135 with pytest.raises(SystemExit) as e: 

136 check_is_required(msg, "field_b1", "field_b2") 

137 

138 assert e.value.code == 1 

139 assert ( 

140 "Field 'field_b2' of 'apio.common.proto.MessageB' " 

141 "is not required" in log.out 

142 ) 

143 

144 # -- Check with an optional nested field (failure) 

145 with apio_runner.with_logger() as log: 

146 with pytest.raises(SystemExit) as e: 

147 check_is_required(msg, "field_b1", "field_b1.field_a2") 

148 

149 assert e.value.code == 1 

150 assert ( 

151 "Field 'field_b1.field_a2' of 'apio.common.proto.MessageB' " 

152 "is not required" in log.out 

153 ) 

154 

155 # -- Check with an unknown field (failure) 

156 with apio_runner.with_logger() as log: 

157 with pytest.raises(SystemExit) as e: 

158 check_is_required(msg, "field_b1", "field_xyz") 

159 

160 assert e.value.code == 1 

161 assert ( 

162 "Field 'field_xyz' is not a field of protocol buffer " 

163 "message 'apio.common.proto.MessageB'" in log.out 

164 ) 

165 

166 # -- Check with an unknown nested field (failure) 

167 with apio_runner.with_logger() as log: 

168 with pytest.raises(SystemExit) as e: 

169 check_is_required(msg, "field_b1", "field_b1.field_xyz") 

170 

171 assert e.value.code == 1 

172 assert ( 

173 "Field 'field_b1.field_xyz' is not a field of protocol buffer " 

174 "message 'apio.common.proto.MessageB'" in log.out 

175 ) 

176 

177 

178def test_check_not_required(apio_runner: ApioRunner): 

179 """Test test_check_not_required()""" 

180 

181 with apio_runner.in_sandbox(): 

182 

183 msg = MessageB(field_b1=MessageA(field_a1="aaa")) 

184 

185 # -- Check optional fields only (success) 

186 check_not_required(msg, "field_b2") 

187 check_not_required(msg, "field_b2.field_a1") 

188 check_not_required(msg, "field_b2.field_a2") 

189 check_not_required(msg, "field_b1.field_a2") 

190 check_not_required(msg.field_b1, "field_a2") 

191 

192 # -- Check with required field (failure) 

193 with apio_runner.with_logger() as log: 

194 with pytest.raises(SystemExit) as e: 

195 check_not_required(msg, "field_b2", "field_b1") 

196 

197 assert e.value.code == 1 

198 assert ( 

199 "Field 'field_b1' of 'apio.common.proto.MessageB' " 

200 "is required" in log.out 

201 ) 

202 

203 # -- Check with an required nested field (failure) 

204 with apio_runner.with_logger() as log: 

205 with pytest.raises(SystemExit) as e: 

206 check_not_required(msg, "field_b2", "field_b1.field_a1") 

207 

208 assert e.value.code == 1 

209 assert ( 

210 "Field 'field_b1.field_a1' of 'apio.common.proto.MessageB' " 

211 "is required" in log.out 

212 ) 

213 

214 # -- Check with an unknown field (failure) 

215 with apio_runner.with_logger() as log: 

216 with pytest.raises(SystemExit) as e: 

217 check_is_required(msg, "field_b1", "field_xyz") 

218 

219 assert e.value.code == 1 

220 assert ( 

221 "Field 'field_xyz' is not a field of protocol buffer " 

222 "message 'apio.common.proto.MessageB'" in log.out 

223 ) 

224 

225 # -- Check with an unknown nested field (failure) 

226 with apio_runner.with_logger() as log: 

227 with pytest.raises(SystemExit) as e: 

228 check_is_required(msg, "field_b1", "field_b1.field_xyz") 

229 

230 assert e.value.code == 1 

231 assert ( 

232 "Field 'field_b1.field_xyz' is not a field of protocol buffer " 

233 "message 'apio.common.proto.MessageB'" in log.out 

234 )