def test_incorrect_sized_chunks(self):
     assembler = util.DataAssembler(3)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00' * (assembler.alignment + 1), 0)
     assembler.add(b'\xff\xfe\xfd', 3)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00' * (assembler.alignment + 1), 0)
     with self.assertRaises(util.UnexpectedChunkLengthError):
         assembler.add(b'\x00' * (assembler.alignment - 1), 0)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00' * (assembler.alignment + 1), 6)
     self.assertEqual(b'\x00\x00\x00\xff\xfe\xfd',
                      assembler.getbytes(incomplete=True))
     # Test with predefined length
     assembler = util.DataAssembler(3, length=5)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00\x00', 0)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00\x00', 3)
     with self.assertRaises(util.UnexpectedChunkLengthError):
         assembler.add(b'\x00', 3)
     with self.assertRaises(util.UnexpectedChunkLengthError):
         assembler.add(b'\x00\x00\x00', 3)
     assembler.add(b'\xba\xbe', 3)
     self.assertEqual(b'\x00\x00\x00\xba\xbe',
                      assembler.getbytes(incomplete=True))
     assembler = util.DataAssembler(3, length=6)
     with self.assertRaises(util.UnexpectedChunkLengthError):
         assembler.add(b'\x00', 3)
     with self.assertRaises(util.UnexpectedChunkLengthError):
         assembler.add(b'\x00\x00', 3)
     assembler.add(b'\xfe\xba\xbe', 3)
     self.assertEqual(b'\x00\x00\x00\xfe\xba\xbe',
                      assembler.getbytes(incomplete=True))
 def _test_unsized_add(self, length, alignment):
     data, chunks = self._generate_data(length, alignment)
     # Test all possible permutations
     for sequence in itertools.permutations(chunks):
         assembler = util.DataAssembler(alignment)
         for chunk in sequence:
             assembler.add(chunk.data, chunk.offset)
         self.assertEqual(data, assembler.getbytes())
         # Same test with add_chunk()
         assembler = util.DataAssembler(alignment)
         for chunk in sequence:
             assembler.add_chunk(chunk)
         self.assertEqual(data, assembler.getbytes())
 def test_out_of_bound_offsets(self):
     assembler = util.DataAssembler(3)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00', -1)
     assembler = util.DataAssembler(3, length=3)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00', -1)
     with self.assertRaises(util.ChunkPastEndError):
         assembler.add(b'\x00\x00\x00', 3)
     assembler = util.DataAssembler(3, length=4)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00', -1)
     with self.assertRaises(util.ChunkPastEndError):
         assembler.add(b'\x00\x00\x00', 6)
    def test_length_deduction(self):
        assembler = util.DataAssembler(3)
        self.assertIsNone(assembler.length)
        assembler.add(b'\xff\xfe', 0)
        self.assertEqual(2, assembler.length)
        self.assertEqual(b'\xff\xfe', assembler.getbytes())

        assembler = util.DataAssembler(3)
        self.assertIsNone(assembler.length)
        assembler.add(b'\xff\xfe\xfd', 0)
        self.assertIsNone(assembler.length)
        assembler.add(b'\x01\x02', 6)
        self.assertEqual(8, assembler.length)
        assembler.add(b'\xfc\xfb\x00', 3)
        self.assertEqual(b'\xff\xfe\xfd\xfc\xfb\x00\x01\x02',
                         assembler.getbytes())
 def _update_request_data(self, data, offset):
   if self.request_data is None:
     self.request_data = util.DataAssembler(30, length=self.request_length)
   try:
     self.request_data.add(data, offset)
   except util.UnexpectedChunkError:
     self.collision = True
     return
   if self.request_data.length is not None:
     self._update_request_length(self.request_data.length)
 def test_getbytes_incomplete(self):
     assembler = util.DataAssembler(2, length=5)
     with self.assertRaises(util.IncompleteDataError):
         assembler.getbytes()
     self.assertEqual(b'\x00\x00\x00\x00\x00',
                      assembler.getbytes(incomplete=True))
     assembler.add(b'\x01\x02', 2)
     with self.assertRaises(util.IncompleteDataError):
         assembler.getbytes()
     self.assertEqual(b'\x00\x00\x01\x02\x00',
                      assembler.getbytes(incomplete=True))
 def test_maligned_offsets(self):
     assembler = util.DataAssembler(3)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00', 1)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00', 2)
     assembler.add(b'\xca\xfe\xba', 0)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00', 4)
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00', 5)
     assembler.add(b'\xbe\x00\xff', 3)
     self.assertEqual(b'\xca\xfe\xba\xbe\x00\xff', assembler.getbytes())
 def test_init_bad_args(self):
     with self.assertRaises(TypeError):
         util.DataAssembler(None)  # alignment must be int
     with self.assertRaises(TypeError):
         util.DataAssembler(1.1)  # alignment must be int
     with self.assertRaises(ValueError):
         util.DataAssembler(-1)  # alignment must be positive
     with self.assertRaises(ValueError):
         util.DataAssembler(0)  # alignment must be positive
     with self.assertRaises(TypeError):
         util.DataAssembler(1, length=1.1)  # size must be int or None
     with self.assertRaises(TypeError):
         util.DataAssembler(1, length='asdf')  # size must be int or None
     with self.assertRaises(ValueError):
         util.DataAssembler(1, length=-1)  # size must be non-negative
  def _update_response_data(self, segment_length, segment_offset, chunk):
    if self.response_data is None:
      self.response_data = util.DataAssembler(3, length=self.response_length)
    # Response data is queried by split segments. chunk is a piece of data
    # within one of the segments. In order to reassemble the entire data, we
    # must first construct each segment from chunks, then assemble segments into
    # final data. Fortunately, the known implementation uses segment_length=48,
    # which is a multiple of 3; therefore a single DataAssembler can be used.
    absolute_offset = segment_offset + chunk.offset
    try:
      self.response_data.add(chunk.data, absolute_offset)
    except util.UnexpectedChunkError:
      self.collision = True
      return

    if segment_length < 48:
      try:
        self.response_data.length = segment_offset + segment_length
      except ValueError:
        self.collision = True
    if self.response_data.length is not None:
      self._update_response_length(self.response_data.length)
 def test_collisions(self):
     assembler = util.DataAssembler(3)
     assembler.add(b'\x00\x01\x00', 3)
     with self.assertRaises(util.ChunkCollisionError):
         assembler.add(b'\x00\x02\x00', 3)
     assembler.add(b'\x00\x01\x00', 3)  # same content
     assembler.add(b'\x00\x03\x00', 0)
     with self.assertRaises(util.ChunkCollisionError):
         assembler.add(b'\x00\x04\x00', 0)
     assembler.add(b'\x00\x03\x00', 0)  # same content
     assembler.add(b'\x00\x05', 6)
     with self.assertRaises(util.ChunkCollisionError):
         assembler.add(b'\x00\x06', 6)
     with self.assertRaises(util.ChunkCollisionError):
         assembler.add(b'\x00', 6)
     with self.assertRaises(util.ChunkCollisionError):
         assembler.add(b'\x00\x00\x00', 6)
     # Collision with too long chunks should be handled as ValueError.
     # Note that the previous add has determined the length the whole data.
     with self.assertRaises(ValueError):
         assembler.add(b'\x00\x00\x00\x00', 6)
     assembler.add(b'\x00\x05', 6)  # same content
     self.assertEqual(b'\x00\x03\x00\x00\x01\x00\x00\x05',
                      assembler.getbytes())