# gmlan unit tests
#
# Type the following command to launch start the tests:
# $ sudo bash test/run_tests -t test/gmlan.uts -F

% gmlan unit tests

+ Configuration of scapy
= Load gmlan layer
~ conf

load_contrib("automotive.ecu", globals_dict=globals())
load_contrib("automotive.gm.gmlan", globals_dict=globals())

from scapy.contrib.automotive.gm.gmlan_ecu_states import *
from scapy.contrib.automotive.gm.gmlan_logging import *

+ Basic Packet Tests()
= Set GMLAN ECU AddressingScheme

conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
assert conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] == 2

= Craft Packet
x = GMLAN(b'\x52\x02\x01\x16\x71\x00\x00\x0c\xaa\xbb')
x.load == b'\x00\x0c\xaa\xbb'
x.service == 0x52

= Craft VIN Packet
x = GMLAN(b'\x5a\x90'+ raw(b"WOOOJBF35W1042000"))
x.load == b'WOOOJBF35W1042000'
x.dataIdentifier == 0x90

= Test Packet with ECU AddressingScheme2
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22')
x.memoryAddress == 0x1122
x.memorySize == 0x4422

= Test Packet GMLAN_RMBAPR with ECU AddressingScheme2
y = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22')
y.memoryAddress == 0x1122
y.dataRecord == b'\x44\x22'
y.answers(x) == True

= Craft Packet with ECU AddressingScheme2
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22')
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x1122, memorySize=0x4422)
bytes(x) == bytes(y)

= Test Packet with ECU AddressingScheme3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11')
x.memoryAddress == 0x112244
x.memorySize == 0x2211

= Test Packet GMLAN_RMBAPR with ECU AddressingScheme3
y = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11')
y.memoryAddress == 0x112244
y.dataRecord == b'\x22\x11'
y.answers(x) == True

= Craft Packet with ECU AddressingScheme3
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11')
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x112244, memorySize=0x2211)
bytes(x) == bytes(y)

= Test Packet with ECU AddressingScheme4
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00')
x.memoryAddress == 0x11224422
x.memorySize == 0x1100

= Test Packet GMLAN_RMBAPR with ECU AddressingScheme4
y = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11\x00')
y.memoryAddress == 0x11224422
y.dataRecord == b'\x11\x00'
y.answers(x) == True

= Craft Packet with ECU AddressingScheme4
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00')
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x11224422, memorySize=0x1100)
bytes(x) == bytes(y)

= Craft Packet for RequestDownload2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x34\x12\x08\x15')
x.service == 0x34
x.dataFormatIdentifier == 0x12
x.memorySize == 0x815

y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x815)
bytes(y) == bytes(x)

= Craft Packet for RequestDownload3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x34\x12\x08\x15\x00')
x.service == 0x34
x.dataFormatIdentifier == 0x12
x.memorySize == 0x81500

y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x81500)
bytes(y) == bytes(x)

= Craft Packet for RequestDownload4
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x34\x12\x08\x15\x00\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x12
x.memorySize == 0x8150011

= Craft Packet for RFRD1
a = GMLAN(b'\x12\x01')
a.service == 0x12
a.subfunction == 1

= Craft Packet for RFRD2
b = GMLAN(b'\x12\x02\x01\x02\x03\x04')
b.service == 0x12
b.subfunction == 2
b.dtc.failureRecordNumber == 1
b.dtc.DTCHighByte == 2
b.dtc.DTCLowByte == 3
b.dtc.DTCFailureType == 4

= Craft Packet for RFRDPR_RFRI
x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04')
x.service == 0x52
x.subfunction == 1
x.failureRecordDataStructureIdentifier == 0
x.dtcs[0].failureRecordNumber == 1
x.dtcs[0].DTCHighByte == 2
x.dtcs[0].DTCLowByte == 3
x.dtcs[0].DTCFailureType == 4
x.answers(a) == True

= Craft Packet for RFRDPR_RFRI
x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04')
x.service == 0x52
x.subfunction == 1
x.failureRecordDataStructureIdentifier == 0
x.dtcs[0].failureRecordNumber == 1
x.dtcs[0].DTCHighByte == 2
x.dtcs[0].DTCLowByte == 3
x.dtcs[0].DTCFailureType == 4
x.dtcs[1].failureRecordNumber == 1
x.dtcs[1].DTCHighByte == 2
x.dtcs[1].DTCLowByte == 3
x.dtcs[1].DTCFailureType == 4
x.dtcs[2].failureRecordNumber == 1
x.dtcs[2].DTCHighByte == 2
x.dtcs[2].DTCLowByte == 3
x.dtcs[2].DTCFailureType == 4
x.dtcs[3].failureRecordNumber == 1
x.dtcs[3].DTCHighByte == 2
x.dtcs[3].DTCLowByte == 3
x.dtcs[3].DTCFailureType == 4
x.answers(a) == True

