Beispiel #1
0
    def setUp(self):
        """ Start all external services """
        # Get all clients
        self.database_client = DatabaseClient()
        self.database_client.connect()
        try:
            self.queue_client, self.listener = main()
        except ConnectionException as err:
            raise RuntimeError(
                "Could not connect to ActiveMQ - check you credentials. If running locally check that "
                "ActiveMQ is running and started by `python setup.py start`"
            ) from err

        # Add placeholder variables:
        # these are used to ensure runs are deleted even if test fails before completion
        self.instrument = 'ARMI'
        self.rb_number = 1234567
        self.run_number = 101

        # Create test archive and add data
        self.data_archive = DataArchive([self.instrument], 19, 19)
        self.data_archive.create()

        # Create and send json message to ActiveMQ
        self.data_ready_message = Message(rb_number=self.rb_number,
                                          instrument=self.instrument,
                                          run_number=self.run_number,
                                          description="This is a system test",
                                          facility="ISIS",
                                          started_by=0)
 def setUp(self) -> None:
     if Path(ARCHIVE_ROOT).exists():
         rmtree(ARCHIVE_ROOT)
     self.data_archive = DataArchive(["test"], 19, 20)
     self.expected_cycle_path = Path(get_project_root(), "data-archive", "NDXtest", "Instrument", "data")
     self.expected_script_path = Path(
         Path(get_project_root(), "data-archive", "NDXtest", "user", "scripts", "autoreduction"))
Beispiel #3
0
 def setUpClass(cls):
     super().setUpClass()
     cls.instrument_name = "TestInstrument"
     cls.data_archive = DataArchive([cls.instrument_name], 21, 21)
     cls.data_archive.create()
     cls.data_archive.add_reduce_vars_script(cls.instrument_name,
                                             """standard_vars={"variable1":"test_variable_value_123"}""")
Beispiel #4
0
 def setUpClass(cls):
     super().setUpClass()
     cls.instrument_name = "TestInstrument"
     cls.data_archive = DataArchive([cls.instrument_name], 21, 21)
     cls.data_archive.create()
     cls.data_archive.add_reduction_script(cls.instrument_name,
                                           """print('some text')""")
     # NOTE the value here must match the value of the last variable in the fixture
     # if it doesn't it's value will be updated once the previous range (100151-100199) is updated
     # this is expected, as the final variable (valid for 100200 onwards) should have the default script value!
     cls.data_archive.add_reduce_vars_script(
         cls.instrument_name, """standard_vars={"variable1":"value4"}""")
    def test_alert_message_when_reduce_vars_has_error(self):
        """
        Test that the correct message is shown when the reduce_vars.py has an error in it
        """
        data_archive = DataArchive([self.instrument_name], 21, 21)
        data_archive.create()

        # add a reduce_vars script with a syntax error -> a missing " after value1
        data_archive.add_reduce_vars_script(self.instrument_name, """standard_vars={"variable1":"value1}""")

        self.page.launch()
        expected = "The buttons above have been disabled because reduce_vars.py has an import or syntax error."
        assert self.page.alert_message_text() == expected

        data_archive.delete()
    def setUpClass(cls):
        """ Start all external services """
        super().setUpClass()
        cls.database_client = DatabaseClient()
        cls.database_client.connect()
        try:
            cls.queue_client, cls.listener = main()
        except ConnectionException as err:
            raise RuntimeError(
                "Could not connect to ActiveMQ - check you credentials. If running locally check that "
                "ActiveMQ is running and started by `python setup.py start`"
            ) from err

        cls.instrument_name = "TestInstrument"
        cls.rb_number = 1234567
        cls.run_number = 99999

        cls.data_archive = DataArchive([cls.instrument_name], 21, 21)
        cls.data_archive.create()
        cls.data_archive.add_reduce_vars_script(
            cls.instrument_name,
            """standard_vars={"variable1":"test_variable_value_123"}""")
