text: """
  Tests for PyPoE.poe.file.dat
  
  Overview
  ===============================================================================
  
  +----------+------------------------------------------------------------------+
  | Path     | tests/PyPoE/poe/file/test_dat.py                                 |
  +----------+------------------------------------------------------------------+
  | Version  | 1.0.0a0                                                          |
  +----------+------------------------------------------------------------------+
  | Revision | $Id: 56091095946e9402425a659fdd90222710f8e5cb $                                                             |
  +----------+------------------------------------------------------------------+
  | Author   | Omega_K2                                                         |
  +----------+------------------------------------------------------------------+
  
  Description
  ===============================================================================
  
  Tests for dat.py
  
  Agreement
  ===============================================================================
  
  See PyPoE/LICENSE
  
  TODO
  ===============================================================================
  
  - more/better tests for DatFile
  =============================================================================== RecordList tests
  """
  
  # =============================================================================
  # Imports
  # =============================================================================
  
  # Python
  import os
  import struct
  
  # 3rd Party
  import pytest
  
  # self
  from PyPoE.poe.constants import MOD_DOMAIN
  from PyPoE.poe.file import dat
  
  # =============================================================================
  # Setup
  # =============================================================================
  
  spec_dir = os.path.join(
      os.path.split(os.path.realpath(__file__))[0], '_data', 'specifications'
  )
  
  test_data = data = [
      ('bool', '?', 1),
      ('byte', 'b', -2**7+1),
      ('ubyte', 'B', 2**8-2),
      ('short', 'h', -2**15+1),
      ('ushort', 'H', 2**16-2),
      ('int', 'i', -2**31+1),
      ('uint', 'I', 2**32-2),
      ('long', 'q', -2**63+1),
      ('ulong', 'Q', 2**64-2),
  ]
  
  test_str = 'Hello world'
  test_str_enc = test_str.encode('utf-16le') + b'\x00\x00\x00\x00'
  test_list = [17418241, 777123, 0xFEFEFEFE]
  
  
  @pytest.fixture(scope='module')
  def testspec_dat_file():
      # One row
      temp_file = []
      baseptr = 8
      # One row
      temp_file.append(struct.pack('<I', 1))
      for item in test_data:
          temp_file.append(struct.pack('<' + item[1], item[2]))
      # ref|string -> int pointer
      temp_file.append(struct.pack('<i', baseptr))
      baseptr += len(test_str_enc)
      # ref|list -> int size, int pointer
      temp_file.append(struct.pack('<i', 3))
      temp_file.append(struct.pack('<i', baseptr))
      baseptr += 3*4
      # ref|ref|ref|int
      # The first reference in the data list
      temp_file.append(struct.pack('<i', baseptr))
      baseptr += 4
  
      # Magic number
      temp_file.append(dat.DAT_FILE_MAGIC_NUMBER)
      # Data
      temp_file.append(test_str_enc)
      for item in test_list:
          temp_file.append(struct.pack('<I', item))
  
      # ref|ref|ref|int
      # the two extra refs, and integer value
      for i in range(0, 2):
          temp_file.append(struct.pack('<i', baseptr))
          baseptr += 4
      temp_file.append(struct.pack('<i', 0x1337))
      baseptr += 4
  
      return b''.join(temp_file)
  
  
  @pytest.fixture(scope='module')
  def rr_temp_dir(tmpdir_factory):
      file_datas = (
          {
              'file_name': 'Main.dat',
              'data': (
                  (0, 1, 0, 0, 10, 1),
                  (1, 2, 1, 1, 20, 2),
                  (2, 3, 3, 0xFEFEFEFE, 30, 3),
              )
          },
          {
              'file_name': 'Other.dat',
              'data': (
                  (10, ),
                  (20, ),
                  (30, ),
              ),
          },
      )
  
      temp_dir = str(tmpdir_factory.mktemp('rrtest'))
      os.mkdir(os.path.join(temp_dir, 'Data'))
  
      for file_data in file_datas:
          with open(os.path.join(temp_dir, 'Data', file_data['file_name']),
                    'wb') as temp_file:
              temp_file.write(struct.pack('<I', len(file_data['data'])))
              for row in file_data['data']:
                  for value in row:
                      temp_file.write(struct.pack('<I', value))
              temp_file.write(dat.DAT_FILE_MAGIC_NUMBER)
  
      return temp_dir
  
  
  @pytest.fixture(scope='module')
  def rr_instance(rr_temp_dir):
      return dat.RelationalReader(
          path_or_ggpk=rr_temp_dir,
          read_options={
              'specification': dat.load_spec(os.path.join(
                  spec_dir, 'rr_test.ini'
              )),
              'use_dat_value': False,
          },
      )
  
  # =============================================================================
  # Tests
  # =============================================================================
  
  
  def test_load_spec():
      return dat.load_spec(os.path.join(spec_dir, 'dat_testspec.ini'))
  
  
  def test_reload_default_spec():
      old = dat._default_spec
      dat.reload_default_spec()
      assert id(old) != id(dat._default_spec), 'Specification wasn\'t reloaded'
  
  #
  # DatValue
  #
  # DatValue instances are created by the script, so they don't contain any
  # error checking upon creation themselves.
  # Hence we just test for them working correctly with correct information.
  
  
  class TestDatValue:
      # Basic Data
      dv_basic = dat.DatValue(value=5, offset=0, size=4, parent=None, specification=None)
      dv_basic_int_negative = dat.DatValue(value=-42, offset=0, size=4, parent=None, specification=None)
      dv_basic_int_positive = dat.DatValue(value=1337, offset=0, size=4, parent=None, specification=None)
      dv_basic_str_test = dat.DatValue(value='test', offset=0, size=4*2+4, parent=None, specification=None)
      dv_basic_str_a = dat.DatValue(value='a', offset=0, size=1*2+4, parent=None, specification=None)
      dv_basic_str_z = dat.DatValue(value='z', offset=0, size=1*2+4, parent=None, specification=None)
  
      # Raw
      dv_raw = dat.DatValue(value=b'\x00\x00', offset=0, size=2, parent=None, specification=None)
  
      # Pointers
      dv_pointer = dat.DatValue(value=8, offset=0, size=4, parent=None, specification=None)
      dv_pointer.child = dat.DatValue(value='test', offset=8, size=4*2+4, parent=dv_pointer, specification=None)
  
      dv_pointer_int_negative = dat.DatValue(value=8, offset=0, size=4, parent=None, specification=None)
      dv_pointer_int_negative.child = dat.DatValue(value=-42, offset=8, size=4, parent=dv_pointer_int_negative, specification=None)
  
      dv_pointer_int_positive = dat.DatValue(value=8, offset=0, size=4, parent=None, specification=None)
      dv_pointer_int_positive.child = dat.DatValue(value=1337, offset=8, size=4, parent=dv_pointer_int_positive, specification=None)
  
      # Lists
      dv_list = dat.DatValue(value=(3, 8), offset=0, size=8, parent=None, specification=None)
      dv_list.children = [
          dat.DatValue(value=1, offset=8, size=4, parent=dv_list, specification=None),
          dat.DatValue(value=2, offset=12, size=4, parent=dv_list, specification=None),
          dat.DatValue(value=3, offset=16, size=4, parent=dv_list, specification=None),
      ]
  
      dv_list_reversed = dat.DatValue(value=(3, 8), offset=0, size=8, parent=None, specification=None)
      dv_list_reversed.children = [
          dat.DatValue(value=3, offset=8, size=4, parent=dv_list_reversed, specification=None),
          dat.DatValue(value=2, offset=12, size=4, parent=dv_list_reversed, specification=None),
          dat.DatValue(value=1, offset=16, size=4, parent=dv_list_reversed, specification=None),
      ]
  
  
      def test_instance_basic(self):
          """
          Test for a basic DatValue instance.
          """
          # Function tests
          assert self.dv_basic.get_value() == 5, 'Value should be identical'
  
          # Property Tests
          with pytest.raises(TypeError):
              self.dv_basic.data_size
              self.dv_basic.data_start_offset
              self.dv_basic.data_end_offset
          assert self.dv_basic.is_data == False, 'Should not be data'
          assert self.dv_basic.has_data == False, 'Should not have any data'
          assert self.dv_basic.is_pointer == False, 'Should not be a pointer'
          assert self.dv_basic.is_list == False, 'Should not be a list'
          assert self.dv_basic.is_parsed == True, 'Should count as parsed value'
  
      def test_instance_raw(self):
          """
          Test for a basic DatValue instance with raw data.
          """
          # Function tests
          assert self.dv_raw.get_value() == b'\x00\x00', 'Value should be identical'
  
          # Property Tests
          with pytest.raises(TypeError):
              self.dv_raw.data_size
              self.dv_raw.data_start_offset
              self.dv_raw.data_end_offset
          assert self.dv_raw.is_data == False, 'Should not be data'
          assert self.dv_raw.has_data == False, 'Should not have any data'
          assert self.dv_raw.is_pointer == False, 'Should not be a pointer'
          assert self.dv_raw.is_list == False, 'Should not be a list'
          assert self.dv_raw.is_parsed == False, 'Should count as unparsed/binary value'
  
      def test_instance_pointer(self):
          """
          Test for a pointer DatValue instance.
          """
          # Function tests
          assert self.dv_pointer.get_value() == 'test', 'Value should be identical'
  
          # Property Tests
          assert self.dv_pointer.data_size == self.dv_pointer.child.size, 'Should return the size of the child'
          assert self.dv_pointer.data_start_offset == self.dv_pointer.value, 'Should return the self.dv_pointer.value, i.e. the pointer'
          assert self.dv_pointer.data_end_offset == self.dv_pointer.value + self.dv_pointer.data_size, 'Should be the pointer + sizeof(child)'
          assert self.dv_pointer.is_data == False, 'Should not be data'
          assert self.dv_pointer.has_data == True, 'Should have data'
          assert self.dv_pointer.is_pointer == True, 'Should be a pointer'
          assert self.dv_pointer.is_list == False, 'Should not be a list'
          assert self.dv_pointer.is_parsed == True, 'Should count as parsed value'
  
          # Child Property tests
          with pytest.raises(TypeError):
              self.dv_pointer.child.data_size
              self.dv_pointer.child.data_start_offset
              self.dv_pointer.child.data_end_offset
          assert self.dv_pointer.child.is_data == True, 'Should be data'
          assert self.dv_pointer.child.has_data == False, 'Should not have any data'
          assert self.dv_pointer.child.is_pointer == False, 'Should not be a pointer'
          assert self.dv_pointer.child.is_list == False, 'Should not be a list'
          assert self.dv_pointer.child.is_parsed == True, 'Should count as parsed value'
  
      def test_instance_list(self):
          """
          Test for a list DatValue instance.
          """
          # Function tests
          assert self.dv_list.get_value() == [1,2,3], 'Value should be identical'
  
          # Property Tests
          assert self.dv_list.data_size == self.dv_list.value[0] * self.dv_list.children[0].size, 'Should return sizeof(self) + sizeof(child)'
          assert self.dv_list.data_start_offset == self.dv_list.value[1], 'Should return the self.dv_list.value, i.e. the pointer'
          assert self.dv_list.data_end_offset == self.dv_list.value[1] + self.dv_list.data_size, 'Should be the pointer + sizeof(child)'
          assert self.dv_list.is_data == False, 'Should not be data'
          assert self.dv_list.has_data == True, 'Should have data'
          assert self.dv_list.is_pointer == False, 'Should not be a pointer'
          assert self.dv_list.is_list == True, 'Should be a list'
          assert self.dv_list.is_parsed == True, 'Should count as parsed value'
  
          # Child property tests
          for i in range(0, len(self.dv_list.children)):
              child = self.dv_list.children[i]
              with pytest.raises(TypeError):
                  child.data_size
                  child.data_start_offset
                  child.data_end_offset
              assert child.is_data == True, 'Should be data'
              assert child.has_data == False, 'Should not have any data'
              assert child.is_pointer == False, 'Should not be a pointer'
              assert child.is_list == False, 'Should not be a list'
              assert child.is_parsed == True, 'Should count as parsed value'
  
      # comprehensions are performed on the values, not the datvalues themselves
  
      types = ['__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__']
      cmp_tests = []
  
      # Basic Tests
      basic_tests = [
          [dv_basic_int_negative, dv_basic],
          [dv_basic, dv_basic_int_positive],
          [dv_basic_int_negative, 0],
          [dv_basic_int_negative, dv_pointer_int_positive],
          [dv_basic_str_a, dv_basic_str_z],
          [dv_basic_str_a, 'test'],
          [dv_basic_str_a, dv_pointer],
          [dv_list, dv_list_reversed],
          [dv_list, [8]],
      ]
      for item in zip(types, [True, True, False, True, False, False]):
          item = list(item)
          for vars in basic_tests:
              cmp_tests.append(item + vars)
  
      @pytest.mark.parametrize('cmp_type,result,a,b', cmp_tests)
      def test_cmp(self, cmp_type, result, a, b):
          assert getattr(a, cmp_type)(b) == result, '%s for %s and %s should return %s' % (cmp_type, a, b, result)
  
  
  #
  # DatFile tests
  #
  
  def test_dat_file(testspec_dat_file):
      spec = test_load_spec()
  
      df = dat.DatFile('TestSpec.dat')
      dr = df.read(testspec_dat_file, specification=spec)
  
      for row in dr:
          for test in test_data:
              assert row[test[0]] == test[2], 'Value mismatch - int'
          assert row['ref|string'] == test_str, 'Value mismatch - string'
          l = row['ref|list|int']
          assert l[0] == test_list[0], 'Value mismatch - list'
          assert l[1] == test_list[1], 'Value mismatch - list'
          # 0xFEFEFEFE is a magic key, so return -1
          assert l[2] is None, 'Value mismatch - list, special value'
          assert row['ref|ref|ref|int'] == 0x1337, 'Value mismatch - nested pointers'
  
  
  class TestSpecificationErrors:
      errors = (
          (
              'invalid_foreign_key_file.ini',
              dat.SpecificationError.ERRORS.INVALID_FOREIGN_KEY_FILE,
          ),
          (
              'invalid_foreign_key_id.ini',
              dat.SpecificationError.ERRORS.INVALID_FOREIGN_KEY_ID,
          ),
          (
              'invalid_argument_combination.ini',
              dat.SpecificationError.ERRORS.INVALID_ARGUMENT_COMBINATION,
          ),
          (
              'invalid_enum_name.ini',
              dat.SpecificationError.ERRORS.INVALID_ENUM_NAME,
          ),
          (
              'virtual_key_empty.ini',
              dat.SpecificationError.ERRORS.VIRTUAL_KEY_EMPTY,
          ),
          (
              'virtual_key_duplicate.ini',
              dat.SpecificationError.ERRORS.VIRTUAL_KEY_DUPLICATE,
          ),
          (
              'virtual_key_invalid_key.ini',
              dat.SpecificationError.ERRORS.VIRTUAL_KEY_INVALID_KEY,
          ),
          (
              'virtual_key_invalid_data_type.ini',
              dat.SpecificationError.ERRORS.VIRTUAL_KEY_INVALID_DATA_TYPE,
          ),
  
      )
  
      @pytest.mark.parametrize('file_name,error', errors)
      def test_validation_errors(self, file_name, error):
          with pytest.raises(dat.SpecificationError) as e:
              dat.load_spec(os.path.join(spec_dir, file_name))
          assert e.value.code == error
  
      def test_runtime_missing_specification(self, testspec_dat_file):
          df = dat.DatFile('TestSpec.dat')
          with pytest.raises(dat.SpecificationError) as e:
              dr = df.read(testspec_dat_file)
          assert e.value.code == \
                 dat.SpecificationError.ERRORS.RUNTIME_MISSING_SPECIFICATION
  
      def test_runtime_rowsize_mismatch(self, rr_temp_dir):
          df = dat.DatFile('Main.dat')
          with pytest.raises(dat.SpecificationError) as e:
              dr = df.read(
                  os.path.join(rr_temp_dir, 'Data', 'Main.dat'),
                  specification=dat.load_spec(os.path.join(
                      spec_dir, 'runtime_rowsize_mismatch.ini'
                  )),
              )
  
          assert e.value.code == \
              dat.SpecificationError.ERRORS.RUNTIME_ROWSIZE_MISMATCH
  
  
      foreign_key_errors = (
          'runtime_missing_foreign_key1.ini',
          'runtime_missing_foreign_key2.ini',
      )
  
      @pytest.mark.parametrize('spec_name', foreign_key_errors)
      def test_runtime_missing_foreign_key(self, rr_temp_dir, spec_name):
          rr = dat.RelationalReader(
              path_or_ggpk=rr_temp_dir,
              raise_error_on_missing_relation=True,
              read_options={
                  'specification': dat.load_spec(os.path.join(
                      spec_dir, spec_name
                  )),
                  'use_dat_value': False,
              },
          )
          with pytest.raises(dat.SpecificationError) as e:
              rr.get_file('Data/Main.dat')
          assert e.value.code == \
              dat.SpecificationError.ERRORS.RUNTIME_MISSING_FOREIGN_KEY
  
  
  class TestRelationalReader():
      relations_expected = {
          'ForeignKey': [0, 1, 2],
          'ForeignKeyOffset': [0, 1, 2],
          'ForeignKeyNone': [0, 1, None],
          'ForeignKeyCellValue': [0, 1, 2],
      }
  
      @pytest.mark.parametrize('use_dat_value', (True, False))
      def test_relations(self, rr_temp_dir, use_dat_value):
          rr = dat.RelationalReader(
              path_or_ggpk=rr_temp_dir,
              read_options={
                  'specification': dat.load_spec(os.path.join(
                      spec_dir, 'rr_test.ini'
                  )),
                  'use_dat_value': use_dat_value,
              },
          )
  
          for column, values in self.relations_expected.items():
              for i, row in enumerate(rr['Main.dat']):
                  expected = values[i]
                  if expected is not None:
                      expected = rr['Other.dat'][expected]
                  else:
                      expected = None
  
                  assert row[column] == expected, 'Testing against expected row'
  
      enums_expected = {
          'ConstTest': (MOD_DOMAIN(1), MOD_DOMAIN(2), MOD_DOMAIN(3)),
      }
  
      @pytest.mark.parametrize('use_dat_value', (True, False))
      def test_enums(self, rr_temp_dir, use_dat_value):
          rr = dat.RelationalReader(
              path_or_ggpk=rr_temp_dir,
              read_options={
                  'specification': dat.load_spec(os.path.join(
                      spec_dir, 'rr_test.ini'
                  )),
                  'use_dat_value': use_dat_value,
              },
          )
          for column, values in self.enums_expected.items():
              for i, row in enumerate(rr['Main.dat']):
                  assert row[column] == values[i], 'Testing against expected enum'
  
      def test_getitem(self, rr_instance):
          assert rr_instance['Main.dat'] == \
                 rr_instance.get_file('Data/Main.dat').reader