= Craft Packet for RFRDPR_RFRP
x = GMLAN(b'\x52\x02\x01\x02\x03\x04deadbeef')
x.service == 0x52
x.subfunction == 2
x.dtc.failureRecordNumber == 1
x.dtc.DTCHighByte == 2
x.dtc.DTCLowByte == 3
x.dtc.DTCFailureType == 4
x.show()
x.load == b'deadbeef'
x.answers(b) == True


= Craft Packet for RDBI
x = GMLAN(b'\x1A\x11')
x.service == 0x1A
x.dataIdentifier == 0x11

= Craft Packet for RDBIPR
y = GMLAN(b'\x5A\x11deadbeef')
y.service == 0x5A
y.dataIdentifier == 0x11
y.load == b'deadbeef'
y.answers(x) == True


= Craft Packet for RDBPI
x = GMLAN(b'\x22\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88\x99\x99')
x.service == 0x22
x.identifiers[0] == 0x1111
x.identifiers[1] == 0x2222
x.identifiers[2] == 0x3333
x.identifiers[3] == 0x4444
x.identifiers[4] == 0x5555
x.identifiers[5] == 0x6666
x.identifiers[6] == 0x7777
x.identifiers[7] == 0x8888
x.identifiers[8] == 0x9999

= Craft Packet for RDBPIPR
y = GMLAN(b'\x62\x11\x11deadbeef')
y.service == 0x62
y.parameterIdentifier == 0x1111
y.load == b'deadbeef'
y.answers(x) == True

= Craft Packet for GMLAN_RDBPKTI1
x = GMLAN(b'\xAA\x01deadbeef')
x.service == 0xAA
x.subfunction == 0x01
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]

= Craft Packet for GMLAN_RDBPKTI3
x = GMLAN(b'\xAA\x02deadbeef')
x.service == 0xAA
x.subfunction == 0x02
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]

= Craft Packet for GMLAN_RDBPKTI4
x = GMLAN(b'\xAA\x03deadbeef')
x.service == 0xAA
x.subfunction == 0x03
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]

= Craft Packet for GMLAN_RDBPKTI2
x = GMLAN(b'\xAA\x00')
x.service == 0xAA
x.subfunction == 0

= Build GMLAN_RDBPKTI1
x = GMLAN()/GMLAN_RDBPKTI(subfunction=1, request_DPIDs=[0x64, 0x65])
assert b"\xaa\x01de" == bytes(x)

= Craft Packet for GMLAN_SA1
a = GMLAN(b'\x27\x01')
a.service == 0x27
a.subfunction == 1

= Craft Packet for GMLAN_SA2
b = GMLAN(b'\x27\x02\xde\xad')
b.service == 0x27
b.subfunction == 2
b.securityKey == 0xdead

= Craft Packet for GMLAN_SAPR1
x = GMLAN(b'\x67\x02')
x.service == 0x67
x.subfunction == 2
x.answers(b)

ecu = Ecu()
ecu.update(b)
ecu.update(x)
assert ecu.state.security_level == 2


= Craft Packet for GMLAN_SAPR2
x = GMLAN(b'\x67\x01\xde\xad')
x.service == 0x67
x.subfunction == 1
x.securitySeed == 0xdead
x.answers(a)

= Craft Packet for GMLAN_DDM
x = GMLAN(b'\x2c\x02dead')
x.service == 0x2c
x.DPIDIdentifier == 2
x.PIDData == b'dead'

= Craft Packet for GMLAN_DDMPR
y = GMLAN(b'\x6c\x02dead')
y.service == 0x6c
y.DPIDIdentifier == 2
y.answers(x)

= Craft Packet for GMLAN_DPBA1
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x2D\x02\x02\x11\x11\x33')
x.service == 0x2d
x.parameterIdentifier == 0x202
x.memoryAddress == 0x1111
x.memorySize == 0x33

= Craft Packet for GMLAN_DPBA2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x33')
x.service == 0x2d
x.parameterIdentifier == 0x202
x.memoryAddress == 0x111111
x.memorySize == 0x33

= Craft Packet for GMLAN_DPBA3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x11\x33')
x.service == 0x2d
x.parameterIdentifier == 0x202
x.memoryAddress == 0x11111111
x.memorySize == 0x33

= Craft Packet for GMLAN_DPBAPR
y = GMLAN(b'\x6D\x02\x02')
y.service == 0x6d
y.parameterIdentifier == 0x202
y.answers(x)

= Craft Packet for GMLAN_RD1
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x34\x02\x11\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x2
x.memorySize == 0x1111

= Craft Packet for GMLAN_RD2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x34\x02\x11\x11\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x2
x.memorySize == 0x111111