Beispiel #7
0
    def setUpClass(cls):
        super().setUpClass()
        cls.instrument_name = "TestInstrument"
        cls.data_archive = DataArchive([cls.instrument_name], 21, 21)
        cls.data_archive.create()
        cls.data_archive.add_reduction_script(cls.instrument_name,
                                              """print('some text')""")
        cls.data_archive.add_reduce_vars_script(
            cls.instrument_name,
            f"""standard_vars={{"variable1":"{REDUCE_VARS_DEFAULT_VALUE}"}}""")
        cls.database_client = DatabaseClient()
        cls.database_client.connect()
        try:
            cls.queue_client, cls.listener = main()
        except ConnectionException as err:
            raise RuntimeError(
                "Could not connect to ActiveMQ - check you credentials. If running locally check that "
                "ActiveMQ is running and started by `python setup.py start`"
            ) from err

        cls.instrument_name = "TestInstrument"
        cls.rb_number = 1234567
        cls.run_number = 99999
Beispiel #8
0
class TestEndToEnd(unittest.TestCase):
    """ Class to test pipelines in autoreduction"""
    def setUp(self):
        """ Start all external services """
        # Get all clients
        self.database_client = DatabaseClient()
        self.database_client.connect()
        try:
            self.queue_client, self.listener = main()
        except ConnectionException as err:
            raise RuntimeError(
                "Could not connect to ActiveMQ - check you credentials. If running locally check that "
                "ActiveMQ is running and started by `python setup.py start`"
            ) from err

        # Add placeholder variables:
        # these are used to ensure runs are deleted even if test fails before completion
        self.instrument = 'ARMI'
        self.rb_number = 1234567
        self.run_number = 101

        # Create test archive and add data
        self.data_archive = DataArchive([self.instrument], 19, 19)
        self.data_archive.create()

        # Create and send json message to ActiveMQ
        self.data_ready_message = Message(rb_number=self.rb_number,
                                          instrument=self.instrument,
                                          run_number=self.run_number,
                                          description="This is a system test",
                                          facility="ISIS",
                                          started_by=0)

    def tearDown(self):
        """ Disconnect from services, stop external services and delete data archive """
        self.queue_client.disconnect()
        self.database_client.disconnect()
        self._remove_run_from_database(self.instrument, self.run_number)
        self.data_archive.delete()

        self._delete_reduction_directory()

    def _setup_data_structures(self, reduce_script, vars_script):
        """
        Sets up a fake archive and reduced data save location on the system
        :param reduce_script: The content to use in the reduce.py file
        :param vars_script:  The content to use in the reduce_vars.py file
        :return: file_path to the reduced data
        """
        raw_file = '{}{}.nxs'.format(self.instrument, self.run_number)
        self.data_archive.add_reduction_script(self.instrument, reduce_script)
        self.data_archive.add_reduce_vars_script(self.instrument, vars_script)
        raw_file = self.data_archive.add_data_file(self.instrument, raw_file,
                                                   19, 1)
        return raw_file

    def _find_run_in_database(self):
        """
        Find a ReductionRun record in the database
        This includes a timeout to wait for several seconds to ensure the database has received
        the record in question
        :return: The resulting record
        """
        instrument = db.get_instrument(self.instrument)
        return instrument.reduction_runs.filter(run_number=self.run_number)

    @staticmethod
    def _remove_run_from_database(instrument, run_number):
        """
        Uses the scripts.manual_operations.manual_remove script
        to remove records added to the database
        """
        if not isinstance(run_number, list):
            run_number = [run_number]
        for run in run_number:
            remove.remove(instrument, run, delete_all_versions=True)

    @staticmethod
    def _delete_reduction_directory():
        """ Delete the temporary reduction directory"""
        path = Path(os.path.join(PROJECT_ROOT, 'reduced-data'))
        if path.exists():
            shutil.rmtree(path.absolute())

    def send_and_wait_for_result(self, message):
        """Sends the message to the queue and waits until the listener has finished processing it"""
        # forces the is_processing to return True so that the listener has time to actually start processing the message
        self.listener._processing = True  #pylint:disable=protected-access
        self.queue_client.send('/queue/DataReady', message)
        while self.listener.is_processing_message():
            time.sleep(0.5)

        # Get Result from database
        results = self._find_run_in_database()

        assert results
        return results

    def test_end_to_end_wish_invalid_rb_number_skipped(self):
        """
        Test that data gets skipped when the RB Number doesn't validate
        """
        # Set meta data for test
        self.rb_number = 222
        self.data_ready_message.rb_number = self.rb_number

        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=REDUCE_SCRIPT, vars_script='')
        self.data_ready_message.data = file_location
        results = self.send_and_wait_for_result(self.data_ready_message)

        # Validate
        self.assertEqual(self.instrument, results[0].instrument.name)
        self.assertEqual(self.rb_number,
                         results[0].experiment.reference_number)
        self.assertEqual(self.run_number, results[0].run_number)
        self.assertEqual("This is a system test", results[0].run_name)
        self.assertEqual('Skipped', results[0].status.value_verbose())

    def test_end_to_end_wish_completed(self):
        """
        Test that runs gets completed when everything is OK
        """
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=REDUCE_SCRIPT, vars_script='')
        self.data_ready_message.data = file_location
        results = self.send_and_wait_for_result(self.data_ready_message)

        # Validate
        self.assertEqual(self.instrument, results[0].instrument.name)
        self.assertEqual(self.rb_number,
                         results[0].experiment.reference_number)
        self.assertEqual(self.run_number, results[0].run_number)
        self.assertEqual("This is a system test", results[0].run_name)
        self.assertEqual('Completed', results[0].status.value_verbose())

    def test_end_to_end_wish_bad_script_syntax_error(self):
        """
        Test that run gets marked as error when the script has a syntax error
        """
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=SYNTAX_ERROR_REDUCE_SCRIPT, vars_script='')
        self.data_ready_message.data = file_location
        results = self.send_and_wait_for_result(self.data_ready_message)

        # Validate
        self.assertEqual(self.instrument, results[0].instrument.name)
        self.assertEqual(self.rb_number,
                         results[0].experiment.reference_number)
        self.assertEqual(self.run_number, results[0].run_number)
        self.assertEqual("This is a system test", results[0].run_name)
        self.assertEqual('Error', results[0].status.value_verbose())

        self.assertIn("REDUCTION Error", results[0].message)
        self.assertIn("Error encountered when running the reduction script",
                      results[0].message)
        self.assertIn("SyntaxError('EOL while scanning string literal'",
                      results[0].message)

    def test_end_to_end_wish_bad_script_raises_exception(self):
        """
        Test that WISH data goes through the system without issue
        """
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script="raise ValueError('hello from the other side')",
            vars_script='')
        self.data_ready_message.data = file_location
        results = self.send_and_wait_for_result(self.data_ready_message)

        # Validate
        self.assertEqual(self.instrument, results[0].instrument.name)
        self.assertEqual(self.rb_number,
                         results[0].experiment.reference_number)
        self.assertEqual(self.run_number, results[0].run_number)
        self.assertEqual("This is a system test", results[0].run_name)
        self.assertEqual('Error', results[0].status.value_verbose())
        self.assertIn('ValueError', results[0].message)
        self.assertIn('hello from the other side', results[0].message)

    def test_end_to_end_wish_vars_script_gets_new_variable(self):
        """Test running the same run twice, but the second time the reduce_vars has a new variable"""
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=REDUCE_SCRIPT, vars_script='')
        self.data_ready_message.data = file_location
        result_one = self.send_and_wait_for_result(self.data_ready_message)

        assert len(result_one) == 1
        run_without_vars = result_one[0]

        self.data_archive.add_reduce_vars_script(self.instrument, VARS_SCRIPT)
        result_two = self.send_and_wait_for_result(self.data_ready_message)

        assert len(result_two) == 2
        assert run_without_vars == result_two[
            0]  # check that the first run is queried again

        run_with_vars = result_two[1]
        assert run_without_vars.run_variables.count() == 0
        assert run_with_vars.run_variables.count(
        ) == 1  # the one standard variable in the VARS_SCRIPT
        var = run_with_vars.run_variables.first().variable
        assert var.name == "variable1"
        assert var.value == "value1"

    def test_end_to_end_wish_vars_script_loses_variable(self):
        """Test running the same run twice, but the second time the reduce_vars has one less variable"""
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=REDUCE_SCRIPT, vars_script=VARS_SCRIPT)
        self.data_ready_message.data = file_location
        result_one = self.send_and_wait_for_result(self.data_ready_message)

        assert len(result_one) == 1
        run_with_vars = result_one[0]
        assert run_with_vars.run_variables.count(
        ) == 1  # the one standard variable in the VARS_SCRIPT
        var = run_with_vars.run_variables.first().variable
        assert var.name == "variable1"
        assert var.value == "value1"

        self.data_archive.add_reduce_vars_script(self.instrument, "")
        result_two = self.send_and_wait_for_result(self.data_ready_message)

        assert len(result_two) == 2
        assert run_with_vars == result_two[0]
        run_without_vars = result_two[1]
        assert run_without_vars.run_variables.count() == 0

    def test_end_to_end_vars_script_has_variable_value_changed(self):
        """Test that reducing the same run after changing the reduce_vars updates the variable's value"""
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=REDUCE_SCRIPT, vars_script=VARS_SCRIPT)
        self.data_ready_message.data = file_location
        result_one = self.send_and_wait_for_result(self.data_ready_message)

        assert len(result_one) == 1
        run_with_initial_var = result_one[0]
        assert run_with_initial_var.run_variables.count(
        ) == 1  # the one standard variable in the VARS_SCRIPT
        var = run_with_initial_var.run_variables.first().variable
        assert var.name == "variable1"
        assert var.value == "value1"

        self.data_archive.add_reduce_vars_script(
            self.instrument, 'standard_vars={"variable1": 123}')
        result_two = self.send_and_wait_for_result(self.data_ready_message)

        assert len(result_two) == 2
        assert run_with_initial_var == result_two[0]

        run_with_changed_var = result_two[1]

        assert run_with_initial_var.run_variables.count() == 1
        assert run_with_changed_var.run_variables.count() == 1

        initial_var = run_with_initial_var.run_variables.first().variable
        changed_var = run_with_changed_var.run_variables.first().variable

        assert initial_var == changed_var

    def test_end_to_end_wish_vars_script_has_variable_reused_on_new_run_number(
            self):
        """Test that the variables are reused on new run numbers, IF their value has not changed"""
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=REDUCE_SCRIPT, vars_script=VARS_SCRIPT)
        self.data_ready_message.data = file_location
        result_one = self.send_and_wait_for_result(self.data_ready_message)

        run_with_initial_var = result_one[0]

        self.data_ready_message.run_number = 1234568
        result_two = self.send_and_wait_for_result(self.data_ready_message)
        run_with_different_run_number = result_two[0]

        assert run_with_initial_var.run_variables.count() == 1
        assert run_with_different_run_number.run_variables.count() == 1

        initial_var = run_with_initial_var.run_variables.first().variable
        new_var = run_with_different_run_number.run_variables.first().variable

        assert initial_var == new_var

    def test_end_to_end_wish_vars_script_has_variable_copied_on_new_run_number_when_value_changed(
            self):
        """Test that the variable is copied for a new run WHEN it's value has been changed"""
        # Create supporting data structures e.g. Data Archive, Reduce directory
        file_location = self._setup_data_structures(
            reduce_script=REDUCE_SCRIPT, vars_script=VARS_SCRIPT)

        self.run_number = 101
        self.data_ready_message.data = file_location
        result_one = self.send_and_wait_for_result(self.data_ready_message)

        assert len(result_one) == 1
        run_with_initial_var = result_one[0]
        assert run_with_initial_var.run_variables.count(
        ) == 1  # the one standard variable in the VARS_SCRIPT
        var = run_with_initial_var.run_variables.first().variable
        assert var.name == "variable1"
        assert var.value == "value1"

        # update the run number in the class because it's used to query for the correct run
        self.data_ready_message.run_number = self.run_number = 102
        self.data_archive.add_reduce_vars_script(
            self.instrument, 'standard_vars={"variable1": 123}')
        result_two = self.send_and_wait_for_result(self.data_ready_message)

        # making the run_number a list so that they can be deleted by the tearDown!
        self.run_number = [101, 102]

        assert len(result_two) == 1

        run_with_changed_var = result_two[0]

        assert run_with_initial_var.run_variables.count() == 1
        assert run_with_changed_var.run_variables.count() == 1

        initial_var = run_with_initial_var.run_variables.first().variable
        changed_var = run_with_changed_var.run_variables.first().variable

        assert initial_var != changed_var
        assert initial_var.name == changed_var.name
        assert initial_var.value != changed_var.value
        assert initial_var.type != changed_var.type
        assert initial_var.instrumentvariable.start_run < changed_var.instrumentvariable.start_run
