From f2eff01529430265c6707e394eca6641c000cda8 Mon Sep 17 00:00:00 2001 From: JanLJL Date: Thu, 29 Aug 2019 16:36:14 +0200 Subject: [PATCH] more tests and bugfixes --- osaca/api/db_interface.py | 4 +- osaca/semantics/hw_model.py | 3 +- tests/all_tests.py | 3 +- tests/test_db_interface.py | 113 ++++++++++++++++++++++++++++++++ tests/test_frontend.py | 3 +- tests/test_marker_utils.py | 3 +- tests/test_parser_AArch64v81.py | 3 +- tests/test_parser_x86att.py | 3 +- tests/test_semantics.py | 49 +++++++++++++- 9 files changed, 175 insertions(+), 9 deletions(-) create mode 100755 tests/test_db_interface.py diff --git a/osaca/api/db_interface.py b/osaca/api/db_interface.py index 88dac2d..5bcdac8 100755 --- a/osaca/api/db_interface.py +++ b/osaca/api/db_interface.py @@ -21,7 +21,7 @@ def add_entry_to_db(arch: str, entry): """ # load yaml arch = arch.lower() - filepath = os.path.join(os.path.expanduser('~/.osaca/data/', arch + '.yml')) + filepath = os.path.join(os.path.expanduser('~/.osaca/data/' + arch + '.yml')) assert os.path.exists(filepath) with open(filepath, 'r') as f: data = yaml.load(f, Loader=yaml.Loader) @@ -52,7 +52,7 @@ def add_entries_to_db(arch: str, entries: list) -> None: """ # load yaml arch = arch.lower() - filepath = os.path.join(os.path.expanduser('~/.osaca/data/', arch + '.yml')) + filepath = os.path.join(os.path.expanduser('~/.osaca/data/' + arch + '.yml')) assert os.path.exists(filepath) with open(filepath, 'r') as f: data = yaml.load(f, Loader=yaml.Loader) diff --git a/osaca/semantics/hw_model.py b/osaca/semantics/hw_model.py index 217be43..655e3d4 100755 --- a/osaca/semantics/hw_model.py +++ b/osaca/semantics/hw_model.py @@ -27,9 +27,10 @@ class MachineModel(object): ) elif path_to_yaml: try: + assert os.path.exists(self._path) with open(self._path, 'r') as f: self._data = yaml.load(f, Loader=yaml.Loader) - except AssertionError: + except (AssertionError, FileNotFoundError): raise ValueError( 'Cannot find specified path to YAML file. Make sure the machine file exists.' ) diff --git a/tests/all_tests.py b/tests/all_tests.py index 87860b5..374a8be 100755 --- a/tests/all_tests.py +++ b/tests/all_tests.py @@ -10,7 +10,8 @@ suite = unittest.TestLoader().loadTestsFromNames( 'test_parser_AArch64v81', 'test_marker_utils', 'test_semantics', - 'test_frontend' + 'test_frontend', + 'test_db_interface', ] ) diff --git a/tests/test_db_interface.py b/tests/test_db_interface.py new file mode 100755 index 0000000..2adf13f --- /dev/null +++ b/tests/test_db_interface.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +""" +Unit tests for DB interface +""" + +import copy +import os +import sys +import unittest + +from osaca.api import add_entries_to_db, add_entry_to_db, sanity_check +from osaca.semantics import MachineModel + + +class TestDBInterface(unittest.TestCase): + @classmethod + def setUpClass(self): + sample_entry = { + 'name': 'DoItRightAndDoItFast', + 'operands': [ + {'class': 'memory', 'offset': 'imd', 'base': 'gpr', 'index': 'gpr', 'scale': 8}, + {'class': 'register', 'name': 'xmm'}, + ], + 'throughput': 1.25, + 'latency': 125, + 'uops': 6, + } + self.entry_csx = sample_entry.copy() + self.entry_vulcan = sample_entry.copy() + self.entry_zen1 = sample_entry.copy() + + self.entry_csx['port_pressure'] = [1.25, 0, 1.25, 0.5, 0.5, 0.5, 0.5, 0, 1.25, 1.25, 0] + self.entry_vulcan['port_pressure'] = [2.5, 2.5, 0, 0, 0.5, 0.5] + del(self.entry_vulcan['operands'][1]['name']) + self.entry_vulcan['operands'][1]['prefix'] = 'x' + self.entry_zen1['port_pressure'] = [1, 1, 1, 1, 0, 1, 0, 0, 0, 0.5, 1, 0.5, 1] + + @classmethod + def tearDownClass(self): + if sys.exc_info() == (None, None, None): + # Test successful, remove DB entries + for arch in ['csx', 'vulcan', 'zen1']: + lines = [] + with open(os.path.expanduser('~/.osaca/data/' + arch + '.yml'), 'r') as f: + lines = f.readlines() + with open(os.path.expanduser('~/.osaca/data/' + arch + '.yml'), 'w') as f: + f.writelines([line for line in lines[:-24]]) + + ########### + # Tests + ########### + + def test_add_single_entry(self): + num_entries_csx = len(MachineModel('csx')['instruction_forms']) + num_entries_vulcan = len(MachineModel('vulcan')['instruction_forms']) + num_entries_zen1 = len(MachineModel('zen1')['instruction_forms']) + + add_entry_to_db('csx', self.entry_csx) + add_entry_to_db('vulcan', self.entry_vulcan) + add_entry_to_db('zen1', self.entry_zen1) + + num_entries_csx = len(MachineModel('csx')['instruction_forms']) - num_entries_csx + num_entries_vulcan = len(MachineModel('vulcan')['instruction_forms']) - num_entries_vulcan + num_entries_zen1 = len(MachineModel('zen1')['instruction_forms']) - num_entries_zen1 + + self.assertEqual(num_entries_csx, 1) + self.assertEqual(num_entries_vulcan, 1) + self.assertEqual(num_entries_zen1, 1) + + def test_add_multiple_entries(self): + num_entries_csx = len(MachineModel('csx')['instruction_forms']) + num_entries_vulcan = len(MachineModel('vulcan')['instruction_forms']) + num_entries_zen1 = len(MachineModel('zen1')['instruction_forms']) + + entries_csx, entries_vulcan, entries_zen1 = [], [], [] + for i in range(2): + self.entry_csx['name'] += '-' + self.entry_vulcan['name'] += '-' + self.entry_zen1['name'] += '-' + entries_csx.append(copy.deepcopy(self.entry_csx)) + entries_vulcan.append(copy.deepcopy(self.entry_vulcan)) + entries_zen1.append(copy.deepcopy(self.entry_zen1)) + + add_entries_to_db('csx', entries_csx) + add_entries_to_db('vulcan', entries_vulcan) + add_entries_to_db('zen1', entries_zen1) + + num_entries_csx = len(MachineModel('csx')['instruction_forms']) - num_entries_csx + num_entries_vulcan = len(MachineModel('vulcan')['instruction_forms']) - num_entries_vulcan + num_entries_zen1 = len(MachineModel('zen1')['instruction_forms']) - num_entries_zen1 + + self.assertEqual(num_entries_csx, 2) + self.assertEqual(num_entries_vulcan, 2) + self.assertEqual(num_entries_zen1, 2) + + def test_sanity_check(self): + # non-verbose + sanity_check('csx', verbose=False) + sanity_check('vulcan', verbose=False) + sanity_check('zen1', verbose=False) + # verbose + sanity_check('csx', verbose=True) + sanity_check('vulcan', verbose=True) + sanity_check('zen1', verbose=True) + + ################## + # Helper functions + ################## + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TestDBInterface) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_frontend.py b/tests/test_frontend.py index 6ffe04f..eb644ef 100755 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -18,7 +18,8 @@ class TestFrontend(unittest.TestCase): os.path.dirname(os.path.split(os.path.abspath(__file__))[0]), 'osaca/data/' ) - def setUp(self): + @classmethod + def setUpClass(self): # set up parser and kernels self.parser_x86 = ParserX86ATT() self.parser_AArch64 = ParserAArch64v81() diff --git a/tests/test_marker_utils.py b/tests/test_marker_utils.py index 0d38c0e..3d2297c 100755 --- a/tests/test_marker_utils.py +++ b/tests/test_marker_utils.py @@ -10,7 +10,8 @@ from osaca.parser import ParserAArch64v81, ParserX86ATT class TestMarkerUtils(unittest.TestCase): - def setUp(self): + @classmethod + def setUpClass(self): self.parser_AArch = ParserAArch64v81() self.parser_x86 = ParserX86ATT() with open(self._find_file('triad-arm-iaca.s')) as f: diff --git a/tests/test_parser_AArch64v81.py b/tests/test_parser_AArch64v81.py index 5a807c9..9dd03b0 100755 --- a/tests/test_parser_AArch64v81.py +++ b/tests/test_parser_AArch64v81.py @@ -12,7 +12,8 @@ from osaca.parser import AttrDict, ParserAArch64v81 class TestParserAArch64v81(unittest.TestCase): - def setUp(self): + @classmethod + def setUpClass(self): self.parser = ParserAArch64v81() with open(self._find_file('triad-arm-iaca.s')) as f: self.triad_code = f.read() diff --git a/tests/test_parser_x86att.py b/tests/test_parser_x86att.py index 9ec2b67..76943a1 100755 --- a/tests/test_parser_x86att.py +++ b/tests/test_parser_x86att.py @@ -12,7 +12,8 @@ from osaca.parser import AttrDict, ParserX86ATT class TestParserX86ATT(unittest.TestCase): - def setUp(self): + @classmethod + def setUpClass(self): self.parser = ParserX86ATT() with open(self._find_file('triad-x86-iaca.s')) as f: self.triad_code = f.read() diff --git a/tests/test_semantics.py b/tests/test_semantics.py index d9a2bd4..a6db1e4 100755 --- a/tests/test_semantics.py +++ b/tests/test_semantics.py @@ -21,7 +21,8 @@ class TestSemanticTools(unittest.TestCase): ) USER_DATA_DIR = os.path.join(os.path.expanduser('~'), '.osaca/') - def setUp(self): + @classmethod + def setUpClass(self): # copy db files in user directory if not os.path.isdir(os.path.join(self.USER_DATA_DIR, 'data')): os.makedirs(os.path.join(self.USER_DATA_DIR, 'data')) @@ -50,6 +51,8 @@ class TestSemanticTools(unittest.TestCase): self.machine_model_tx2, path_to_yaml=os.path.join(self.MODULE_DATA_DIR, 'isa/aarch64.yml'), ) + self.machine_model_zen = MachineModel(arch='zen1') + for i in range(len(self.kernel_x86)): self.semantics_csx.assign_src_dst(self.kernel_x86[i]) self.semantics_csx.assign_tp_lt(self.kernel_x86[i]) @@ -85,6 +88,7 @@ class TestSemanticTools(unittest.TestCase): self.assertTrue('src_dst' in instruction_form['operands']) def test_tp_lt_assignment_x86(self): + self.assertTrue('ports' in self.machine_model_csx) port_num = len(self.machine_model_csx['ports']) for instruction_form in self.kernel_x86: with self.subTest(instruction_form=instruction_form): @@ -94,6 +98,7 @@ class TestSemanticTools(unittest.TestCase): self.assertEqual(len(instruction_form['port_pressure']), port_num) def test_tp_lt_assignment_AArch64(self): + self.assertTrue('ports' in self.machine_model_tx2) port_num = len(self.machine_model_tx2['ports']) for instruction_form in self.kernel_AArch64: with self.subTest(instruction_form=instruction_form): @@ -237,6 +242,48 @@ class TestSemanticTools(unittest.TestCase): self.assertTrue(dag.is_written(reg, instr_form_non_rw_1)) self.assertTrue(dag.is_written(reg, instr_form_non_rw_1)) + def test_invalid_MachineModel(self): + with self.assertRaises(ValueError): + MachineModel() + with self.assertRaises(ValueError): + MachineModel(arch='CSX', path_to_yaml=os.path.join(self.MODULE_DATA_DIR, 'csx.yml')) + with self.assertRaises(ValueError): + MachineModel(arch='THE_MACHINE') + with self.assertRaises(ValueError): + MachineModel(path_to_yaml=os.path.join(self.MODULE_DATA_DIR, 'THE_MACHINE.yml')) + + def test_MachineModel_getter(self): + sample_operands = [ + { + 'memory': { + 'offset': None, + 'base': {'name': 'r12'}, + 'index': {'name': 'rcx'}, + 'scale': 8, + } + } + ] + self.assertIsNone(self.machine_model_csx.get_instruction('GETRESULT', sample_operands)) + self.assertIsNone(self.machine_model_tx2.get_instruction('GETRESULT', sample_operands)) + + self.assertEqual(self.machine_model_csx.get_arch(), 'CSX') + self.assertEqual(self.machine_model_tx2.get_arch(), 'Vulcan') + + self.assertEqual(self.machine_model_csx.get_ISA(), 'x86') + self.assertEqual(self.machine_model_tx2.get_ISA(), 'AArch64') + + ports_csx = ['0', '0DV', '1', '2', '2D', '3', '3D', '4', '5', '6', '7'] + data_ports_csx = ['2D', '3D'] + self.assertEqual(self.machine_model_csx.get_ports(), ports_csx) + self.assertEqual(self.machine_model_csx.get_data_ports(), data_ports_csx) + + self.assertFalse(self.machine_model_tx2.has_hidden_loads()) + self.assertTrue(self.machine_model_zen.has_hidden_loads()) + + self.assertEqual(MachineModel.get_isa_for_arch('CSX'), 'x86') + self.assertEqual(MachineModel.get_isa_for_arch('VuLcAn'), 'aarch64') + self.assertIsNone(MachineModel.get_isa_for_arch('THE_MACHINE')) + ################## # Helper functions ##################