Skip to content

Commit 272569e

Browse files
authored
Parse request/response from payload success depends on module loading order (#158)
* Fixed #157 : Parsing payload may fail depending on module loading order
1 parent 2ead26b commit 272569e

27 files changed

+99
-54
lines changed

test/test_response.py

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,32 @@
33
from test.UdsTest import UdsTest
44
import inspect
55

6+
67
class DummyServiceNormal(BaseService):
78
_sid = 0x13
89

910
def subfunction_id(self):
1011
return 0x44
1112

13+
1214
class DummyServiceNoSubunction(BaseService):
1315
_sid = 0x13
1416
_use_subfunction = False
1517

18+
1619
class DummyServiceNoResponseData(BaseService):
1720
_sid = 0x13
1821
_no_response_data = True
1922

23+
2024
class RandomClass:
2125
pass
2226

2327

2428
class TestResponse(UdsTest):
2529

2630
def test_create_from_instance_ok(self):
27-
response = Response(DummyServiceNormal(), code = 0x22)
31+
response = Response(DummyServiceNormal(), code=0x22)
2832
self.assertTrue(response.valid)
2933
self.assertEqual(response.service.request_id(), 0x13)
3034
self.assertEqual(response.code, 0x22)
@@ -36,57 +40,59 @@ def test_create_from_class_ok(self):
3640
self.assertEqual(response.code, 0x22)
3741

3842
def test_make_payload_basic_positive(self):
39-
response = Response(DummyServiceNormal(), code = 0, data=b"\x01")
43+
response = Response(DummyServiceNormal(), code=0, data=b"\x01")
4044
self.assertTrue(response.positive)
4145
self.assertTrue(response.valid)
4246
payload = response.get_payload()
43-
self.assertEqual(b"\x53\x01", payload) # Original ID + 0x40
47+
self.assertEqual(b"\x53\x01", payload) # Original ID + 0x40
4448

4549
def test_make_payload_basic_negative(self):
46-
response = Response(DummyServiceNormal(), code = 0x10) # General Reject
50+
response = Response(DummyServiceNormal(), code=0x10) # General Reject
4751
self.assertFalse(response.positive)
4852
self.assertTrue(response.valid)
4953
payload = response.get_payload()
50-
self.assertEqual(b"\x7F\x13\x10", payload) # Original ID + 0x40. 7F indicate negative
54+
self.assertEqual(b"\x7F\x13\x10", payload) # Original ID + 0x40. 7F indicate negative
5155

5256
def test_make_payload_custom_data_negative(self):
53-
response = Response(DummyServiceNormal(), code = 0x10)
57+
response = Response(DummyServiceNormal(), code=0x10)
5458
self.assertTrue(response.valid)
5559
self.assertFalse(response.positive)
5660
response.data = b"\x12\x34\x56\x78"
5761
payload = response.get_payload()
5862
self.assertEqual(b"\x7F\x13\x10\x12\x34\x56\x78", payload)
5963

6064
def test_from_payload_basic_positive(self):
61-
payload=b'\x7E\x00' # 0x7e = TesterPresent
65+
payload = b'\x7E\x00' # 0x7e = TesterPresent
6266
response = Response.from_payload(payload)
6367
self.assertTrue(response.valid)
6468
self.assertTrue(response.positive)
6569
self.assertEqual(response.service.response_id(), 0x7E)
6670
self.assertEqual(response.code, 0)
71+
self.assertEqual(response.code_name, 'PositiveResponse')
6772

6873
def test_from_payload_basic_negative(self):
69-
payload=b'\x7F\x3E\x10' # 0x3e = TesterPresent, 0x10 = General Reject
74+
payload = b'\x7F\x3E\x10' # 0x3e = TesterPresent, 0x10 = General Reject
7075
response = Response.from_payload(payload)
7176
self.assertTrue(response.valid)
7277
self.assertFalse(response.positive)
7378
self.assertEqual(response.service.response_id(), 0x7E)
74-
self.assertEqual(response.code, 0x10)
79+
self.assertEqual(response.code, 0x10)
80+
self.assertEqual(response.code_name, 'GeneralReject')
7581

7682
def test_from_payload_custom_data_positive(self):
77-
payload=b'\x7E\x01\x12\x34\x56\x78' # 0x3E = TesterPresent
83+
payload = b'\x7E\x01\x12\x34\x56\x78' # 0x3E = TesterPresent
7884
response = Response.from_payload(payload)
7985
self.assertTrue(response.valid)
8086
self.assertTrue(response.positive)
81-
self.assertEqual(response.service.response_id(), 0x7E)
87+
self.assertEqual(response.service.response_id(), 0x7E)
8288
self.assertEqual(response.data, b'\x01\x12\x34\x56\x78')
8389

8490
def test_from_payload_custom_data_negative(self):
85-
payload=b'\x7F\x3E\x10\x12\x34\x56\x78' # 0x3E = TesterPresent, 0x10 = General Reject
91+
payload = b'\x7F\x3E\x10\x12\x34\x56\x78' # 0x3E = TesterPresent, 0x10 = General Reject
8692
response = Response.from_payload(payload)
8793
self.assertTrue(response.valid)
8894
self.assertEqual(response.service.response_id(), 0x7E)
89-
self.assertEqual(response.code, 0x10)
95+
self.assertEqual(response.code, 0x10)
9096
self.assertEqual(response.data, b'\x12\x34\x56\x78')
9197

9298
def test_from_empty_payload(self):
@@ -110,25 +116,25 @@ def test_str_repr(self):
110116

111117
def test_from_input_param(self):
112118
with self.assertRaises(ValueError):
113-
response = Response(service = "a string")
119+
response = Response(service="a string")
114120

115121
with self.assertRaises(ValueError):
116-
response = Response(service = RandomClass())
122+
response = Response(service=RandomClass())
117123

118124
with self.assertRaises(ValueError):
119-
response = Response(service=DummyServiceNormal(), code = "string")
125+
response = Response(service=DummyServiceNormal(), code="string")
120126

121127
with self.assertRaises(ValueError):
122-
response = Response(service=DummyServiceNormal(), code = -1)
128+
response = Response(service=DummyServiceNormal(), code=-1)
123129

124130
with self.assertRaises(ValueError):
125-
response = Response(service=DummyServiceNormal(), code = 0x100)
131+
response = Response(service=DummyServiceNormal(), code=0x100)
126132

127133
with self.assertRaises(ValueError):
128-
response = Response(service=DummyServiceNormal(), code = 0x10, data=11)
134+
response = Response(service=DummyServiceNormal(), code=0x10, data=11)
129135

130136
def test_all_response_code_have_version(self):
131-
codes = [ member[1] for member in inspect.getmembers(Response.Code) if isinstance(member[1], int) and not member[0].startswith('_')]
137+
codes = [member[1] for member in inspect.getmembers(Response.Code) if isinstance(member[1], int) and not member[0].startswith('_')]
132138
self.assertGreater(len(codes), 0)
133139
for code in codes:
134140
# Make sure no exception is raised
@@ -137,10 +143,10 @@ def test_all_response_code_have_version(self):
137143
Response.Code.is_supported_by_standard(code, 2020)
138144

139145
# Case of known code introduced in 2020
140-
self.assertFalse(Response.Code.is_supported_by_standard(Response.Code.ResourceTemporarilyNotAvailable , 2006))
141-
self.assertFalse(Response.Code.is_supported_by_standard(Response.Code.ResourceTemporarilyNotAvailable , 2013))
142-
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.ResourceTemporarilyNotAvailable , 2020))
146+
self.assertFalse(Response.Code.is_supported_by_standard(Response.Code.ResourceTemporarilyNotAvailable, 2006))
147+
self.assertFalse(Response.Code.is_supported_by_standard(Response.Code.ResourceTemporarilyNotAvailable, 2013))
148+
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.ResourceTemporarilyNotAvailable, 2020))
143149