class TestDataArchive(TestCase):
    def setUp(self) -> None:
        if Path(ARCHIVE_ROOT).exists():
            rmtree(ARCHIVE_ROOT)
        self.data_archive = DataArchive(["test"], 19, 20)
        self.expected_cycle_path = Path(get_project_root(), "data-archive", "NDXtest", "Instrument", "data")
        self.expected_script_path = Path(
            Path(get_project_root(), "data-archive", "NDXtest", "user", "scripts", "autoreduction"))

    def tearDown(self) -> None:
        if Path(ARCHIVE_ROOT).exists():
            rmtree(ARCHIVE_ROOT)

    def test_delete(self):
        """
        Tests the delete method removes the data-archive
        """
        test_archive_path = Path(CYCLE_DIRECTORY)
        test_archive_path.mkdir(parents=True)
        self.assertTrue(test_archive_path.exists())
        self.data_archive.delete()
        self.assertFalse(test_archive_path.exists())

    def test_delete_post_create(self):
        """
        Tests delete when archvie was created from create
        """
        self.data_archive.create()
        self.data_archive.delete()
        self.assertFalse(self.expected_cycle_path.exists())

    def test_create(self):
        """
        Tests the data-archive is created with the correct structure in the correct place
        """
        self.data_archive.create()
        self.assertTrue(self.expected_cycle_path.exists())
        self.assertTrue(self.expected_script_path.exists())

    def test_add_data_file(self):
        """
        Tests that a datafile can be added in the correct location with the correct name
        """
        expected_data_file = self.expected_cycle_path / "cycle_19_1" / "datafile.nxs"
        result = self.data_archive.add_data_file("test", "datafile.nxs", 19, 1)
        self.assertEqual(str(expected_data_file), result)
        self.assertTrue(expected_data_file.exists())

    def test_add_reduction_script(self):
        """
        Tests that a reduction script can be added with the correct text
        """
        expected_script_file = self.expected_script_path / "reduce.py"
        expected_script_text = "print('hello')\nprint('world')"
        self.data_archive.create()
        self.data_archive.add_reduction_script("test", expected_script_text)
        self.assertTrue(expected_script_file.exists())
        with open(expected_script_file) as fle:
            actual_text = fle.read()
        self.assertEqual(expected_script_text, actual_text)

    def test_add_reduce_vars_script(self):
        """
        Tests that a reduce vars script can be added with the correct text
        """
        expected_var_file = self.expected_script_path / "reduce_vars.py"
        expected_var_text = "vars = {}"
        self.data_archive.create()
        self.data_archive.add_reduce_vars_script("test", expected_var_text)
        self.assertTrue(expected_var_file.exists())
        with open(expected_var_file) as fle:
            actual_text = fle.read()
        self.assertEqual(expected_var_text, actual_text)