= Craft Packet for GMLAN_RD3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x34\x02\x11\x11\x11\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x2
x.memorySize == 0x11111111

= Craft Packet for GMLAN_TD1
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x36\x02\x11\x11dead')
x.service == 0x36
x.subfunction == 0x2
x.startingAddress == 0x1111
x.dataRecord == b'dead'

= Craft Packet for GMLAN_TD2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x36\x02\x11\x11\x11dead')
x.service == 0x36
x.subfunction == 0x2
x.startingAddress == 0x111111
x.dataRecord == b'dead'

= Craft Packet for GMLAN_TD3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x36\x02\x11\x11\x11\x11dead')
x.service == 0x36
x.subfunction == 0x2
x.startingAddress == 0x11111111
x.dataRecord == b'dead'

= Craft Packet for WDBI
x = GMLAN(b'\x3b\x11deadbeef')
x.service == 0x3b
x.dataIdentifier == 0x11
x.dataRecord == b'deadbeef'

= Craft Packet for WDBIPR
y = GMLAN(b'\x7b\x11')
y.service == 0x7b
y.dataIdentifier == 0x11
y.answers(x)

= Craft Packet for RPSPR
x = GMLAN(b'\xe2\x11')
x.service == 0xe2
x.programmedState == 0x11

= Craft Packet for PM
x = GMLAN(b'\xA5\x11')
x.service == 0xA5
x.subfunction == 0x11

= Craft Packet for RDI
x = GMLAN(b'\xA9\x11')
x.service == 0xA9
x.subfunction == 0x11

= Craft Packet for RDI_BN
x = GMLAN(b'\xA9\x80\x11\x22\x33')
x.service == 0xA9
x.subfunction == 0x80
x.DTCHighByte == 0x11
x.DTCLowByte == 0x22
x.DTCFailureType == 0x33

= Craft Packet for RDI_BM1
x = GMLAN(b'\xA9\x81\x11')
x.service == 0xA9
x.subfunction == 0x81
x.DTCStatusMask == 0x11

= Craft Packet for RDI_BM2
x = GMLAN(b'\xA9\x82\x11')
x.service == 0xA9
x.subfunction == 0x82
x.DTCStatusMask == 0x11

= Craft Packet for NR
x = GMLAN(b'\x7f\x11\x00\x11\x22')
x.service == 0x7f
x.requestServiceId == 0x11
x.returnCode == 0
x.deviceControlLimitExceeded == 0x1122

= Check not answers
y = GMLAN(b'\x11deadbeef')
x = GMLAN(b'\x7f\x10\x00\x11\x22')
assert not x.answers(y)

= Check answers 1
y = GMLAN(b'\x10deadbeef')
x = GMLAN(b'\x7f\x10\x00\x11\x22')
assert x.answers(y)

= Set treat-response-pending-as-answer
conf.contribs['GMLAN']['treat-response-pending-as-answer'] = False
assert conf.contribs['GMLAN']['treat-response-pending-as-answer'] == False

= Check response-pending is not considered as answer
y = GMLAN(b'\x10deadbeef')
x = GMLAN(b'\x7f\x10\x78\x11\x22')
assert not x.answers(y)

= Check response-pending is considered as answer
conf.contribs['GMLAN']['treat-response-pending-as-answer'] = True
assert conf.contribs['GMLAN']['treat-response-pending-as-answer'] == True
y = GMLAN(b'\x10deadbeef')
x = GMLAN(b'\x7f\x10\x78\x11\x22')
assert x.answers(y)

= Check hashret 1
print(y.hashret())
print(x.hashret())

y.hashret() == x.hashret()

= Check answers 2
y = GMLAN()/GMLAN_SA(subfunction=1)
x = GMLAN()/GMLAN_SAPR(subfunction=1)
assert x.answers(y)

= Check hashret 2
y.hashret() == x.hashret()

= Check modifies ecu state
ecu = Ecu()
ecu.update(GMLAN(service="InitiateDiagnosticOperation"))
ecu.update(GMLAN(service="InitiateDiagnosticOperationPositiveResponse"))
assert ecu.state.session == 3
ecu.update(GMLAN(service="ReturnToNormalOperation"))
ecu.update(GMLAN(service="ReturnToNormalOperationPositiveResponse"))
assert ecu.state.session == 1
ecu.update(GMLAN(service="ProgrammingMode"))
ecu.update(GMLAN(service="ProgrammingModePositiveResponse"))
assert ecu.state.session == 2
ecu.update(GMLAN(service="DisableNormalCommunication"))
ecu.update(GMLAN(service="DisableNormalCommunicationPositiveResponse"))
assert ecu.state.communication_control == 1
ecu.update(GMLAN(service="ReturnToNormalOperation"))
ecu.update(GMLAN(service="ReturnToNormalOperationPositiveResponse"))
assert ecu.state.session == 1

