def setUp(self): self.channel = MockChannel() self.rkl = ramkernel.RAMKernelProtocol(self.channel) # Pretend we did initialize the chip already so we # can test commands. # Ensuring that these are set will be done in separate tests. self.rkl._flash_init = True self.rkl._kernel_init = True
class RAMKernelTests(unittest.TestCase): def setUp(self): self.channel = MockChannel() self.rkl = ramkernel.RAMKernelProtocol(self.channel) # Pretend we did initialize the chip already so we # can test commands. # Ensuring that these are set will be done in separate tests. self.rkl._flash_init = True self.rkl._kernel_init = True def test_kernel_not_yet_init_exception(self): """ Test that working with an uninitialized kernel raises KernelNotInitializedError """ rkl = ramkernel.RAMKernelProtocol(self.channel) for command in (rkl.getver, rkl.flash_get_capacity): with self.assertRaises(ramkernel.KernelNotInitializedError) as cm: command() def test_flash_init_error(self): self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_INIT, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_initial() self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_INIT) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_INITIAL) self.assertEqual(cm.exception.length, 0) def test_flash_get_capacity(self): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 2057) self.assertEqual(self.rkl.flash_get_capacity(), 2057) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 0x1FFFF) self.assertEqual(self.rkl.flash_get_capacity(), 0x1FFFF) def test_getver(self): # Test CMD_GETVER responses of various shapes and sizes, including # with payloads containing non-ASCII data. for imx_version, flash_model in ( (0xface, b"HAL 9000"), (0xbeef, b"Taste the biscuit"), (0x2057, b"Silly rabbit Freescale's for kids"), (0xfeed, b"So I should \x1bprobably sleep\xAF\x99 at some point \x00tonight"), ): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, imx_version, len(flash_model), flash_model) ver, flash = self.rkl.getver() self.assertEqual(ver, imx_version) self.assertEqual(flash, flash_model) def test_erase(self): """ Test the flash_erase API with no callback specified. """ block_size = 0x20000 for block_index in range(50): self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE, block_index, block_size) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) self.rkl.flash_erase(0x0, 1) def test_erase_callback(self): """ Test flash_erase with erase_callback specified. """ callback_data = [] block_size = 0x20000 # Queue 50 block erase responses, each with a different block size. # Generally the block size returned by RKL is fixed, but if it is not, # we should be sending the right value. for block_index in range(50): self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE, block_index, block_size + block_index) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) # Close over callback_data to make sure it is called correctly. def erase_cb(block_idx, block_sz): callback_data.append((block_idx, block_sz)) # Flash erase doesn't care how many bytes you specify, or the start address. # We're just testing how it handles RKL proto responses. self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb) self.assertEqual(len(callback_data), 50) for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data): self.assertEqual(cb_block_idx, index) self.assertEqual(cb_block_sz, block_size + index) def test_erase_partial_error(self): """ Test flash_erase error handling - partial erase """ callback_data = [] block_size = 0x20000 # queue up 25 good partial responses followed by an error. # The callback should be called for each of the 25 good responses. for block_index in range(25): self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE, block_index, block_size + block_index) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PART_ERASE, 0, 0) # Close over callback_data to make sure it is called correctly. def erase_cb(block_idx, block_sz): callback_data.append((block_idx, block_sz)) # Ensure an exception is raised with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PART_ERASE) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE) self.assertEqual(len(callback_data), 25) for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data): self.assertEqual(cb_block_idx, index) self.assertEqual(cb_block_sz, block_size + index) def test_erase_initial_error(self): """ Test flash_erase error handling - no blocks erased """ callback_data = [] self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0) # Close over callback_data to make sure it is called correctly. def erase_cb(block_idx, block_sz): callback_data.append((block_idx, block_sz)) # Ensure an exception is raised with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE) # The callback should never have been called. self.assertEqual(len(callback_data), 0) def test_flash_dump(self): """ Ensure flash_dump in normal operation works correctly - multiple FLASH_PARTLY responses, checksum validation, etc. """ data = b"testing random \x00 data 1 2 \x03" cksum = ramkernel.calculate_checksum(data) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data), data) # NOTE: the RKL main() never sends ACK_SUCCESS for CMD_FLASH_DUMP - only # a sequence of ACK_FLASH_PARTLY responses. # Don't care about the address or requested size, we're mocking it dump_data = self.rkl.flash_dump(0x0000, len(data)) self.assertEqual(data, dump_data) test_data = ( b"testing random \x00 data 1 2 \x03", b"cold wind to valhalla", b"\xff" * 1024, b"\x00" * 2048 ) # Multipart for data_chunk in test_data: cksum = ramkernel.calculate_checksum(data_chunk) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data_chunk), data_chunk) total_test_data = b"".join(test_data) # Don't care about the address. dump_data = self.rkl.flash_dump(0x0000, len(total_test_data)) self.assertEqual(total_test_data, dump_data) def test_flash_dump_checksum_error(self): """ Ensure flash_dump handles checksum errors correctly, by raising ramkernel.ChecksumError. flash_dump should set ChecksumError internal values correctly. """ data = b"testing random \x00 data 1 2 \x03" real_cksum = ramkernel.calculate_checksum(data) fake_cksum = (real_cksum + 1) & 0xFFFF self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum, len(data), data) with self.assertRaises(ramkernel.ChecksumError) as cm: # Don't care about the address or requested size, we're mocking it self.rkl.flash_dump(0x0000, len(data)) self.assertEqual(cm.exception.expected_checksum, fake_cksum) self.assertEqual(cm.exception.checksum, real_cksum) ## Test with initial successes, then a failed checksum test_data = ( b"testing random \x00 data 1 2 \x03", b"cold wind to valhalla", b"\xff" * 1024, b"\x00" * 2048 ) for data_chunk in test_data: # Calculate the real checksum for these chunks cksum = ramkernel.calculate_checksum(data_chunk) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data_chunk), data_chunk) # Now for the bad checksum... data = b"testing random \x00 data 1 2 \x03" real_cksum = ramkernel.calculate_checksum(data) fake_cksum = (real_cksum + 1) & 0xFFFF self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum, len(data), data) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) total_data_length = len(b"".join(test_data + (data,))) with self.assertRaises(ramkernel.ChecksumError) as cm: self.rkl.flash_dump(0x0000, total_data_length) self.assertEqual(cm.exception.expected_checksum, fake_cksum) self.assertEqual(cm.exception.checksum, real_cksum) def test_flash_dump_partial_error(self): """ Ensure flash_dump handles partial read errors correctly. """ data = b"testing random \x00 data 1 2 \x03" cksum = ramkernel.calculate_checksum(data) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data), data) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_READ, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: # We ask for more size than the first response gives, so we try to read again # and encounter the FLASH_ERROR_READ. self.rkl.flash_dump(0x0000, len(data) * 2) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_READ) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP) def test_flash_dump_initial_error(self): """ Ensure flash_dump handles initial read error correctly. """ self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: # Don't care about the address or requested size, we're mocking it self.rkl.flash_dump(0x0000, 0x400) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP) def test_flash_program_arguments(self): """ Test sanity checks on flash_program """ class AVeryLargeObject(object): def __init__(self, length): self.length = length def __len__(self): return self.length self.assertRaises(ValueError, self.rkl.flash_program, -1, "asdf") self.assertRaises(ValueError, self.rkl.flash_program, 0, AVeryLargeObject(ramkernel.FLASH_PROGRAM_MAX_WRITE_SIZE + 1)) self.assertRaises(ValueError, self.rkl.flash_program, 0, "") self.assertRaises(ValueError, self.rkl.flash_program, 0, "asdf", file_format = -1) def test_flash_program(self): data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na" partial_data_len = len(data) // 2 def queue_responses(): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1, len(data) - partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) queue_responses() self.rkl.flash_program(0x0000, data) # Now again, with a callback defined. callback_data = [] def prog_cb(block, length_written): callback_data.append((block, length_written)) queue_responses() self.rkl.flash_program(0x0000, data, program_callback = prog_cb) self.assertEqual(len(callback_data), 2) self.assertEqual(callback_data[0][0], 0) self.assertEqual(callback_data[0][1], partial_data_len) self.assertEqual(callback_data[1][0], 1) self.assertEqual(callback_data[1][1], len(data) - partial_data_len) def test_flash_program_verify(self): data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na" partial_data_len = len(data) // 2 def queue_responses(): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1, len(data) - partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 0, partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 1, len(data) - partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) queue_responses() self.rkl.flash_program(0x0000, data, read_back_verify=True) # Now again, with a callback defined. prog_cb_data = [] verify_cb_data = [] def prog_cb(block, length_written): prog_cb_data.append((block, length_written)) def verify_cb(block, length_verified): verify_cb_data.append((block, length_verified)) queue_responses() self.rkl.flash_program(0x0000, data, read_back_verify = True, program_callback = prog_cb, verify_callback = verify_cb) for cb_data in prog_cb_data, verify_cb_data: self.assertEqual(len(cb_data), 2) self.assertEqual(cb_data[0][0], 0) self.assertEqual(cb_data[0][1], partial_data_len) self.assertEqual(cb_data[1][0], 1) self.assertEqual(cb_data[1][1], len(data) - partial_data_len) def test_flash_program_initial_error(self): """ Test flash_dump with an initial error raises CommandResponseError """ self.channel.queue_rkl_response(ramkernel.FLASH_FAILED, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: # Don't care about the address or requested size, we're mocking it self.rkl.flash_program(0x0000, "asdf") self.assertEqual(cm.exception.ack, ramkernel.FLASH_FAILED) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM) def test_flash_program_partial_error(self): """ Ensure flash_program handles partial program errors correctly. """ data = "testing random \x00 data 1 2 \x03" self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_program(0x0000, data) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, len(data) - 1) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_program(0x0000, data) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)
class RAMKernelTests(unittest.TestCase): def setUp(self): self.channel = MockChannel() self.rkl = ramkernel.RAMKernelProtocol(self.channel) # Pretend we did initialize the chip already so we # can test commands. # Ensuring that these are set will be done in separate tests. self.rkl._flash_init = True self.rkl._kernel_init = True def test_kernel_not_yet_init_exception(self): """ Test that working with an uninitialized kernel raises KernelNotInitializedError """ rkl = ramkernel.RAMKernelProtocol(self.channel) for command in (rkl.getver, rkl.flash_get_capacity): with self.assertRaises(ramkernel.KernelNotInitializedError) as cm: command() def test_flash_init_error(self): self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_INIT, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_initial() self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_INIT) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_INITIAL) self.assertEqual(cm.exception.length, 0) def test_flash_get_capacity(self): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 2057) self.assertEqual(self.rkl.flash_get_capacity(), 2057) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 0x1FFFF) self.assertEqual(self.rkl.flash_get_capacity(), 0x1FFFF) def test_getver(self): # Test CMD_GETVER responses of various shapes and sizes, including # with payloads containing non-ASCII data. for imx_version, flash_model in ( (0xface, b"HAL 9000"), (0xbeef, b"Taste the biscuit"), (0x2057, b"Silly rabbit Freescale's for kids"), (0xfeed, b"So I should \x1bprobably sleep\xAF\x99 at some point \x00tonight" ), ): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, imx_version, len(flash_model), flash_model) ver, flash = self.rkl.getver() self.assertEqual(ver, imx_version) self.assertEqual(flash, flash_model) def test_erase(self): """ Test the flash_erase API with no callback specified. """ block_size = 0x20000 for block_index in range(50): self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE, block_index, block_size) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) self.rkl.flash_erase(0x0, 1) def test_erase_callback(self): """ Test flash_erase with erase_callback specified. """ callback_data = [] block_size = 0x20000 # Queue 50 block erase responses, each with a different block size. # Generally the block size returned by RKL is fixed, but if it is not, # we should be sending the right value. for block_index in range(50): self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE, block_index, block_size + block_index) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) # Close over callback_data to make sure it is called correctly. def erase_cb(block_idx, block_sz): callback_data.append((block_idx, block_sz)) # Flash erase doesn't care how many bytes you specify, or the start address. # We're just testing how it handles RKL proto responses. self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb) self.assertEqual(len(callback_data), 50) for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data): self.assertEqual(cb_block_idx, index) self.assertEqual(cb_block_sz, block_size + index) def test_erase_partial_error(self): """ Test flash_erase error handling - partial erase """ callback_data = [] block_size = 0x20000 # queue up 25 good partial responses followed by an error. # The callback should be called for each of the 25 good responses. for block_index in range(25): self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE, block_index, block_size + block_index) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PART_ERASE, 0, 0) # Close over callback_data to make sure it is called correctly. def erase_cb(block_idx, block_sz): callback_data.append((block_idx, block_sz)) # Ensure an exception is raised with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PART_ERASE) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE) self.assertEqual(len(callback_data), 25) for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data): self.assertEqual(cb_block_idx, index) self.assertEqual(cb_block_sz, block_size + index) def test_erase_initial_error(self): """ Test flash_erase error handling - no blocks erased """ callback_data = [] self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0) # Close over callback_data to make sure it is called correctly. def erase_cb(block_idx, block_sz): callback_data.append((block_idx, block_sz)) # Ensure an exception is raised with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE) # The callback should never have been called. self.assertEqual(len(callback_data), 0) def test_flash_dump(self): """ Ensure flash_dump in normal operation works correctly - multiple FLASH_PARTLY responses, checksum validation, etc. """ data = b"testing random \x00 data 1 2 \x03" cksum = ramkernel.calculate_checksum(data) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data), data) # NOTE: the RKL main() never sends ACK_SUCCESS for CMD_FLASH_DUMP - only # a sequence of ACK_FLASH_PARTLY responses. # Don't care about the address or requested size, we're mocking it dump_data = self.rkl.flash_dump(0x0000, len(data)) self.assertEqual(data, dump_data) test_data = (b"testing random \x00 data 1 2 \x03", b"cold wind to valhalla", b"\xff" * 1024, b"\x00" * 2048) # Multipart for data_chunk in test_data: cksum = ramkernel.calculate_checksum(data_chunk) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data_chunk), data_chunk) total_test_data = b"".join(test_data) # Don't care about the address. dump_data = self.rkl.flash_dump(0x0000, len(total_test_data)) self.assertEqual(total_test_data, dump_data) def test_flash_dump_checksum_error(self): """ Ensure flash_dump handles checksum errors correctly, by raising ramkernel.ChecksumError. flash_dump should set ChecksumError internal values correctly. """ data = b"testing random \x00 data 1 2 \x03" real_cksum = ramkernel.calculate_checksum(data) fake_cksum = (real_cksum + 1) & 0xFFFF self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum, len(data), data) with self.assertRaises(ramkernel.ChecksumError) as cm: # Don't care about the address or requested size, we're mocking it self.rkl.flash_dump(0x0000, len(data)) self.assertEqual(cm.exception.expected_checksum, fake_cksum) self.assertEqual(cm.exception.checksum, real_cksum) ## Test with initial successes, then a failed checksum test_data = (b"testing random \x00 data 1 2 \x03", b"cold wind to valhalla", b"\xff" * 1024, b"\x00" * 2048) for data_chunk in test_data: # Calculate the real checksum for these chunks cksum = ramkernel.calculate_checksum(data_chunk) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data_chunk), data_chunk) # Now for the bad checksum... data = b"testing random \x00 data 1 2 \x03" real_cksum = ramkernel.calculate_checksum(data) fake_cksum = (real_cksum + 1) & 0xFFFF self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum, len(data), data) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) total_data_length = len(b"".join(test_data + (data, ))) with self.assertRaises(ramkernel.ChecksumError) as cm: self.rkl.flash_dump(0x0000, total_data_length) self.assertEqual(cm.exception.expected_checksum, fake_cksum) self.assertEqual(cm.exception.checksum, real_cksum) def test_flash_dump_partial_error(self): """ Ensure flash_dump handles partial read errors correctly. """ data = b"testing random \x00 data 1 2 \x03" cksum = ramkernel.calculate_checksum(data) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data), data) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_READ, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: # We ask for more size than the first response gives, so we try to read again # and encounter the FLASH_ERROR_READ. self.rkl.flash_dump(0x0000, len(data) * 2) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_READ) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP) def test_flash_dump_initial_error(self): """ Ensure flash_dump handles initial read error correctly. """ self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: # Don't care about the address or requested size, we're mocking it self.rkl.flash_dump(0x0000, 0x400) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP) def test_flash_program_arguments(self): """ Test sanity checks on flash_program """ class AVeryLargeObject(object): def __init__(self, length): self.length = length def __len__(self): return self.length self.assertRaises(ValueError, self.rkl.flash_program, -1, "asdf") self.assertRaises( ValueError, self.rkl.flash_program, 0, AVeryLargeObject(ramkernel.FLASH_PROGRAM_MAX_WRITE_SIZE + 1)) self.assertRaises(ValueError, self.rkl.flash_program, 0, "") self.assertRaises(ValueError, self.rkl.flash_program, 0, "asdf", file_format=-1) def test_flash_program(self): data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na" partial_data_len = len(data) // 2 def queue_responses(): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1, len(data) - partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) queue_responses() self.rkl.flash_program(0x0000, data) # Now again, with a callback defined. callback_data = [] def prog_cb(block, length_written): callback_data.append((block, length_written)) queue_responses() self.rkl.flash_program(0x0000, data, program_callback=prog_cb) self.assertEqual(len(callback_data), 2) self.assertEqual(callback_data[0][0], 0) self.assertEqual(callback_data[0][1], partial_data_len) self.assertEqual(callback_data[1][0], 1) self.assertEqual(callback_data[1][1], len(data) - partial_data_len) def test_flash_program_verify(self): data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na" partial_data_len = len(data) // 2 def queue_responses(): self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1, len(data) - partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 0, partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 1, len(data) - partial_data_len) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0) queue_responses() self.rkl.flash_program(0x0000, data, read_back_verify=True) # Now again, with a callback defined. prog_cb_data = [] verify_cb_data = [] def prog_cb(block, length_written): prog_cb_data.append((block, length_written)) def verify_cb(block, length_verified): verify_cb_data.append((block, length_verified)) queue_responses() self.rkl.flash_program(0x0000, data, read_back_verify=True, program_callback=prog_cb, verify_callback=verify_cb) for cb_data in prog_cb_data, verify_cb_data: self.assertEqual(len(cb_data), 2) self.assertEqual(cb_data[0][0], 0) self.assertEqual(cb_data[0][1], partial_data_len) self.assertEqual(cb_data[1][0], 1) self.assertEqual(cb_data[1][1], len(data) - partial_data_len) def test_flash_program_initial_error(self): """ Test flash_dump with an initial error raises CommandResponseError """ self.channel.queue_rkl_response(ramkernel.FLASH_FAILED, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: # Don't care about the address or requested size, we're mocking it self.rkl.flash_program(0x0000, "asdf") self.assertEqual(cm.exception.ack, ramkernel.FLASH_FAILED) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM) def test_flash_program_partial_error(self): """ Ensure flash_program handles partial program errors correctly. """ data = "testing random \x00 data 1 2 \x03" self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_program(0x0000, data) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM) self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data)) self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, len(data) - 1) self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0) with self.assertRaises(ramkernel.CommandResponseError) as cm: self.rkl.flash_program(0x0000, data) self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG) self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)
def setUp(self): self.channel = MockChannel() self.sbp = boot.SerialBootProtocol(self.channel)
class SerialBootProtocolTests(unittest.TestCase): def setUp(self): self.channel = MockChannel() self.sbp = boot.SerialBootProtocol(self.channel) def test_get_status_string(self): self.assertEqual("Successful operation complete", boot.get_status_string(boot.HAB_PASSED)) self.assertEqual("Failure not matching any other description", boot.get_status_string(boot.HAB_FAILURE)) self.assertEqual("Unknown code 0x5643beef", boot.get_status_string(0x5643BEEF)) def queue_sbp_resp(self, resp): self.channel.queue_data(struct.pack(">I", resp)) def queue_ack_prod(self): self.queue_sbp_resp(boot.ACK_PRODUCTION_PART) def queue_ack_eng(self): self.queue_sbp_resp(boot.ACK_ENGINEERING_PART) def test_get_status(self): """ Test decoding SBP status """ self.channel.queue_data(b"\xff\xff\xff\xff") status = self.sbp.get_status() self.assertEqual(0xffffffff, status) self.channel.queue_data(b"\xef\xbe\xad\xde") status = self.sbp.get_status() self.assertEqual(0xdeadbeef, status) def test_read_memory_invalid_address_size(self): """ Test raising ValueError on calling read_memory() with invalid address and/or access width. """ for address, datasize in ( (-1, boot.DATA_SIZE_BYTE), (0, 99), (0xffffffff + 1, boot.DATA_SIZE_WORD), ): self.assertRaises(ValueError, self.sbp.read_memory, address, datasize) def test_read_memory_halfword(self): """ Test basic functionality of read_memory(). """ # Queue up the response self.queue_ack_eng() self.channel.queue_data("\xaa\xbb") ret = self.sbp.read_memory(0x25, boot.DATA_SIZE_HALFWORD, 1) # Ensure we get an array of the correct type with the correctly byte-swapped # value. self.assertEqual(array.array('H', [0xbbaa]), ret) # Ensure we send the correct command data, aligned to 16 bytes. self.assertEqual(b"\x01\x01\x00\x00\x00\x25\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00", self.channel.get_data_written()) def test_read_memory_word_multiple(self): """ Test basic functionality of read_memory(). """ # Queue up the response self.queue_ack_eng() self.channel.queue_data("\x01\x00\x00\x00\x02\x00\x00\x00") ret = self.sbp.read_memory(0x99, boot.DATA_SIZE_WORD, 2) # Ensure we get an array of the correct type with the correctly byte-swapped # value. self.assertEqual(array.array('I', [1, 2]), ret) # Ensure we send the correct command data, aligned to 16 bytes. self.assertEqual(b"\x01\x01\x00\x00\x00\x99\x20\x00\x00\x00\x02\x00\x00\x00\x00\x00", self.channel.get_data_written()) def test_read_memory_word_multiple_byteswap(self): """ Test basic functionality of read_memory() with byte swapping. """ self.queue_ack_eng() # Switch processor byte order to the opposite byte order of # the host system to ensure we need to byte swap if "little" == sys.byteorder: self.sbp.byteorder = "big" self.channel.queue_data("\x00\x00\x00\x01\x00\x00\x00\x02") else: self.sbp.byteorder = "little" self.channel.queue_data("\x01\x00\x00\x00\x02\x00\x00\x00") ret = self.sbp.read_memory(0x99, boot.DATA_SIZE_WORD, 2) # Ensure we get an array of the correct type with the correctly byte-swapped # value. self.assertEqual(array.array('I', [1, 2]), ret) # Ensure we send the correct command data, aligned to 16 bytes. self.assertEqual(b"\x01\x01\x00\x00\x00\x99\x20\x00\x00\x00\x02\x00\x00\x00\x00\x00", self.channel.get_data_written()) def test_read_memory_short_response(self): """ Test error functionality of read_memory() with not enough data read. """ self.queue_ack_eng() self.assertRaises(boot.CommandResponseError, self.sbp.read_memory, 0x99, boot.DATA_SIZE_WORD, 2) def test_read_memory_single(self): """ Test basic functionality of read_memory(). """ # Queue up the response self.queue_ack_eng() self.channel.queue_data("\xaa\xbb") ret = self.sbp.read_memory_single(0x25, boot.DATA_SIZE_HALFWORD) # Ensure we get an array of the correct type with the correctly byte-swapped # value. self.assertEqual(0xbbaa, ret) # Ensure we send the correct command data, aligned to 16 bytes. self.assertEqual(b"\x01\x01\x00\x00\x00\x25\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00", self.channel.get_data_written()) def test_complete_boot(self): """ Test the Completed command for the serial boot protocol. """ self.queue_sbp_resp(boot.BOOT_PROTOCOL_COMPLETE) self.assertEqual(boot.BOOT_PROTOCOL_COMPLETE, self.sbp._complete_boot()) # Doesn't matter what was written, as long as it is 16 bytes! self.assertEqual(16, len(self.channel.get_data_written())) def test_complete_boot_bad_response(self): """ Test the Completed command for the serial boot protocol. """ self.queue_sbp_resp(boot.HAB_PASSED) self.assertRaises(boot.CommandResponseError, self.sbp._complete_boot) # Doesn't matter what was written, as long as it is 16 bytes! self.assertEqual(16, len(self.channel.get_data_written())) def test_write_memory_byte(self): # Queue ACK_ENGINEERING_PART self.queue_ack_eng() # Queue ACK_WRITE self.queue_sbp_resp(boot.ACK_WRITE_SUCCESS) self.sbp.write_memory(0xbeefcafe, boot.DATA_SIZE_BYTE, 0x01) self.assertEqual(b"\x02\x02\xbe\xef\xca\xfe\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00", self.channel.get_data_written()) def test_write_memory_halfword(self): # Queue ACK_ENGINEERING_PART self.queue_ack_eng() # Queue ACK_WRITE self.queue_sbp_resp(boot.ACK_WRITE_SUCCESS) self.sbp.write_memory(0xbeefcafe, boot.DATA_SIZE_HALFWORD, 0xfeed) self.assertEqual(b"\x02\x02\xbe\xef\xca\xfe\x10\x00\x00\x00\x00\x00\x00\xfe\xed\x00", self.channel.get_data_written()) def test_write_memory_word(self): # Queue ACK_ENGINEERING_PART self.queue_ack_eng() # Queue ACK_WRITE self.queue_sbp_resp(boot.ACK_WRITE_SUCCESS) self.sbp.write_memory(0xbeefcafe, boot.DATA_SIZE_WORD, 0xcafefeed) self.assertEqual(b"\x02\x02\xbe\xef\xca\xfe\x20\x00\x00\x00\x00\xca\xfe\xfe\xed\x00", self.channel.get_data_written())