144-
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.GeneralReject , 2006))
145-
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.GeneralReject , 2013))
146-
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.GeneralReject , 2020))
150+
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.GeneralReject, 2006))
151+
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.GeneralReject, 2013))
152+
self.assertTrue(Response.Code.is_supported_by_standard(Response.Code.GeneralReject, 2020))

udsoncan/BaseService.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def response_id(cls) -> int:
5353

5454
@staticmethod
5555
def __get_all_subclasses(cls) -> List[Type["BaseService"]]:
56+
import udsoncan.services # This import is required for __subclasses__ to have a value. Python never import twice the same package.
57+
5658
# this gets all subclasses and returns a list where the "most original" subclasses are listed in front of the other subclasses
5759
# This enables subclasses of any BaseService outside of udsoncan.services to be available, enabling specialization of calls in
5860
# cases where a CAN message is similar to one found in official UDS documentation but has a different service ID

udsoncan/services/AccessTimingParameter.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from udsoncan import Request, Response
1+
from udsoncan.Request import Request
2+
from udsoncan.Response import Response
23
from udsoncan.exceptions import *
34
from udsoncan.BaseService import BaseService, BaseSubfunction, BaseResponseData
45
from udsoncan.ResponseCode import ResponseCode

udsoncan/services/ClearDiagnosticInformation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import struct
2-
from udsoncan import Request, Response, latest_standard
2+
from udsoncan import latest_standard
3+
from udsoncan.Request import Request
4+
from udsoncan.Response import Response
35
from udsoncan.exceptions import *
46
from udsoncan.BaseService import BaseService, BaseResponseData
57
from udsoncan.ResponseCode import ResponseCode

