#!/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()