1484 lines
57 KiB
Python
1484 lines
57 KiB
Python
#!/usr/bin/python
|
|
|
|
# Copyright (c) 2005-2013, Alexander Belchenko
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms,
|
|
# with or without modification, are permitted provided
|
|
# that the following conditions are met:
|
|
#
|
|
# * Redistributions of source code must retain
|
|
# the above copyright notice, this list of conditions
|
|
# and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce
|
|
# the above copyright notice, this list of conditions
|
|
# and the following disclaimer in the documentation
|
|
# and/or other materials provided with the distribution.
|
|
# * Neither the name of the author nor the names
|
|
# of its contributors may be used to endorse
|
|
# or promote products derived from this software
|
|
# without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
|
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
|
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
"""Test suite for IntelHex class."""
|
|
|
|
import array
|
|
from cStringIO import StringIO
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
from compat import asbytes, asstr
|
|
import intelhex
|
|
from intelhex import IntelHex, \
|
|
IntelHexError, \
|
|
HexReaderError, \
|
|
AddressOverlapError, \
|
|
HexRecordError, \
|
|
RecordLengthError, \
|
|
RecordTypeError, \
|
|
RecordChecksumError, \
|
|
EOFRecordError, \
|
|
ExtendedSegmentAddressRecordError, \
|
|
ExtendedLinearAddressRecordError, \
|
|
StartSegmentAddressRecordError, \
|
|
StartLinearAddressRecordError, \
|
|
DuplicateStartAddressRecordError, \
|
|
InvalidStartAddressValueError, \
|
|
_EndOfFile, \
|
|
BadAccess16bit, \
|
|
hex2bin, \
|
|
Record
|
|
|
|
|
|
__docformat__ = 'restructuredtext'
|
|
|
|
##
|
|
# Data for tests
|
|
|
|
hex8 = '''\
|
|
:1004E300CFF0FBE2FDF220FF20F2E120E2FBE6F396
|
|
:1004F3000A00FDE0E1E2E3B4E4E5BAE6E7B3BFE80E
|
|
:10050300E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8E0
|
|
:10051300F9FCFEFF00C0C1C2C3A5C4C5AAC6C7B2C9
|
|
:10052300AFC8C9CACBCCCDCECFD0D1D2D3D4D5D6F8
|
|
:07053300D7D8D9DCDEDF00A0
|
|
:10053A0078227C007D007BFF7A0479F57E007F2398
|
|
:10054A0012042F78457C007D007BFF7A0579187E9E
|
|
:10055A00007F2212042F759850438920758DDDD2B1
|
|
:10056A008ED2996390017BFF7A0479E31200658049
|
|
:01057A00FE82
|
|
:030000000205A254
|
|
:0C05A200787FE4F6D8FD75817A02053AF6
|
|
:10035F00E709F608DFFA8046E709F208DFFA803E80
|
|
:10036F0088828C83E709F0A3DFFA8032E309F6086D
|
|
:10037F00DFFA8078E309F208DFFA807088828C83D5
|
|
:10038F00E309F0A3DFFA806489828A83E0A3F60889
|
|
:10039F00DFFA805889828A83E0A3F208DFFA804C63
|
|
:1003AF0080D280FA80C680D4806980F2803380103A
|
|
:1003BF0080A680EA809A80A880DA80E280CA8033A3
|
|
:1003CF0089828A83ECFAE493A3C8C582C8CCC5831B
|
|
:1003DF00CCF0A3C8C582C8CCC583CCDFE9DEE780EB
|
|
:1003EF000D89828A83E493A3F608DFF9ECFAA9F06A
|
|
:1003FF00EDFB2289828A83ECFAE0A3C8C582C8CCC0
|
|
:10040F00C583CCF0A3C8C582C8CCC583CCDFEADED8
|
|
:10041F00E880DB89828A83E493A3F208DFF980CC3A
|
|
:10042F0088F0EF60010E4E60C388F0ED2402B40433
|
|
:10043F000050B9F582EB2402B4040050AF232345DA
|
|
:06044F0082239003AF734D
|
|
:10000300E576246AF8E60576227867300702786A8F
|
|
:10001300E475F0011204AD0204552000EB7F2ED2EB
|
|
:10002300008018EF540F2490D43440D4FF30040BD5
|
|
:10003300EF24BFB41A0050032461FFE57760021573
|
|
:1000430077057AE57A7002057930070D7867E475EC
|
|
:10005300F0011204ADEF02049B02057B7403D20787
|
|
:100063008003E4C207F5768B678A688969E4F577CC
|
|
:10007300F579F57AE57760077F2012003E80F57504
|
|
:1000830078FFC201C200C202C203C205C206C2088F
|
|
:1000930012000CFF700D3007057F0012004FAF7A7E
|
|
:1000A300AE7922B4255FC2D5C20412000CFF24D05E
|
|
:1000B300B40A00501A75F00A787730D50508B6FFF0
|
|
:1000C3000106C6A426F620D5047002D20380D924E3
|
|
:1000D300CFB41A00EF5004C2E5D20402024FD2019A
|
|
:1000E30080C6D20080C0D20280BCD2D580BAD205ED
|
|
:1000F30080B47F2012003E2002077401B5770040D0
|
|
:10010300F1120003FF12003E020077D208D20680EC
|
|
:1001130095120003FB120003FA120003F94A4B7015
|
|
:100123000679207A037BFF20022EE577602A7E0082
|
|
:100133008E8275830012046E60060EEE657870F091
|
|
:10014300C2D5EBC0E0EAC0E0E9C0E0EE120296D00F
|
|
:10015300E0F9D0E0FAD0E0FB120455FF60AAEBC04F
|
|
:10016300E0EAC0E0E9C0E012003ED0E02401F9D0AB
|
|
:10017300E03400FAD0E0FBE5780460DCD578D98080
|
|
:10018300877BFF7A027992D202809C791080027970
|
|
:1001930008C206C2088008D2D5790A8004790AC247
|
|
:1001A300D5E578047002F578E4FAFDFEFF1200034A
|
|
:1001B300FC7B08200113120003FD7B1030000A12A0
|
|
:1001C3000003FE120003FF7B20EC3382D592D5504F
|
|
:1001D30013C3E43000069FFFE49EFEE42001039D69
|
|
:1001E300FDE49CFCE4CBF8C201EC700CCFCECDCC8B
|
|
:1001F300E824F8F870F38017C3EF33FFEE33FEED16
|
|
:1002030033FDEC33FCEB33FB994002FB0FD8E9EBF6
|
|
:10021300300105F8D0E0C448B201C0E00AEC4D4E0D
|
|
:100223004F78207B0070C2EAB5780040BCC0E01272
|
|
:100233000298D0F0D0E0200104C4C0E0C4B201C0F1
|
|
:10024300F0120027D0F0D5F0EB0200771204BD01C5
|
|
:100253001453018E5800E54C00E14201924F019A7C
|
|
:0F02630044019A4900FA4301A0550184460184E1
|
|
:100272004501844703405000E92D00ED2E01102B6B
|
|
:1002820000F123010E2003292A00A94800000108D9
|
|
:100292003F3F3F00790AA2D5200314300509B91067
|
|
:1002A200020404B9080104A2D52006025001042068
|
|
:1002B20002689202B577005034C0E07F2030031903
|
|
:1002C2007F30A20272067205500F1202EFC202C202
|
|
:1002D20006C205C2087F30800F300503E9C0E01274
|
|
:1002E200003E300503D0E0F9D0E0B577CC300517F9
|
|
:1002F2007F30B9100C12003E7F583004077F78809F
|
|
:1003020003B9080312003E3002057F2D02003E7F32
|
|
:10031200202008F87F2B2006F322920280CF286E3D
|
|
:10032200756C6C2900D2011200033001F8C2017809
|
|
:100332007730D50108F60200A92D50434958120022
|
|
:10034200032403B405004001E490033B9312002F01
|
|
:0D035200743A12002FD20375770402018E59
|
|
:10045500BB010689828A83E0225002E722BBFE02A5
|
|
:09046500E32289828A83E49322D8
|
|
:10046E00BB010CE58229F582E5833AF583E0225043
|
|
:10047E0006E92582F8E622BBFE06E92582F8E2228D
|
|
:0D048E00E58229F582E5833AF583E49322A7
|
|
:10049B00BB010689828A83F0225002F722BBFE0140
|
|
:0204AB00F3223A
|
|
:1004AD00FAE6FB0808E6F925F0F618E6CA3AF62250
|
|
:1004BD00D083D082F8E4937012740193700DA3A3CE
|
|
:1004CD0093F8740193F5828883E4737402936860E2
|
|
:0604DD00EFA3A3A380DFE2
|
|
:10057B00EFB40A07740D120586740A309811A89906
|
|
:10058B00B8130CC2983098FDA899C298B811F630E0
|
|
:07059B0099FDC299F59922B8
|
|
:00000001FF
|
|
'''
|
|
bin8 = array.array('B',[2, 5, 162, 229, 118, 36, 106, 248, 230, 5, 118, 34,
|
|
120, 103, 48, 7, 2, 120, 106, 228, 117, 240, 1, 18,
|
|
4, 173, 2, 4, 85, 32, 0, 235, 127, 46, 210, 0, 128,
|
|
24, 239, 84, 15, 36, 144, 212, 52, 64, 212, 255, 48,
|
|
4, 11, 239, 36, 191, 180, 26, 0, 80, 3, 36, 97, 255,
|
|
229, 119, 96, 2, 21, 119, 5, 122, 229, 122, 112, 2,
|
|
5, 121, 48, 7, 13, 120, 103, 228, 117, 240, 1, 18,
|
|
4, 173, 239, 2, 4, 155, 2, 5, 123, 116, 3, 210, 7,
|
|
128, 3, 228, 194, 7, 245, 118, 139, 103, 138, 104,
|
|
137, 105, 228, 245, 119, 245, 121, 245, 122, 229,
|
|
119, 96, 7, 127, 32, 18, 0, 62, 128, 245, 117, 120,
|
|
255, 194, 1, 194, 0, 194, 2, 194, 3, 194, 5, 194, 6,
|
|
194, 8, 18, 0, 12, 255, 112, 13, 48, 7, 5, 127, 0,
|
|
18, 0, 79, 175, 122, 174, 121, 34, 180, 37, 95, 194,
|
|
213, 194, 4, 18, 0, 12, 255, 36, 208, 180, 10, 0, 80,
|
|
26, 117, 240, 10, 120, 119, 48, 213, 5, 8, 182, 255,
|
|
1, 6, 198, 164, 38, 246, 32, 213, 4, 112, 2, 210, 3,
|
|
128, 217, 36, 207, 180, 26, 0, 239, 80, 4, 194, 229,
|
|
210, 4, 2, 2, 79, 210, 1, 128, 198, 210, 0, 128, 192,
|
|
210, 2, 128, 188, 210, 213, 128, 186, 210, 5, 128,
|
|
180, 127, 32, 18, 0, 62, 32, 2, 7, 116, 1, 181, 119,
|
|
0, 64, 241, 18, 0, 3, 255, 18, 0, 62, 2, 0, 119, 210,
|
|
8, 210, 6, 128, 149, 18, 0, 3, 251, 18, 0, 3, 250,
|
|
18, 0, 3, 249, 74, 75, 112, 6, 121, 32, 122, 3, 123,
|
|
255, 32, 2, 46, 229, 119, 96, 42, 126, 0, 142, 130,
|
|
117, 131, 0, 18, 4, 110, 96, 6, 14, 238, 101, 120,
|
|
112, 240, 194, 213, 235, 192, 224, 234, 192, 224,
|
|
233, 192, 224, 238, 18, 2, 150, 208, 224, 249, 208,
|
|
224, 250, 208, 224, 251, 18, 4, 85, 255, 96, 170,
|
|
235, 192, 224, 234, 192, 224, 233, 192, 224, 18, 0,
|
|
62, 208, 224, 36, 1, 249, 208, 224, 52, 0, 250, 208,
|
|
224, 251, 229, 120, 4, 96, 220, 213, 120, 217, 128,
|
|
135, 123, 255, 122, 2, 121, 146, 210, 2, 128, 156,
|
|
121, 16, 128, 2, 121, 8, 194, 6, 194, 8, 128, 8, 210,
|
|
213, 121, 10, 128, 4, 121, 10, 194, 213, 229, 120, 4,
|
|
112, 2, 245, 120, 228, 250, 253, 254, 255, 18, 0, 3,
|
|
252, 123, 8, 32, 1, 19, 18, 0, 3, 253, 123, 16, 48,
|
|
0, 10, 18, 0, 3, 254, 18, 0, 3, 255, 123, 32, 236,
|
|
51, 130, 213, 146, 213, 80, 19, 195, 228, 48, 0, 6,
|
|
159, 255, 228, 158, 254, 228, 32, 1, 3, 157, 253,
|
|
228, 156, 252, 228, 203, 248, 194, 1, 236, 112, 12,
|
|
207, 206, 205, 204, 232, 36, 248, 248, 112, 243, 128,
|
|
23, 195, 239, 51, 255, 238, 51, 254, 237, 51, 253,
|
|
236, 51, 252, 235, 51, 251, 153, 64, 2, 251, 15, 216,
|
|
233, 235, 48, 1, 5, 248, 208, 224, 196, 72, 178, 1,
|
|
192, 224, 10, 236, 77, 78, 79, 120, 32, 123, 0, 112,
|
|
194, 234, 181, 120, 0, 64, 188, 192, 224, 18, 2, 152,
|
|
208, 240, 208, 224, 32, 1, 4, 196, 192, 224, 196,
|
|
178, 1, 192, 240, 18, 0, 39, 208, 240, 213, 240, 235,
|
|
2, 0, 119, 18, 4, 189, 1, 20, 83, 1, 142, 88, 0, 229,
|
|
76, 0, 225, 66, 1, 146, 79, 1, 154, 68, 1, 154, 73,
|
|
0, 250, 67, 1, 160, 85, 1, 132, 70, 1, 132, 69, 1,
|
|
132, 71, 3, 64, 80, 0, 233, 45, 0, 237, 46, 1, 16,
|
|
43, 0, 241, 35, 1, 14, 32, 3, 41, 42, 0, 169, 72, 0,
|
|
0, 1, 8, 63, 63, 63, 0, 121, 10, 162, 213, 32, 3, 20,
|
|
48, 5, 9, 185, 16, 2, 4, 4, 185, 8, 1, 4, 162, 213,
|
|
32, 6, 2, 80, 1, 4, 32, 2, 104, 146, 2, 181, 119, 0,
|
|
80, 52, 192, 224, 127, 32, 48, 3, 25, 127, 48, 162,
|
|
2, 114, 6, 114, 5, 80, 15, 18, 2, 239, 194, 2, 194,
|
|
6, 194, 5, 194, 8, 127, 48, 128, 15, 48, 5, 3, 233,
|
|
192, 224, 18, 0, 62, 48, 5, 3, 208, 224, 249, 208,
|
|
224, 181, 119, 204, 48, 5, 23, 127, 48, 185, 16, 12,
|
|
18, 0, 62, 127, 88, 48, 4, 7, 127, 120, 128, 3, 185,
|
|
8, 3, 18, 0, 62, 48, 2, 5, 127, 45, 2, 0, 62, 127,
|
|
32, 32, 8, 248, 127, 43, 32, 6, 243, 34, 146, 2, 128,
|
|
207, 40, 110, 117, 108, 108, 41, 0, 210, 1, 18, 0, 3,
|
|
48, 1, 248, 194, 1, 120, 119, 48, 213, 1, 8, 246, 2,
|
|
0, 169, 45, 80, 67, 73, 88, 18, 0, 3, 36, 3, 180, 5,
|
|
0, 64, 1, 228, 144, 3, 59, 147, 18, 0, 47, 116, 58,
|
|
18, 0, 47, 210, 3, 117, 119, 4, 2, 1, 142, 231, 9,
|
|
246, 8, 223, 250, 128, 70, 231, 9, 242, 8, 223, 250,
|
|
128, 62, 136, 130, 140, 131, 231, 9, 240, 163, 223,
|
|
250, 128, 50, 227, 9, 246, 8, 223, 250, 128, 120,
|
|
227, 9, 242, 8, 223, 250, 128, 112, 136, 130, 140,
|
|
131, 227, 9, 240, 163, 223, 250, 128, 100, 137,
|
|
130, 138, 131, 224, 163, 246, 8, 223, 250, 128, 88,
|
|
137, 130, 138, 131, 224, 163, 242, 8, 223, 250, 128,
|
|
76, 128, 210, 128, 250, 128, 198, 128, 212, 128, 105,
|
|
128, 242, 128, 51, 128, 16, 128, 166, 128, 234, 128,
|
|
154, 128, 168, 128, 218, 128, 226, 128, 202, 128, 51,
|
|
137, 130, 138, 131, 236, 250, 228, 147, 163, 200,
|
|
197, 130, 200, 204, 197, 131, 204, 240, 163, 200,
|
|
197, 130, 200, 204, 197, 131, 204, 223, 233, 222,
|
|
231, 128, 13, 137, 130, 138, 131, 228, 147, 163, 246,
|
|
8, 223, 249, 236, 250, 169, 240, 237, 251, 34, 137,
|
|
130, 138, 131, 236, 250, 224, 163, 200, 197, 130,
|
|
200, 204, 197, 131, 204, 240, 163, 200, 197, 130,
|
|
200, 204, 197, 131, 204, 223, 234, 222, 232, 128,
|
|
219, 137, 130, 138, 131, 228, 147, 163, 242, 8,
|
|
223, 249, 128, 204, 136, 240, 239, 96, 1, 14, 78,
|
|
96, 195, 136, 240, 237, 36, 2, 180, 4, 0, 80, 185,
|
|
245, 130, 235, 36, 2, 180, 4, 0, 80, 175, 35, 35,
|
|
69, 130, 35, 144, 3, 175, 115, 187, 1, 6, 137, 130,
|
|
138, 131, 224, 34, 80, 2, 231, 34, 187, 254, 2, 227,
|
|
34, 137, 130, 138, 131, 228, 147, 34, 187, 1, 12,
|
|
229, 130, 41, 245, 130, 229, 131, 58, 245, 131, 224,
|
|
34, 80, 6, 233, 37, 130, 248, 230, 34, 187, 254, 6,
|
|
233, 37, 130, 248, 226, 34, 229, 130, 41, 245, 130,
|
|
229, 131, 58, 245, 131, 228, 147, 34, 187, 1, 6,
|
|
137, 130, 138, 131, 240, 34, 80, 2, 247, 34, 187,
|
|
254, 1, 243, 34, 250, 230, 251, 8, 8, 230, 249, 37,
|
|
240, 246, 24, 230, 202, 58, 246, 34, 208, 131, 208,
|
|
130, 248, 228, 147, 112, 18, 116, 1, 147, 112, 13,
|
|
163, 163, 147, 248, 116, 1, 147, 245, 130, 136,
|
|
131, 228, 115, 116, 2, 147, 104, 96, 239, 163, 163,
|
|
163, 128, 223, 207, 240, 251, 226, 253, 242, 32,
|
|
255, 32, 242, 225, 32, 226, 251, 230, 243, 10, 0,
|
|
253, 224, 225, 226, 227, 180, 228, 229, 186, 230,
|
|
231, 179, 191, 232, 233, 234, 235, 236, 237, 238,
|
|
239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
|
|
249, 252, 254, 255, 0, 192, 193, 194, 195, 165, 196,
|
|
197, 170, 198, 199, 178, 175, 200, 201, 202, 203,
|
|
204, 205, 206, 207, 208, 209, 210, 211, 212, 213,
|
|
214, 215, 216, 217, 220, 222, 223, 0, 120, 34, 124,
|
|
0, 125, 0, 123, 255, 122, 4, 121, 245, 126, 0, 127,
|
|
35, 18, 4, 47, 120, 69, 124, 0, 125, 0, 123, 255,
|
|
122, 5, 121, 24, 126, 0, 127, 34, 18, 4, 47, 117,
|
|
152, 80, 67, 137, 32, 117, 141, 221, 210, 142, 210,
|
|
153, 99, 144, 1, 123, 255, 122, 4, 121, 227, 18, 0,
|
|
101, 128, 254, 239, 180, 10, 7, 116, 13, 18, 5, 134,
|
|
116, 10, 48, 152, 17, 168, 153, 184, 19, 12, 194,
|
|
152, 48, 152, 253, 168, 153, 194, 152, 184, 17,
|
|
246, 48, 153, 253, 194, 153, 245, 153, 34, 120, 127,
|
|
228, 246, 216, 253, 117, 129, 122, 2, 5, 58])
|
|
|
|
|
|
hex16 = """:020000040000FA
|
|
:10000000000083120313072055301820042883169C
|
|
:10001000031340309900181598168312031318160D
|
|
:1000200098170800831203138C1E14281A0808005E
|
|
:0C003000831203130C1E1A28990008000C
|
|
:00000001FF
|
|
"""
|
|
bin16 = array.array('H', [0x0000, 0x1283, 0x1303, 0x2007,
|
|
0x3055, 0x2018, 0x2804, 0x1683,
|
|
0x1303, 0x3040, 0x0099, 0x1518,
|
|
0x1698, 0x1283, 0x1303, 0x1618,
|
|
0x1798, 0x0008, 0x1283, 0x1303,
|
|
0x1E8C, 0x2814, 0x081A, 0x0008,
|
|
0x1283, 0x1303, 0x1E0C, 0x281A,
|
|
0x0099, 0x0008, 0x3FFF, 0x3FFF])
|
|
|
|
|
|
hex64k = """:020000040000FA
|
|
:0100000001FE
|
|
:020000040001F9
|
|
:0100000002FD
|
|
:00000001FF
|
|
"""
|
|
data64k = {0: 1, 0x10000: 2}
|
|
|
|
|
|
hex_rectype3 = """:0400000312345678E5
|
|
:0100000001FE
|
|
:00000001FF
|
|
"""
|
|
data_rectype3 = {0: 1}
|
|
start_addr_rectype3 = {'CS': 0x1234, 'IP': 0x5678}
|
|
|
|
|
|
hex_rectype5 = """:0400000512345678E3
|
|
:0100000002FD
|
|
:00000001FF
|
|
"""
|
|
data_rectype5 = {0: 2}
|
|
start_addr_rectype5 = {'EIP': 0x12345678}
|
|
|
|
hex_empty_file = ':00000001FF\n'
|
|
|
|
hex_simple = """\
|
|
:10000000000083120313072055301820042883169C
|
|
:10001000031340309900181598168312031318160D
|
|
:1000200098170800831203138C1E14281A0808005E
|
|
:0C003000831203130C1E1A28990008000C
|
|
:00000001FF
|
|
"""
|
|
|
|
hex_bug_lp_341051 = """\
|
|
:020FEC00E4E738
|
|
:040FF00022E122E1F7
|
|
:00000001FF
|
|
"""
|
|
|
|
|
|
##
|
|
# Test cases
|
|
|
|
class TestIntelHexBase(unittest.TestCase):
|
|
"""Base class for all tests.
|
|
Provide additional functionality for testing.
|
|
"""
|
|
|
|
def assertRaisesMsg(self, excClass, msg, callableObj, *args, **kwargs):
|
|
"""Just like unittest.TestCase.assertRaises,
|
|
but checks that the message is right too.
|
|
|
|
Borrowed from Ned Batchelder Blog.
|
|
See: http://www.nedbatchelder.com/blog/200609.html#e20060905T064418
|
|
|
|
Typical usage::
|
|
|
|
self.assertRaisesMsg(MyException, "Exception message",
|
|
my_function, (arg1, arg2))
|
|
"""
|
|
try:
|
|
callableObj(*args, **kwargs)
|
|
except excClass, exc:
|
|
excMsg = str(exc)
|
|
if not msg:
|
|
# No message provided: any message is fine.
|
|
return
|
|
elif excMsg == msg:
|
|
# Message provided, and we got the right message: it passes.
|
|
return
|
|
else:
|
|
# Message provided, and it didn't match: fail!
|
|
raise self.failureException(
|
|
"Right exception, wrong message: got '%s' expected '%s'" %
|
|
(excMsg, msg)
|
|
)
|
|
else:
|
|
if hasattr(excClass, '__name__'):
|
|
excName = excClass.__name__
|
|
else:
|
|
excName = str(excClass)
|
|
raise self.failureException(
|
|
"Expected to raise %s, didn't get an exception at all" %
|
|
excName
|
|
)
|
|
|
|
def assertEqualWrittenData(self, a, b):
|
|
return self.assertEquals(a, b, """Written data is incorrect
|
|
Should be:
|
|
%s
|
|
|
|
Written:
|
|
%s
|
|
""" % (a, b))
|
|
#/class TestIntelHexBase
|
|
|
|
|
|
class TestIntelHex(TestIntelHexBase):
|
|
|
|
def setUp(self):
|
|
self.f = StringIO(hex8)
|
|
|
|
def tearDown(self):
|
|
self.f.close()
|
|
del self.f
|
|
|
|
def test_init_from_file(self):
|
|
ih = IntelHex(self.f)
|
|
for addr in xrange(len(bin8)):
|
|
expected = bin8[addr]
|
|
actual = ih[addr]
|
|
self.assertEqual(expected, actual,
|
|
"Data different at address "
|
|
"%x (%x != %x)" % (addr, expected, actual))
|
|
|
|
def test_hex_fromfile(self):
|
|
ih = IntelHex()
|
|
ih.fromfile(self.f, format='hex')
|
|
for addr in xrange(len(bin8)):
|
|
expected = bin8[addr]
|
|
actual = ih[addr]
|
|
self.assertEqual(expected, actual,
|
|
"Data different at address "
|
|
"%x (%x != %x)" % (addr, expected, actual))
|
|
|
|
def test_unicode_filename(self):
|
|
handle, fname = tempfile.mkstemp(u'')
|
|
os.close(handle)
|
|
try:
|
|
self.assertTrue(isinstance(fname, unicode))
|
|
f = open(fname, 'w')
|
|
try:
|
|
f.write(hex8)
|
|
finally:
|
|
f.close()
|
|
ih = IntelHex(fname)
|
|
self.assertEqual(0, ih.minaddr())
|
|
self.assertEqual(len(bin8)-1, ih.maxaddr())
|
|
finally:
|
|
os.remove(fname)
|
|
|
|
def test_tobinarray_empty(self):
|
|
ih = IntelHex()
|
|
ih.padding = 0xFF # set-up explicit padding value and don't use pad parameter
|
|
self.assertEqual(array.array('B', []), ih.tobinarray())
|
|
self.assertEqual(array.array('B', []), ih.tobinarray(start=0))
|
|
self.assertEqual(array.array('B', []), ih.tobinarray(end=2))
|
|
self.assertEqual(array.array('B', [255,255,255]), ih.tobinarray(0,2))
|
|
|
|
def test_tobinarray_with_size(self):
|
|
ih = IntelHex(self.f)
|
|
self.assertEqual(array.array('B', [2, 5, 162, 229, 118, 36, 106, 248]),
|
|
ih.tobinarray(size=8)) # from addr 0
|
|
self.assertEqual(array.array('B', [120, 103, 48, 7, 2, 120, 106, 228]),
|
|
ih.tobinarray(start=12, size=8))
|
|
self.assertEqual(array.array('B', [2, 5, 162, 229, 118, 36, 106, 248]),
|
|
ih.tobinarray(end=7, size=8)) # addr: 0..7, 8 bytes
|
|
self.assertEqual(array.array('B', [120, 103, 48, 7, 2, 120, 106, 228]),
|
|
ih.tobinarray(end=19, size=8)) # addr: 12..19, 8 bytes
|
|
self.assertRaises(ValueError, ih.tobinarray, start=0, end=7, size=8)
|
|
self.assertRaises(ValueError, ih.tobinarray, end=3, size=8)
|
|
self.assertRaises(ValueError, ih.tobinarray, size=0)
|
|
self.assertRaises(ValueError, ih.tobinarray, size=-1)
|
|
|
|
def test_tobinstr(self):
|
|
ih = IntelHex(self.f)
|
|
s1 = ih.tobinstr()
|
|
s2 = asstr(bin8.tostring())
|
|
self.assertEqual(s2, s1, "data not equal\n%s\n\n%s" % (s1, s2))
|
|
|
|
def test_tobinfile(self):
|
|
ih = IntelHex(self.f)
|
|
sio = StringIO()
|
|
ih.tobinfile(sio)
|
|
s1 = sio.getvalue()
|
|
sio.close()
|
|
s2 = asstr(bin8.tostring())
|
|
self.assertEqual(s2, s1, "data not equal\n%s\n\n%s" % (s1, s2))
|
|
# new API: .tofile universal method
|
|
sio = StringIO()
|
|
ih.tofile(sio, format='bin')
|
|
s1 = sio.getvalue()
|
|
sio.close()
|
|
s2 = asstr(bin8.tostring())
|
|
self.assertEqual(s2, s1, "data not equal\n%s\n\n%s" % (s1, s2))
|
|
|
|
def test_write_empty_hexfile(self):
|
|
ih = intelhex.IntelHex()
|
|
sio = StringIO()
|
|
ih.write_hex_file(sio)
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
self.assertEqualWrittenData(hex_empty_file, s)
|
|
|
|
def test_write_hexfile(self):
|
|
ih = intelhex.IntelHex(StringIO(hex_simple))
|
|
sio = StringIO()
|
|
ih.write_hex_file(sio)
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
self.assertEqualWrittenData(hex_simple, s)
|
|
# new API: .tofile universal method
|
|
sio = StringIO()
|
|
ih.tofile(sio, format='hex')
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
self.assertEqualWrittenData(hex_simple, s)
|
|
|
|
def test_write_hex_bug_341051(self):
|
|
ih = intelhex.IntelHex(StringIO(hex_bug_lp_341051))
|
|
sio = StringIO()
|
|
ih.tofile(sio, format='hex')
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
self.assertEqualWrittenData(hex_bug_lp_341051, s)
|
|
|
|
def test_write_hex_first_extended_linear_address(self):
|
|
ih = IntelHex({0x20000: 0x01})
|
|
sio = StringIO()
|
|
ih.write_hex_file(sio)
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
# should be
|
|
r = [Record.extended_linear_address(2),
|
|
Record.data(0x0000, [0x01]),
|
|
Record.eof()]
|
|
h = '\n'.join(r) + '\n'
|
|
# compare
|
|
self.assertEqual(h, s)
|
|
|
|
def test_tofile_wrong_format(self):
|
|
ih = IntelHex()
|
|
sio = StringIO()
|
|
self.assertRaises(ValueError, ih.tofile, sio, {'format': 'bad'})
|
|
|
|
def test_todict(self):
|
|
ih = IntelHex()
|
|
self.assertEquals({}, ih.todict())
|
|
ih = IntelHex(StringIO(hex64k))
|
|
self.assertEquals(data64k, ih.todict())
|
|
ih = IntelHex()
|
|
ih[1] = 2
|
|
ih.start_addr = {'EIP': 1234}
|
|
self.assertEquals({1: 2, 'start_addr': {'EIP': 1234}}, ih.todict())
|
|
|
|
def test_fromdict(self):
|
|
ih = IntelHex()
|
|
ih.fromdict({1:2, 3:4})
|
|
self.assertEquals({1:2, 3:4}, ih.todict())
|
|
ih.fromdict({1:5, 6:7})
|
|
self.assertEquals({1:5, 3:4, 6:7}, ih.todict())
|
|
ih = IntelHex()
|
|
ih.fromdict({1: 2, 'start_addr': {'EIP': 1234}})
|
|
self.assertEquals({1: 2, 'start_addr': {'EIP': 1234}}, ih.todict())
|
|
# bad dict
|
|
self.assertRaises(ValueError, ih.fromdict, {'EIP': 1234})
|
|
self.assertRaises(ValueError, ih.fromdict, {-1: 1234})
|
|
|
|
def test_init_from_obj(self):
|
|
ih = IntelHex({1:2, 3:4})
|
|
self.assertEquals({1:2, 3:4}, ih.todict())
|
|
ih.start_addr = {'EIP': 1234}
|
|
ih2 = IntelHex(ih)
|
|
ih[1] = 5
|
|
ih.start_addr = {'EIP': 5678}
|
|
self.assertEquals({1:2, 3:4, 'start_addr': {'EIP': 1234}}, ih2.todict())
|
|
self.assertNotEqual(id(ih), id(ih2))
|
|
|
|
def test_dict_interface(self):
|
|
ih = IntelHex()
|
|
self.assertEquals(0xFF, ih[0]) # padding byte substitution
|
|
ih[0] = 1
|
|
self.assertEquals(1, ih[0])
|
|
del ih[0]
|
|
self.assertEquals({}, ih.todict()) # padding byte substitution
|
|
|
|
def test_len(self):
|
|
ih = IntelHex()
|
|
self.assertEquals(0, len(ih))
|
|
ih[2] = 1
|
|
self.assertEquals(1, len(ih))
|
|
ih[1000] = 2
|
|
self.assertEquals(2, len(ih))
|
|
|
|
def test__getitem__(self):
|
|
ih = IntelHex()
|
|
# simple cases
|
|
self.assertEquals(0xFF, ih[0])
|
|
ih[0] = 1
|
|
self.assertEquals(1, ih[0])
|
|
# big address
|
|
self.assertEquals(0xFF, ih[2**32-1])
|
|
# wrong addr type/value for indexing operations
|
|
def getitem(index):
|
|
return ih[index]
|
|
self.assertRaisesMsg(TypeError,
|
|
'Address should be >= 0.',
|
|
getitem, -1)
|
|
self.assertRaisesMsg(TypeError,
|
|
"Address has unsupported type: %s" % type('foo'),
|
|
getitem, 'foo')
|
|
# new object with some data
|
|
ih = IntelHex()
|
|
ih[0] = 1
|
|
ih[1] = 2
|
|
ih[2] = 3
|
|
ih[10] = 4
|
|
# full copy via slicing
|
|
ih2 = ih[:]
|
|
self.assertTrue(isinstance(ih2, IntelHex))
|
|
self.assertEquals({0:1, 1:2, 2:3, 10:4}, ih2.todict())
|
|
# other slice operations
|
|
self.assertEquals({}, ih[3:8].todict())
|
|
self.assertEquals({0:1, 1:2}, ih[0:2].todict())
|
|
self.assertEquals({0:1, 1:2}, ih[:2].todict())
|
|
self.assertEquals({2:3, 10:4}, ih[2:].todict())
|
|
self.assertEquals({0:1, 2:3, 10:4}, ih[::2].todict())
|
|
self.assertEquals({10:4}, ih[3:11].todict())
|
|
|
|
def test__setitem__(self):
|
|
ih = IntelHex()
|
|
# simple indexing operation
|
|
ih[0] = 1
|
|
self.assertEquals({0:1}, ih.todict())
|
|
# errors
|
|
def setitem(a,b):
|
|
ih[a] = b
|
|
self.assertRaisesMsg(TypeError,
|
|
'Address should be >= 0.',
|
|
setitem, -1, 0)
|
|
self.assertRaisesMsg(TypeError,
|
|
"Address has unsupported type: %s" % type('foo'),
|
|
setitem, 'foo', 0)
|
|
# slice operations
|
|
ih[0:4] = range(4)
|
|
self.assertEquals({0:0, 1:1, 2:2, 3:3}, ih.todict())
|
|
ih[0:] = range(5,9)
|
|
self.assertEquals({0:5, 1:6, 2:7, 3:8}, ih.todict())
|
|
ih[:4] = range(9,13)
|
|
self.assertEquals({0:9, 1:10, 2:11, 3:12}, ih.todict())
|
|
# with step
|
|
ih = IntelHex()
|
|
ih[0:8:2] = range(4)
|
|
self.assertEquals({0:0, 2:1, 4:2, 6:3}, ih.todict())
|
|
# errors in slice operations
|
|
# ih[1:2] = 'a'
|
|
self.assertRaisesMsg(ValueError,
|
|
'Slice operation expects sequence of bytes',
|
|
setitem, slice(1,2,None), 'a')
|
|
# ih[0:1] = [1,2,3]
|
|
self.assertRaisesMsg(ValueError,
|
|
'Length of bytes sequence does not match address range',
|
|
setitem, slice(0,1,None), [1,2,3])
|
|
# ih[:] = [1,2,3]
|
|
self.assertRaisesMsg(TypeError,
|
|
'Unsupported address range',
|
|
setitem, slice(None,None,None), [1,2,3])
|
|
# ih[:2] = [1,2,3]
|
|
self.assertRaisesMsg(TypeError,
|
|
'start address cannot be negative',
|
|
setitem, slice(None,2,None), [1,2,3])
|
|
# ih[0:-3:-1] = [1,2,3]
|
|
self.assertRaisesMsg(TypeError,
|
|
'stop address cannot be negative',
|
|
setitem, slice(0,-3,-1), [1,2,3])
|
|
|
|
def test__delitem__(self):
|
|
ih = IntelHex()
|
|
ih[0] = 1
|
|
del ih[0]
|
|
self.assertEquals({}, ih.todict())
|
|
# errors
|
|
def delitem(addr):
|
|
del ih[addr]
|
|
self.assertRaises(KeyError, delitem, 1)
|
|
self.assertRaisesMsg(TypeError,
|
|
'Address should be >= 0.',
|
|
delitem, -1)
|
|
self.assertRaisesMsg(TypeError,
|
|
"Address has unsupported type: %s" % type('foo'),
|
|
delitem, 'foo')
|
|
# deleting slice
|
|
del ih[0:1] # no error here because of slicing
|
|
#
|
|
def ihex(size=8):
|
|
ih = IntelHex()
|
|
for i in xrange(size):
|
|
ih[i] = i
|
|
return ih
|
|
ih = ihex(8)
|
|
del ih[:] # delete all data
|
|
self.assertEquals({}, ih.todict())
|
|
ih = ihex(8)
|
|
del ih[2:6]
|
|
self.assertEquals({0:0, 1:1, 6:6, 7:7}, ih.todict())
|
|
ih = ihex(8)
|
|
del ih[::2]
|
|
self.assertEquals({1:1, 3:3, 5:5, 7:7}, ih.todict())
|
|
|
|
def test_addresses(self):
|
|
# empty object
|
|
ih = IntelHex()
|
|
self.assertEquals([], ih.addresses())
|
|
self.assertEquals(None, ih.minaddr())
|
|
self.assertEquals(None, ih.maxaddr())
|
|
# normal object
|
|
ih = IntelHex({1:2, 7:8, 10:0})
|
|
self.assertEquals([1,7,10], ih.addresses())
|
|
self.assertEquals(1, ih.minaddr())
|
|
self.assertEquals(10, ih.maxaddr())
|
|
|
|
def test__get_start_end(self):
|
|
# test for private method _get_start_end
|
|
# for empty object
|
|
ih = IntelHex()
|
|
self.assertRaises(intelhex.EmptyIntelHexError, ih._get_start_end)
|
|
self.assertRaises(intelhex.EmptyIntelHexError, ih._get_start_end, size=10)
|
|
self.assertEquals((0,9), ih._get_start_end(start=0, size=10))
|
|
self.assertEquals((1,10), ih._get_start_end(end=10, size=10))
|
|
# normal object
|
|
ih = IntelHex({1:2, 7:8, 10:0})
|
|
self.assertEquals((1,10), ih._get_start_end())
|
|
self.assertEquals((1,10), ih._get_start_end(size=10))
|
|
self.assertEquals((0,9), ih._get_start_end(start=0, size=10))
|
|
self.assertEquals((1,10), ih._get_start_end(end=10, size=10))
|
|
|
|
|
|
class TestIntelHexLoadBin(TestIntelHexBase):
|
|
|
|
def setUp(self):
|
|
self.data = '0123456789'
|
|
self.f = StringIO(self.data)
|
|
|
|
def tearDown(self):
|
|
self.f.close()
|
|
|
|
def test_loadbin(self):
|
|
ih = IntelHex()
|
|
ih.loadbin(self.f)
|
|
self.assertEqual(0, ih.minaddr())
|
|
self.assertEqual(9, ih.maxaddr())
|
|
self.assertEqual(self.data, ih.tobinstr())
|
|
|
|
def test_bin_fromfile(self):
|
|
ih = IntelHex()
|
|
ih.fromfile(self.f, format='bin')
|
|
self.assertEqual(0, ih.minaddr())
|
|
self.assertEqual(9, ih.maxaddr())
|
|
self.assertEqual(self.data, ih.tobinstr())
|
|
|
|
def test_loadbin_w_offset(self):
|
|
ih = IntelHex()
|
|
ih.loadbin(self.f, offset=100)
|
|
self.assertEqual(100, ih.minaddr())
|
|
self.assertEqual(109, ih.maxaddr())
|
|
self.assertEqual(self.data, ih.tobinstr())
|
|
|
|
def test_loadfile_format_bin(self):
|
|
ih = IntelHex()
|
|
ih.loadfile(self.f, format='bin')
|
|
self.assertEqual(0, ih.minaddr())
|
|
self.assertEqual(9, ih.maxaddr())
|
|
self.assertEqual(self.data, ih.tobinstr())
|
|
|
|
|
|
class TestIntelHexStartingAddressRecords(TestIntelHexBase):
|
|
|
|
def _test_read(self, hexstr, data, start_addr):
|
|
sio = StringIO(hexstr)
|
|
ih = IntelHex(sio)
|
|
sio.close()
|
|
# test data
|
|
self.assertEqual(data, ih._buf,
|
|
"Internal buffer: %r != %r" %
|
|
(data, ih._buf))
|
|
self.assertEqual(start_addr, ih.start_addr,
|
|
"Start address: %r != %r" %
|
|
(start_addr, ih.start_addr))
|
|
|
|
def test_read_rectype3(self):
|
|
self._test_read(hex_rectype3, data_rectype3, start_addr_rectype3)
|
|
|
|
def test_read_rectype5(self):
|
|
self._test_read(hex_rectype5, data_rectype5, start_addr_rectype5)
|
|
|
|
def _test_write(self, hexstr, data, start_addr, write_start_addr=True):
|
|
# prepare
|
|
ih = IntelHex(None)
|
|
ih._buf = data
|
|
ih.start_addr = start_addr
|
|
# write
|
|
sio = StringIO()
|
|
ih.write_hex_file(sio, write_start_addr)
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
# check
|
|
self.assertEqualWrittenData(hexstr, s)
|
|
|
|
def _test_dont_write(self, hexstr, data, start_addr):
|
|
expected = ''.join(hexstr.splitlines(True)[1:])
|
|
self._test_write(expected, data, start_addr, False)
|
|
|
|
def test_write_rectype3(self):
|
|
self._test_write(hex_rectype3, data_rectype3, start_addr_rectype3)
|
|
|
|
def test_dont_write_rectype3(self):
|
|
self._test_dont_write(hex_rectype3, data_rectype3, start_addr_rectype3)
|
|
|
|
def test_write_rectype5(self):
|
|
self._test_write(hex_rectype5, data_rectype5, start_addr_rectype5)
|
|
|
|
def test_dont_write_rectype5(self):
|
|
self._test_dont_write(hex_rectype5, data_rectype5, start_addr_rectype5)
|
|
|
|
def test_write_invalid_start_addr_value(self):
|
|
ih = IntelHex()
|
|
ih.start_addr = {'foo': 1}
|
|
sio = StringIO()
|
|
self.assertRaises(InvalidStartAddressValueError, ih.write_hex_file, sio)
|
|
|
|
|
|
class TestIntelHex_big_files(TestIntelHexBase):
|
|
"""Test that data bigger than 64K read/write correctly"""
|
|
|
|
def setUp(self):
|
|
self.f = StringIO(hex64k)
|
|
|
|
def tearDown(self):
|
|
self.f.close()
|
|
del self.f
|
|
|
|
def test_readfile(self):
|
|
ih = intelhex.IntelHex(self.f)
|
|
for addr, byte in data64k.items():
|
|
readed = ih[addr]
|
|
self.assertEquals(byte, readed,
|
|
"data not equal at addr %X "
|
|
"(%X != %X)" % (addr, byte, readed))
|
|
|
|
def test_write_hex_file(self):
|
|
ih = intelhex.IntelHex(self.f)
|
|
sio = StringIO()
|
|
ih.write_hex_file(sio)
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
self.assertEqualWrittenData(hex64k, s)
|
|
|
|
|
|
class TestIntelHexGetPutString(TestIntelHexBase):
|
|
|
|
def setUp(self):
|
|
self.ih = IntelHex()
|
|
for i in xrange(10):
|
|
self.ih[i] = i
|
|
|
|
def test_gets(self):
|
|
self.assertEquals('\x00\x01\x02\x03\x04\x05\x06\x07', self.ih.gets(0, 8))
|
|
self.assertEquals('\x07\x08\x09', self.ih.gets(7, 3))
|
|
self.assertRaisesMsg(intelhex.NotEnoughDataError,
|
|
'Bad access at 0x1: '
|
|
'not enough data to read 10 contiguous bytes',
|
|
self.ih.gets, 1, 10)
|
|
|
|
def test_puts(self):
|
|
self.ih.puts(0x03, 'hello')
|
|
self.assertEquals('\x00\x01\x02hello\x08\x09', self.ih.gets(0, 10))
|
|
|
|
def test_getsz(self):
|
|
self.assertEquals('', self.ih.getsz(0))
|
|
self.assertRaisesMsg(intelhex.NotEnoughDataError,
|
|
'Bad access at 0x1: '
|
|
'not enough data to read zero-terminated string',
|
|
self.ih.getsz, 1)
|
|
self.ih[4] = 0
|
|
self.assertEquals('\x01\x02\x03', self.ih.getsz(1))
|
|
|
|
def test_putsz(self):
|
|
self.ih.putsz(0x03, 'hello')
|
|
self.assertEquals('\x00\x01\x02hello\x00\x09', self.ih.gets(0, 10))
|
|
|
|
|
|
class TestIntelHexDump(TestIntelHexBase):
|
|
|
|
def test_empty(self):
|
|
ih = IntelHex()
|
|
sio = StringIO()
|
|
ih.dump(sio)
|
|
self.assertEquals('', sio.getvalue())
|
|
|
|
def test_simple(self):
|
|
ih = IntelHex()
|
|
ih[0] = 0x12
|
|
ih[1] = 0x34
|
|
sio = StringIO()
|
|
ih.dump(sio)
|
|
self.assertEquals(
|
|
'0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n',
|
|
sio.getvalue())
|
|
ih[16] = 0x56
|
|
ih[30] = 0x98
|
|
sio = StringIO()
|
|
ih.dump(sio)
|
|
self.assertEquals(
|
|
'0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n'
|
|
'0010 56 -- -- -- -- -- -- -- -- -- -- -- -- -- 98 -- |V . |\n',
|
|
sio.getvalue())
|
|
|
|
def test_minaddr_not_zero(self):
|
|
ih = IntelHex()
|
|
ih[16] = 0x56
|
|
ih[30] = 0x98
|
|
sio = StringIO()
|
|
ih.dump(sio)
|
|
self.assertEquals(
|
|
'0010 56 -- -- -- -- -- -- -- -- -- -- -- -- -- 98 -- |V . |\n',
|
|
sio.getvalue())
|
|
|
|
def test_start_addr(self):
|
|
ih = IntelHex()
|
|
ih[0] = 0x12
|
|
ih[1] = 0x34
|
|
ih.start_addr = {'CS': 0x1234, 'IP': 0x5678}
|
|
sio = StringIO()
|
|
ih.dump(sio)
|
|
self.assertEquals(
|
|
'CS = 0x1234, IP = 0x5678\n'
|
|
'0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n',
|
|
sio.getvalue())
|
|
ih.start_addr = {'EIP': 0x12345678}
|
|
sio = StringIO()
|
|
ih.dump(sio)
|
|
self.assertEquals(
|
|
'EIP = 0x12345678\n'
|
|
'0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n',
|
|
sio.getvalue())
|
|
|
|
|
|
class TestIntelHexMerge(TestIntelHexBase):
|
|
|
|
def test_merge_empty(self):
|
|
ih1 = IntelHex()
|
|
ih2 = IntelHex()
|
|
ih1.merge(ih2)
|
|
self.assertEquals({}, ih1.todict())
|
|
|
|
def test_merge_simple(self):
|
|
ih1 = IntelHex({0:1, 1:2, 2:3})
|
|
ih2 = IntelHex({3:4, 4:5, 5:6})
|
|
ih1.merge(ih2)
|
|
self.assertEquals({0:1, 1:2, 2:3, 3:4, 4:5, 5:6}, ih1.todict())
|
|
|
|
def test_merge_wrong_args(self):
|
|
ih1 = IntelHex()
|
|
self.assertRaisesMsg(TypeError, 'other should be IntelHex object',
|
|
ih1.merge, {0:1})
|
|
self.assertRaisesMsg(ValueError, "Can't merge itself",
|
|
ih1.merge, ih1)
|
|
ih2 = IntelHex()
|
|
self.assertRaisesMsg(ValueError, "overlap argument should be either "
|
|
"'error', 'ignore' or 'replace'",
|
|
ih1.merge, ih2, overlap='spam')
|
|
|
|
def test_merge_overlap(self):
|
|
# error
|
|
ih1 = IntelHex({0:1})
|
|
ih2 = IntelHex({0:2})
|
|
self.assertRaisesMsg(intelhex.AddressOverlapError,
|
|
'Data overlapped at address 0x0',
|
|
ih1.merge, ih2, overlap='error')
|
|
# ignore
|
|
ih1 = IntelHex({0:1})
|
|
ih2 = IntelHex({0:2})
|
|
ih1.merge(ih2, overlap='ignore')
|
|
self.assertEquals({0:1}, ih1.todict())
|
|
# replace
|
|
ih1 = IntelHex({0:1})
|
|
ih2 = IntelHex({0:2})
|
|
ih1.merge(ih2, overlap='replace')
|
|
self.assertEquals({0:2}, ih1.todict())
|
|
|
|
def test_merge_start_addr(self):
|
|
# this, None
|
|
ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}})
|
|
ih2 = IntelHex()
|
|
ih1.merge(ih2)
|
|
self.assertEquals({'start_addr': {'EIP': 0x12345678}}, ih1.todict())
|
|
# None, other
|
|
ih1 = IntelHex()
|
|
ih2 = IntelHex({'start_addr': {'EIP': 0x12345678}})
|
|
ih1.merge(ih2)
|
|
self.assertEquals({'start_addr': {'EIP': 0x12345678}}, ih1.todict())
|
|
# this == other: no conflict
|
|
ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}})
|
|
ih2 = IntelHex({'start_addr': {'EIP': 0x12345678}})
|
|
ih1.merge(ih2)
|
|
self.assertEquals({'start_addr': {'EIP': 0x12345678}}, ih1.todict())
|
|
# this != other: conflict
|
|
## overlap=error
|
|
ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}})
|
|
ih2 = IntelHex({'start_addr': {'EIP': 0x87654321}})
|
|
self.assertRaisesMsg(AddressOverlapError,
|
|
'Starting addresses are different',
|
|
ih1.merge, ih2, overlap='error')
|
|
## overlap=ignore
|
|
ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}})
|
|
ih2 = IntelHex({'start_addr': {'EIP': 0x87654321}})
|
|
ih1.merge(ih2, overlap='ignore')
|
|
self.assertEquals({'start_addr': {'EIP': 0x12345678}}, ih1.todict())
|
|
## overlap=replace
|
|
ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}})
|
|
ih2 = IntelHex({'start_addr': {'EIP': 0x87654321}})
|
|
ih1.merge(ih2, overlap='replace')
|
|
self.assertEquals({'start_addr': {'EIP': 0x87654321}}, ih1.todict())
|
|
|
|
|
|
class TestIntelHex16bit(TestIntelHexBase):
|
|
|
|
def setUp(self):
|
|
self.f = StringIO(hex16)
|
|
|
|
def tearDown(self):
|
|
self.f.close()
|
|
del self.f
|
|
|
|
def test_init_from_file(self):
|
|
ih = intelhex.IntelHex16bit(self.f)
|
|
|
|
def test_init_from_ih(self):
|
|
ih = intelhex.IntelHex(self.f)
|
|
ih16 = intelhex.IntelHex16bit(ih)
|
|
|
|
def test_default_padding(self):
|
|
ih16 = intelhex.IntelHex16bit()
|
|
self.assertEqual(0x0FFFF, ih16.padding)
|
|
self.assertEqual(0x0FFFF, ih16[0])
|
|
|
|
def test_minaddr(self):
|
|
ih = intelhex.IntelHex16bit(self.f)
|
|
addr = ih.minaddr()
|
|
self.assertEqual(0, addr,
|
|
'Error in detection of minaddr (0 != 0x%x)' % addr)
|
|
|
|
def test_maxaddr(self):
|
|
ih = intelhex.IntelHex16bit(self.f)
|
|
addr = ih.maxaddr()
|
|
self.assertEqual(0x001D, addr,
|
|
'Error in detection of maxaddr '
|
|
'(0x001D != 0x%x)' % addr)
|
|
|
|
def test_getitem(self):
|
|
ih = intelhex.IntelHex16bit(self.f)
|
|
ih.padding = 0x3FFF
|
|
for addr, word in enumerate(bin16):
|
|
self.assertEqual(word, ih[addr],
|
|
'Data mismatch at address '
|
|
'0x%x (0x%x != 0x%x)' % (addr, word, ih[addr]))
|
|
|
|
def test_not_enough_data(self):
|
|
ih = intelhex.IntelHex()
|
|
ih[0] = 1
|
|
ih16 = intelhex.IntelHex16bit(ih)
|
|
self.assertRaisesMsg(BadAccess16bit,
|
|
'Bad access at 0x0: '
|
|
'not enough data to read 16 bit value',
|
|
lambda x: ih16[x],
|
|
0)
|
|
|
|
def test_write_hex_file(self):
|
|
ih = intelhex.IntelHex16bit(self.f)
|
|
sio = StringIO()
|
|
ih.write_hex_file(sio)
|
|
s = sio.getvalue()
|
|
sio.close()
|
|
|
|
fin = StringIO(s)
|
|
ih2 = intelhex.IntelHex16bit(fin)
|
|
|
|
self.assertEqual(ih.tobinstr(), ih2.tobinstr(),
|
|
"Written hex file does not equal with original")
|
|
|
|
def test_bug_988148(self):
|
|
# see https://bugs.launchpad.net/intelhex/+bug/988148
|
|
ih = intelhex.IntelHex16bit(intelhex.IntelHex())
|
|
ih[0] = 25
|
|
sio = StringIO()
|
|
ih.write_hex_file(sio)
|
|
|
|
def test_setitem(self):
|
|
ih = intelhex.IntelHex16bit(self.f)
|
|
|
|
old = ih[0]
|
|
ih[0] = old ^ 0xFFFF
|
|
|
|
self.assertNotEqual(old, ih[0],
|
|
"Setting new value to internal buffer failed")
|
|
|
|
def test_tobinarray(self):
|
|
ih = intelhex.IntelHex16bit()
|
|
ih[0] = 0x1234
|
|
ih[1] = 0x5678
|
|
self.assertEqual(array.array('H', [0x1234,0x5678,0xFFFF]),
|
|
ih.tobinarray(start=0, end=2))
|
|
# change padding
|
|
ih.padding = 0x3FFF
|
|
self.assertEqual(array.array('H', [0x1234,0x5678,0x3FFF]),
|
|
ih.tobinarray(start=0, end=2))
|
|
#/class TestIntelHex16bit
|
|
|
|
|
|
class TestIntelHexErrors(TestIntelHexBase):
|
|
"""Tests for custom errors classes"""
|
|
|
|
def assertEqualExc(self, message, exception):
|
|
return self.assertEqual(message, str(exception))
|
|
|
|
def test_IntelHexError(self):
|
|
self.assertEqualExc('IntelHex base error', IntelHexError())
|
|
|
|
def test_IntelHexError_message(self):
|
|
self.assertEqualExc('IntelHex custom error message',
|
|
IntelHexError(msg='IntelHex custom error message'))
|
|
self.assertEqualExc('IntelHex base error', IntelHexError(msg=''))
|
|
|
|
def test_HexReaderError(self):
|
|
self.assertEqualExc('Hex reader base error', HexReaderError())
|
|
|
|
def test_HexRecordError(self):
|
|
self.assertEqualExc('Hex file contains invalid record at line 1',
|
|
HexRecordError(line=1))
|
|
|
|
def test_RecordLengthError(self):
|
|
self.assertEqualExc('Record at line 1 has invalid length',
|
|
RecordLengthError(line=1))
|
|
|
|
def test_RecordTypeError(self):
|
|
self.assertEqualExc('Record at line 1 has invalid record type',
|
|
RecordTypeError(line=1))
|
|
|
|
def test_RecordChecksumError(self):
|
|
self.assertEqualExc('Record at line 1 has invalid checksum',
|
|
RecordChecksumError(line=1))
|
|
|
|
def test_EOFRecordError(self):
|
|
self.assertEqualExc('File has invalid End-of-File record',
|
|
EOFRecordError())
|
|
|
|
def test_ExtendedSegmentAddressRecordError(self):
|
|
self.assertEqualExc(
|
|
'Invalid Extended Segment Address Record at line 1',
|
|
ExtendedSegmentAddressRecordError(line=1))
|
|
|
|
def test_ExtendedLinearAddressRecordError(self):
|
|
self.assertEqualExc('Invalid Extended Linear Address Record at line 1',
|
|
ExtendedLinearAddressRecordError(line=1))
|
|
|
|
def test_StartSegmentAddressRecordError(self):
|
|
self.assertEqualExc('Invalid Start Segment Address Record at line 1',
|
|
StartSegmentAddressRecordError(line=1))
|
|
|
|
def test_StartLinearAddressRecordError(self):
|
|
self.assertEqualExc('Invalid Start Linear Address Record at line 1',
|
|
StartLinearAddressRecordError(line=1))
|
|
|
|
def test_DuplicateStartAddressRecord(self):
|
|
self.assertEqualExc('Start Address Record appears twice at line 1',
|
|
DuplicateStartAddressRecordError(line=1))
|
|
|
|
def test_InvalidStartAddressValue(self):
|
|
self.assertEqualExc("Invalid start address value: {'foo': 1}",
|
|
InvalidStartAddressValueError(start_addr={'foo': 1}))
|
|
|
|
def test_AddressOverlapError(self):
|
|
self.assertEqualExc('Hex file has data overlap at address 0x1234 '
|
|
'on line 1',
|
|
AddressOverlapError(address=0x1234, line=1))
|
|
|
|
def test_NotEnoughDataError(self):
|
|
self.assertEqualExc('Bad access at 0x1234: '
|
|
'not enough data to read 10 contiguous bytes',
|
|
intelhex.NotEnoughDataError(address=0x1234, length=10))
|
|
|
|
def test_BadAccess16bit(self):
|
|
self.assertEqualExc('Bad access at 0x1234: '
|
|
'not enough data to read 16 bit value',
|
|
BadAccess16bit(address=0x1234))
|
|
#/class TestIntelHexErrors
|
|
|
|
|
|
class TestDecodeHexRecords(TestIntelHexBase):
|
|
"""Testing that decoding of records is correct
|
|
and all errors raised when needed
|
|
"""
|
|
|
|
def setUp(self):
|
|
self.ih = IntelHex()
|
|
self.decode_record = self.ih._decode_record
|
|
|
|
def tearDown(self):
|
|
del self.ih
|
|
|
|
def test_empty_line(self):
|
|
# do we could to accept empty lines in hex files?
|
|
# standard don't say anything about this
|
|
self.decode_record('')
|
|
|
|
def test_non_empty_line(self):
|
|
self.assertRaisesMsg(HexRecordError,
|
|
'Hex file contains invalid record at line 1',
|
|
self.decode_record,
|
|
' ',
|
|
1)
|
|
|
|
def test_short_record(self):
|
|
# if record too short it's not a hex record
|
|
self.assertRaisesMsg(HexRecordError,
|
|
'Hex file contains invalid record at line 1',
|
|
self.decode_record,
|
|
':',
|
|
1)
|
|
|
|
def test_odd_hexascii_digits(self):
|
|
self.assertRaisesMsg(HexRecordError,
|
|
'Hex file contains invalid record at line 1',
|
|
self.decode_record,
|
|
':0100000100F',
|
|
1)
|
|
|
|
def test_invalid_length(self):
|
|
self.assertRaisesMsg(RecordLengthError,
|
|
'Record at line 1 has invalid length',
|
|
self.decode_record,
|
|
':FF00000100',
|
|
1)
|
|
|
|
def test_invalid_record_type(self):
|
|
self.assertRaisesMsg(RecordTypeError,
|
|
'Record at line 1 has invalid record type',
|
|
self.decode_record,
|
|
':000000FF01',
|
|
1)
|
|
|
|
def test_invalid_checksum(self):
|
|
self.assertRaisesMsg(RecordChecksumError,
|
|
'Record at line 1 has invalid checksum',
|
|
self.decode_record,
|
|
':0000000100',
|
|
1)
|
|
|
|
def test_invalid_eof(self):
|
|
self.assertRaisesMsg(EOFRecordError,
|
|
'File has invalid End-of-File record',
|
|
self.decode_record,
|
|
':0100000100FE',
|
|
1)
|
|
|
|
def test_invalid_extended_segment(self):
|
|
# length
|
|
self.assertRaisesMsg(ExtendedSegmentAddressRecordError,
|
|
'Invalid Extended Segment Address Record at line 1',
|
|
self.decode_record,
|
|
':00000002FE',
|
|
1)
|
|
# addr field
|
|
self.assertRaisesMsg(ExtendedSegmentAddressRecordError,
|
|
'Invalid Extended Segment Address Record at line 1',
|
|
self.decode_record,
|
|
':020001020000FB',
|
|
1)
|
|
|
|
def test_invalid_linear_address(self):
|
|
# length
|
|
self.assertRaisesMsg(ExtendedLinearAddressRecordError,
|
|
'Invalid Extended Linear Address Record '
|
|
'at line 1',
|
|
self.decode_record,
|
|
':00000004FC',
|
|
1)
|
|
# addr field
|
|
self.assertRaisesMsg(ExtendedLinearAddressRecordError,
|
|
'Invalid Extended Linear Address Record '
|
|
'at line 1',
|
|
self.decode_record,
|
|
':020001040000F9',
|
|
1)
|
|
|
|
def test_invalid_start_segment_addr(self):
|
|
# length
|
|
self.assertRaisesMsg(StartSegmentAddressRecordError,
|
|
'Invalid Start Segment Address Record at line 1',
|
|
self.decode_record,
|
|
':00000003FD',
|
|
1)
|
|
# addr field
|
|
self.assertRaisesMsg(StartSegmentAddressRecordError,
|
|
'Invalid Start Segment Address Record at line 1',
|
|
self.decode_record,
|
|
':0400010300000000F8',
|
|
1)
|
|
|
|
def test_duplicate_start_segment_addr(self):
|
|
self.decode_record(':0400000312345678E5')
|
|
self.assertRaisesMsg(DuplicateStartAddressRecordError,
|
|
'Start Address Record appears twice at line 2',
|
|
self.decode_record,
|
|
':0400000300000000F9',
|
|
2)
|
|
|
|
def test_invalid_start_linear_addr(self):
|
|
# length
|
|
self.assertRaisesMsg(StartLinearAddressRecordError,
|
|
'Invalid Start Linear Address Record at line 1',
|
|
self.decode_record,
|
|
':00000005FB',
|
|
1)
|
|
# addr field
|
|
self.assertRaisesMsg(StartLinearAddressRecordError,
|
|
'Invalid Start Linear Address Record at line 1',
|
|
self.decode_record,
|
|
':0400010500000000F6',
|
|
1)
|
|
|
|
def test_duplicate_start_linear_addr(self):
|
|
self.decode_record(':0400000512345678E3')
|
|
self.assertRaisesMsg(DuplicateStartAddressRecordError,
|
|
'Start Address Record appears twice at line 2',
|
|
self.decode_record,
|
|
':0400000500000000F7',
|
|
2)
|
|
|
|
def test_addr_overlap(self):
|
|
self.decode_record(':0100000000FF')
|
|
self.assertRaisesMsg(AddressOverlapError,
|
|
'Hex file has data overlap at address 0x0 '
|
|
'on line 1',
|
|
self.decode_record,
|
|
':0100000000FF',
|
|
1)
|
|
|
|
def test_data_record(self):
|
|
# should be no exceptions
|
|
self.decode_record(':0100000000FF\n')
|
|
self.decode_record(':03000100000102F9\r\n')
|
|
self.decode_record(':1004E300CFF0FBE2FDF220FF20F2E120E2FBE6F396')
|
|
|
|
def test_eof(self):
|
|
# EOF should raise special exception
|
|
self.assertRaises(_EndOfFile, self.decode_record, ':00000001FF')
|
|
|
|
#/class TestDecodeHexRecords
|
|
|
|
|
|
class TestHex2Bin(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.fin = StringIO(hex8)
|
|
self.fout = StringIO()
|
|
|
|
def tearDown(self):
|
|
self.fin.close()
|
|
self.fout.close()
|
|
|
|
def test_hex2bin(self):
|
|
ih = hex2bin(self.fin, self.fout)
|
|
data = array.array('B', asbytes(self.fout.getvalue()))
|
|
for addr in xrange(len(bin8)):
|
|
expected = bin8[addr]
|
|
actual = data[addr]
|
|
self.assertEqual(expected, actual,
|
|
"Data different at address "
|
|
"%x (%x != %x)" % (addr, expected, actual))
|
|
|
|
|
|
class TestDiffDumps(unittest.TestCase):
|
|
|
|
def test_simple(self):
|
|
ih1 = IntelHex({1:0x30, 20:0x31, 40:0x33})
|
|
ih2 = IntelHex({1:0x30, 20:0x32, 40:0x33})
|
|
sio = StringIO()
|
|
intelhex.diff_dumps(ih1, ih2, sio)
|
|
result = sio.getvalue()
|
|
extra = ' '
|
|
if sys.version_info[0] >= 3 or sys.version >= '2.7':
|
|
extra = ''
|
|
shouldbe = (
|
|
"--- a%(extra)s\n"
|
|
"+++ b%(extra)s\n"
|
|
"@@ -1,3 +1,3 @@\n"
|
|
" 0000 -- 30 -- -- -- -- -- -- -- -- -- -- -- -- -- -- | 0 |\n"
|
|
"-0010 -- -- -- -- 31 -- -- -- -- -- -- -- -- -- -- -- | 1 |\n"
|
|
"+0010 -- -- -- -- 32 -- -- -- -- -- -- -- -- -- -- -- | 2 |\n"
|
|
" 0020 -- -- -- -- -- -- -- -- 33 -- -- -- -- -- -- -- | 3 |\n"
|
|
) % dict(extra=extra)
|
|
self.assertEquals(shouldbe, result)
|
|
|
|
|
|
class TestBuildRecords(TestIntelHexBase):
|
|
|
|
def test__from_bytes(self):
|
|
self.assertEqual(':00000001FF',
|
|
intelhex.Record._from_bytes([0,0,0,1]))
|
|
|
|
def test_data(self):
|
|
self.assertEqual(':011234005663', intelhex.Record.data(0x1234, [0x56]))
|
|
self.assertEqual(':0312340056789059',
|
|
intelhex.Record.data(0x1234, [0x56, 0x78, 0x90]))
|
|
|
|
def test_eof(self):
|
|
self.assertEqual(':00000001FF', intelhex.Record.eof())
|
|
|
|
def test_extended_segment_address(self):
|
|
self.assertEqual(':020000021234B6',
|
|
intelhex.Record.extended_segment_address(0x1234))
|
|
|
|
def test_start_segment_address(self):
|
|
self.assertEqual(':0400000312345678E5',
|
|
intelhex.Record.start_segment_address(0x1234, 0x5678))
|
|
|
|
def test_extended_linear_address(self):
|
|
self.assertEqual(':020000041234B4',
|
|
intelhex.Record.extended_linear_address(0x1234))
|
|
|
|
def test_start_linear_address(self):
|
|
self.assertEqual(':0400000512345678E3',
|
|
intelhex.Record.start_linear_address(0x12345678))
|
|
|
|
|
|
class Test_GetFileAndAddrRange(TestIntelHexBase):
|
|
|
|
def test_simple(self):
|
|
self.assertEqual(('filename.hex', None, None),
|
|
intelhex._get_file_and_addr_range('filename.hex'))
|
|
self.assertEqual(('f', None, None),
|
|
intelhex._get_file_and_addr_range('f'))
|
|
self.assertEqual(('filename.hex', 1, None),
|
|
intelhex._get_file_and_addr_range('filename.hex:1:'))
|
|
self.assertEqual(('filename.hex', None, 10),
|
|
intelhex._get_file_and_addr_range('filename.hex::A'))
|
|
self.assertEqual(('filename.hex', 1, 10),
|
|
intelhex._get_file_and_addr_range('filename.hex:1:A'))
|
|
self.assertEqual(('filename.hex', 1, 10),
|
|
intelhex._get_file_and_addr_range('filename.hex:0001:000A'))
|
|
|
|
def test_bad_notation(self):
|
|
self.assertRaises(intelhex._BadFileNotation,
|
|
intelhex._get_file_and_addr_range, 'filename.hex:')
|
|
self.assertRaises(intelhex._BadFileNotation,
|
|
intelhex._get_file_and_addr_range, 'filename.hex:::')
|
|
self.assertRaises(intelhex._BadFileNotation,
|
|
intelhex._get_file_and_addr_range, 'C:\\filename.hex:', True)
|
|
|
|
def test_drive_letter(self):
|
|
self.assertEqual(('C:\\filename.hex', None, None),
|
|
intelhex._get_file_and_addr_range('C:\\filename.hex', True))
|
|
self.assertEqual(('C:\\filename.hex', 1, None),
|
|
intelhex._get_file_and_addr_range('C:\\filename.hex:1:', True))
|
|
self.assertEqual(('C:\\filename.hex', None, 10),
|
|
intelhex._get_file_and_addr_range('C:\\filename.hex::A', True))
|
|
self.assertEqual(('C:\\filename.hex', 1, 10),
|
|
intelhex._get_file_and_addr_range('C:\\filename.hex:1:A', True))
|
|
self.assertEqual(('C:\\filename.hex', 1, 10),
|
|
intelhex._get_file_and_addr_range('C:\\filename.hex:0001:000A', True))
|
|
|
|
|
|
##
|
|
# MAIN
|
|
if __name__ == '__main__':
|
|
unittest.main()
|