udsoncan/services/CommunicationControl.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from udsoncan import Request, Response, CommunicationType
1+
from udsoncan.Request import Request
2+
from udsoncan.Response import Response
3+
from udsoncan import CommunicationType
24
from udsoncan.exceptions import *
35
from udsoncan.BaseService import BaseService, BaseSubfunction, BaseResponseData
46
from udsoncan.ResponseCode import ResponseCode
@@ -85,7 +87,7 @@ def interpret_response(cls, response: Response) -> InterpretedResponse:
8587
"""
8688
if response.data is None:
8789
raise InvalidResponseException(response, "No data in response")
88-
90+
8991
if len(response.data) < 1:
9092
raise InvalidResponseException(response, "Response data must be at least 1 byte")
9193

udsoncan/services/ControlDTCSetting.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from udsoncan import Request, Response
1+
from udsoncan.Request import Request
2+
from udsoncan.Response import Response
23
from udsoncan.exceptions import *
34
from udsoncan.BaseService import BaseService, BaseSubfunction, BaseResponseData
45
from udsoncan.ResponseCode import ResponseCode

udsoncan/services/DiagnosticSessionControl.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import struct
2-
from udsoncan import Request, Response, latest_standard
2+
from udsoncan.Request import Request
3+
from udsoncan.Response import Response
4+
from udsoncan import latest_standard
35
from udsoncan.exceptions import *
46
from udsoncan.BaseService import BaseService, BaseSubfunction, BaseResponseData
57
from udsoncan.ResponseCode import ResponseCode

udsoncan/services/DynamicallyDefineDataIdentifier.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import struct
2-
from udsoncan import Request, Response, DynamicDidDefinition
2+
from udsoncan import DynamicDidDefinition
3+
from udsoncan.Request import Request
4+
from udsoncan.Response import Response
35
from udsoncan.exceptions import *
46
from udsoncan.BaseService import BaseService, BaseSubfunction, BaseResponseData
57
from udsoncan.ResponseCode import ResponseCode

udsoncan/services/ECUReset.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from udsoncan import Request, Response
1+
from udsoncan.Request import Request
2+
from udsoncan.Response import Response
23
from udsoncan.exceptions import *
34
from udsoncan.BaseService import BaseService, BaseSubfunction, BaseResponseData
45
from udsoncan.ResponseCode import ResponseCode

udsoncan/services/InputOutputControlByIdentifier.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import struct
22
import math
3-
4-
from udsoncan import Request, Response, IOMasks, IOValues, DidCodec, CodecDefinition, IOConfig, IOConfigEntry
3+
from udsoncan.Request import Request
4+
from udsoncan.Response import Response
5+
from udsoncan import IOMasks, IOValues, DidCodec, CodecDefinition, IOConfig, IOConfigEntry
56
from udsoncan.exceptions import *
67
from udsoncan.BaseService import BaseService, BaseSubfunction, BaseResponseData
78
from udsoncan.ResponseCode import ResponseCode

0 commit comments

Comments
 (0)