= Craft GMLAN_DC

req = GMLAN()/GMLAN_DC(CPIDNumber=0x11, CPIDControlBytes=b"\xbe\xefabc")
assert bytes(req) == b"\xAE\x11\xbe\xefabc"

req2 = GMLAN()/GMLAN_DC(CPIDNumber=0x12)
assert bytes(req2) == b"\xAE\x12\x00\x00\x00\x00\x00"

resp = GMLAN()/GMLAN_DCPR(CPIDNumber=0x11)
assert bytes(resp) == b"\xEE\x11"


assert resp.answers(req)
assert not resp.answers(req2)

= Dissect test GMLAN_DC

req = GMLAN(b"\xAE\x14caffe")
assert req.service == 0xAE
assert req.CPIDNumber == 20
assert req.CPIDControlBytes == b"caffe"

resp = GMLAN(b"\xEE\x14")
assert resp.service == 0xEE
assert resp.CPIDNumber == 20
assert resp.answers(req)
assert resp.hashret() == req.hashret()

= Logging tests


def get_log(pkt):
    for layer in pkt.layers():
        if not hasattr(layer, "get_log"):
            continue
        try:
            return layer.get_log(pkt)
        except TypeError:
            return layer.get_log.im_func(pkt)

pkt = GMLAN()/GMLAN_RFRD(subfunction=1)
log = get_log(pkt)
assert len(log) == 2
assert log[1] == "readFailureRecordIdentifiers"
assert log[0] == "ReadFailureRecordData"

pkt = GMLAN()/GMLAN_RFRDPR(subfunction=1)
log = get_log(pkt)
assert len(log) == 2
assert log[1] == "readFailureRecordIdentifiers"
assert log[0] == "ReadFailureRecordDataPositiveResponse"

pkt = GMLAN()/GMLAN_RDBPI(identifiers=[5])
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == '[OBD_EngineCoolantTemperature]'
assert log[0] == "ReadDataByParameterIdentifier"

pkt = GMLAN()/GMLAN_RDBPIPR(parameterIdentifier=5)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == 'OBD_EngineCoolantTemperature'
assert log[0] == "ReadDataByParameterIdentifierPositiveResponse"


pkt = GMLAN()/GMLAN_RDBPKTI(subfunction=0)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == 'stopSending'
assert log[0] == "ReadDataByPacketIdentifier"

pkt = GMLAN()/GMLAN_RMBA(memoryAddress=0)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == '0x0'
assert log[0] == "ReadMemoryByAddress"

pkt = GMLAN()/GMLAN_RMBAPR(memoryAddress=0, dataRecord=b"deadbeef")
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1][0] == '0x0'
assert log[1][1] == b'deadbeef'
assert log[0] == "ReadMemoryByAddressPositiveResponse"

pkt = GMLAN()/GMLAN_DDM(DPIDIdentifier=0, PIDData=b"deadbeef")
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1][0] == '0x0'
assert log[1][1] == b'deadbeef'
assert log[0] == "DynamicallyDefineMessage"

pkt = GMLAN()/GMLAN_DDMPR(DPIDIdentifier=0)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == '0x0'
assert log[0] == "DynamicallyDefineMessagePositiveResponse"

pkt = GMLAN()/GMLAN_DPBA(parameterIdentifier=0, memoryAddress=1, memorySize=3)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1][0] == 0
assert log[1][1] == 1
assert log[1][2] == 3
assert log[0] == "DefinePIDByAddress"

pkt = GMLAN()/GMLAN_DPBAPR(parameterIdentifier=0)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == 0
assert log[0] == "DefinePIDByAddressPositiveResponse"

pkt = GMLAN()/GMLAN_WDBI(dataIdentifier=0, dataRecord=b"deadbeef")
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1][0] == "0x0"
assert log[1][1] == b"deadbeef"
assert log[0] == "WriteDataByIdentifier"

pkt = GMLAN()/GMLAN_WDBIPR(dataIdentifier=0)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == "0x0"
assert log[0] == "WriteDataByIdentifierPositiveResponse"

pkt = GMLAN()/GMLAN_RDI(subfunction=0x80)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == "readStatusOfDTCByDTCNumber"
assert log[0] == "ReadDiagnosticInformation"

pkt = GMLAN()/GMLAN_DC(CPIDNumber=0x80)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == "0x80"
assert log[0] == "DeviceControl"

pkt = GMLAN()/GMLAN_DCPR(CPIDNumber=0x80)
log = get_log(pkt)
print(log)
assert len(log) == 2
assert log[1] == "0x80"
assert log[0] == "DeviceControlPositiveResponse"