| # Check that the LNT REST JSON API is working. |
| # create temporary instance |
| # RUN: rm -rf %t.instance |
| # RUN: python %{shared_inputs}/create_temp_instance.py \ |
| # RUN: %s %{shared_inputs}/SmallInstance \ |
| # RUN: %t.instance %S/Inputs/V4Pages_extra_records.sql |
| # |
| # RUN: python %s %t.instance |
| |
| from V4Pages import check_json |
| import lnt.server.db.migrate |
| import lnt.server.ui.app |
| import logging |
| import sys |
| import unittest |
| import yaml |
| |
| logging.basicConfig(level=logging.DEBUG) |
| |
| machines_expected_response = [{u'hardware': u'x86_64', |
| u'os': u'Darwin 11.3.0', |
| u'id': 1, |
| u'name': u'localhost__clang_DEV__x86_64', |
| u'hostname': u'localhost', |
| u'uname': u'Darwin localhost 11.3.0 Darwin Kernel Version 11.3.0: Thu Jan 12' |
| u' 18:47:41 PST 2012; root:xnu-1699.24.23~1/RELEASE_X86_64 x86_64'}, |
| {u'hardware': u'AArch64', |
| u'os': u'linux', |
| u'id': 2, |
| u'name': u'machine2'}, |
| {u'hardware': u'AArch64', |
| u'os': u'linux', |
| u'id': 3, |
| u'name': u'machine3'}] |
| |
| order_expected_response = {u'llvm_project_revision': u'154331', |
| u'id': 1} |
| |
| sample_expected_response = {u'id': 1, |
| u'execution_time': 0.0003, |
| u'test_id': 1, |
| u'compile_time': 0.007} |
| |
| graph_data = [[u'152292', 1.0, |
| {u'date': u'2012-05-01 16:28:23', |
| u'label': u'152292', |
| u'runID': u'5'}], |
| [u'152293', 10.0, |
| {u'date': u'2012-05-03 16:28:24', |
| u'label': u'152293', |
| u'runID': u'6'}]] |
| |
| graph_data2 = [[u'152293', 10.0, |
| {u'date': u'2012-05-03 16:28:24', |
| u'label': u'152293', |
| u'runID': u'6'}]] |
| |
| possible_run_keys = { |
| u'ARCH', |
| u'CC_UNDER_TEST_IS_CLANG', |
| u'CC_UNDER_TEST_TARGET_IS_X86_64', |
| u'DISABLE_CBE', |
| u'DISABLE_JIT', |
| u'ENABLE_HASHED_PROGRAM_OUTPUT', |
| u'ENABLE_OPTIMIZED', |
| u'LLC_OPTFLAGS', |
| u'LLI_OPTFLAGS', |
| u'OPTFLAGS', |
| u'TARGET_CC', |
| u'TARGET_CXX', |
| u'TARGET_FLAGS', |
| u'TARGET_LLVMGCC', |
| u'TARGET_LLVMGXX', |
| u'TEST', |
| u'USE_REFERENCE_OUTPUT', |
| u'__report_version__', |
| u'cc1_exec_hash', |
| u'cc_alt_src_branch', |
| u'cc_alt_src_revision', |
| u'cc_as_version', |
| u'cc_build', |
| u'cc_exec_hash', |
| u'cc_ld_version', |
| u'cc_name', |
| u'cc_src_branch', |
| u'cc_src_revision', |
| u'cc_target', |
| u'cc_version', |
| u'cc_version_number', |
| u'end_time', |
| u'id', |
| u'inferred_run_order', |
| u'llvm_project_revision', |
| u'no_errors', |
| u'order_by', |
| u'order_id', |
| u'start_time', |
| u'sw_vers', |
| u'test_suite_revision', |
| } |
| |
| possible_machine_keys = {u'name', |
| u'hostname', |
| u'hardware', |
| u'os', |
| u'id', |
| u'uname'} |
| |
| |
| class JSONAPITester(unittest.TestCase): |
| """Test the REST api.""" |
| |
| def setUp(self): |
| """Bind to the LNT test instance.""" |
| _, instance_path = sys.argv |
| self.instance_path = instance_path |
| app = lnt.server.ui.app.App.create_standalone(instance_path) |
| app.testing = True |
| self.client = app.test_client() |
| |
| def _check_response_is_well_formed(self, response): |
| """API Should always return the generated by field in the top level dict.""" |
| # All API calls should return a top level dict. |
| self.assertEqual(type(response), dict) |
| |
| # There should be no unexpected top level keys. |
| all_top_level_keys = {'generated_by', 'machine', 'machines', 'runs', 'run', 'orders', 'tests', 'samples'} |
| keys = set(response.keys()) |
| self.assertTrue(keys.issubset(all_top_level_keys), |
| "{} not subset of {}".format(keys, all_top_level_keys)) |
| # All API calls should return as generated by. |
| self.assertIn("LNT Server v", response['generated_by']) |
| |
| def test_machine_api(self): |
| """Check /machines/ and /machines/n return expected results from testdb. |
| """ |
| client = self.client |
| |
| # All machines returns the list of machines with parameters, but no runs. |
| j = check_json(client, 'api/db_default/v4/nts/machines/') |
| self._check_response_is_well_formed(j) |
| self.assertEqual(j['machines'], machines_expected_response) |
| self.assertIsNone(j.get('runs')) |
| |
| j = check_json(client, 'api/db_default/v4/nts/machines') |
| self._check_response_is_well_formed(j) |
| self.assertEqual(j['machines'], machines_expected_response) |
| self.assertIsNone(j.get('runs')) |
| |
| # Machine + properties + run information. |
| j = check_json(client, 'api/db_default/v4/nts/machines/1') |
| self._check_response_is_well_formed(j) |
| self.assertEqual(j['machine'], machines_expected_response[0]) |
| |
| self.assertEqual(len(j['runs']), 2) |
| for run in j['runs']: |
| self.assertSetEqual(set(run.keys()), possible_run_keys) |
| |
| # Specify machine by name |
| j = check_json(client, 'api/db_default/v4/nts/machines/localhost__clang_DEV__x86_64') |
| self._check_response_is_well_formed(j) |
| self.assertEqual(j['machine'], machines_expected_response[0]) |
| |
| # Invalid machine ids are 404. |
| check_json(client, 'api/db_default/v4/nts/machines/99', expected_code=404) |
| check_json(client, 'api/db_default/v4/nts/machines/foo', expected_code=404) |
| |
| def test_run_api(self): |
| """Check /runs/n returns expected run information.""" |
| client = self.client |
| j = check_json(client, 'api/db_default/v4/nts/runs/1') |
| self._check_response_is_well_formed(j) |
| expected = {"end_time": "2012-04-11T16:28:58", |
| "start_time": "2012-04-11T16:28:23", |
| "id": 1, |
| "llvm_project_revision": u'154331'} |
| self.assertEqual(j['run'], j['run'] | expected) |
| self.assertEqual(len(j['tests']), 2) |
| # This should not be a run. |
| check_json(client, 'api/db_default/v4/nts/runs/100', expected_code=404) |
| |
| def test_order_api(self): |
| """Check /orders/n returns the expected order information.""" |
| client = self.client |
| j = check_json(client, 'api/db_default/v4/nts/orders/1') |
| self.assertEqual(j['orders'][0], order_expected_response) |
| self._check_response_is_well_formed(j) |
| check_json(client, 'api/db_default/v4/nts/orders/100', expected_code=404) |
| |
| def test_single_sample_api(self): |
| """Check /samples/n returns the expected sample information.""" |
| client = self.client |
| j = check_json(client, 'api/db_default/v4/nts/samples/1') |
| self._check_response_is_well_formed(j) |
| self.assertEqual(sample_expected_response, j['samples'][0]) |
| check_json(client, 'api/db_default/v4/nts/samples/1000', expected_code=404) |
| |
| def test_graph_api(self): |
| """Check that /graph/x/y/z returns what we expect.""" |
| client = self.client |
| |
| j = check_json(client, 'api/db_default/v4/nts/graph/2/4/2') |
| # TODO: Graph API needs redesign to be well formed. |
| # self._check_response_is_well_formed(j) |
| self.assertEqual(graph_data, j) |
| |
| # Now check that limit works. |
| j2 = check_json(client, 'api/db_default/v4/nts/graph/2/4/2?limit=1') |
| # self._check_response_is_well_formed(j) |
| self.assertEqual(graph_data2, j2) |
| |
| def test_samples_api(self): |
| """Samples API.""" |
| client = self.client |
| # Run IDs must be passed, so 400 if they are not. |
| check_json(client, 'api/db_default/v4/nts/samples', |
| expected_code=400) |
| |
| # Simple single run. |
| j = check_json(client, 'api/db_default/v4/nts/samples?runid=1') |
| self._check_response_is_well_formed(j) |
| expected = [ |
| {u'compile_time': 0.007, u'llvm_project_revision': u'154331', |
| u'name': u'SingleSource/UnitTests/2006-12-01-float_varg', |
| u'run_id': 1, u'execution_time': 0.0003, u'id': 1}, |
| {u'compile_time': 0.0072, u'llvm_project_revision': u'154331', |
| u'name': u'SingleSource/UnitTests/2006-12-04-DynAllocAndRestore', |
| u'run_id': 1, |
| u'execution_time': 0.0003, |
| u'id': 2}] |
| |
| self.assertEqual(j['samples'], expected) |
| |
| # Check that other args are ignored. |
| extra_param = check_json(client, |
| 'api/db_default/v4/nts/samples?runid=1&foo=bar') |
| self._check_response_is_well_formed(extra_param) |
| self.assertEqual(j, extra_param) |
| # There is only one run in the DB. |
| two_runs = check_json(client, |
| 'api/db_default/v4/nts/samples?runid=1&runid=2') |
| self._check_response_is_well_formed(two_runs) |
| self.assertEqual(j, two_runs) |
| |
| def test_fields_api(self): |
| """Fields API.""" |
| client = self.client |
| j = check_json(client, 'api/db_default/v4/nts/fields') |
| |
| fields = j['fields'] |
| |
| # check number of fields |
| self.assertEqual(9, len(fields)) |
| |
| # check first field |
| f0 = fields[0] |
| self.assertEqual(0, f0['column_id']) |
| self.assertEqual('compile_time', f0['column_name']) |
| self.assertEqual('FLOAT', f0['column_type']) |
| |
| def test_tests_api(self): |
| """Tests API.""" |
| client = self.client |
| j = check_json(client, 'api/db_default/v4/nts/tests') |
| |
| tests = j['tests'] |
| |
| # check number of tests |
| self.assertEqual(9, len(tests)) |
| |
| # check first test |
| t0 = tests[0] |
| self.assertEqual(1, t0['id']) |
| self.assertEqual('SingleSource/UnitTests/2006-12-01-float_varg', t0['name']) |
| |
| def test_schema(self): |
| client = self.client |
| rest_schema = check_json(client, 'api/db_default/v4/nts/schema') |
| |
| # The reported schema should be the same as the yaml one on the top. |
| with open('%s/schemas/nts.yaml' % self.instance_path) as syaml: |
| yaml_schema = yaml.safe_load(syaml) |
| # Do some massaging to make it similar to the rest API result. |
| for m in yaml_schema['metrics']: |
| if 'unit' not in m: |
| m['unit'] = None |
| if 'unit_abbrev' not in m: |
| m['unit_abbrev'] = None |
| if 'display_name' not in m: |
| m['display_name'] = m['name'] |
| if 'bigger_is_better' not in m: |
| m['bigger_is_better'] = False |
| yaml_schema['metrics'].sort(key=lambda x: x['name']) |
| yaml_schema['run_fields'].sort(key=lambda x: x['name']) |
| yaml_schema['machine_fields'].sort(key=lambda x: x['name']) |
| self.assertEqual(rest_schema, yaml_schema) |
| |
| |
| if __name__ == '__main__': |
| unittest.main(argv=[sys.argv[0], ]) |