Example #1
0
class TestRunCycles(unittest.TestCase):
    def setUp(self):
        self.lmc = LMC([55], [901, 902, 105, 902, 000, 100])

    def TestExampleProgram(self):
        inputs, outputs, num_cycles = self.lmc.run_cycles()
        self.assertEqual(inputs, [55])
        self.assertEqual(outputs, [55, 155])
        self.assertEqual(num_cycles, 5)
        self.assertEqual(self.lmc.address_reg, 5)
        self.assertEqual(self.lmc.counter, 6)
        self.assertEqual(self.lmc.accumulator, 5)
        self.assertTrue(self.lmc.halted)
Example #2
0
class LMCWrapper:
    def __init__(self,
                 _filepath,
                 _potential_values,
                 max_cycles=50000,
                 **kwargs):
        self.is_quiet = kwargs.get('quiet', None)
        self.is_verbose = kwargs.get('verbose', None)
        self.batch_fp = kwargs.get('batch_fp', None)
        self.checker_fp = kwargs.get('checker_fp', None)
        self.has_graph = kwargs.get('graph', None)
        self.feedback_fp = kwargs.get('feedback', None)

        self.lmc = None
        self.checker = None
        self.batch_tests = None
        self.filename = ntpath.basename(_filepath)
        self.inputs_and_cycles = {}
        self.feedback = ""
        self.mailboxes = []
        self.potential_values = _potential_values
        self.max_cycles = max_cycles
        self.total_cycles = 0
        self.unexpected_outputs = []

        self.setup(_filepath)

    def setup(self, filepath):
        """Checks the extension and converts the file into mailbox machine code."""

        # Adds checker function from file or from batch
        if self.batch_fp:
            self.batch_tests, self.potential_values = file_parser.parse_batch_test_file(
                self.batch_fp)
            self.checker = self.get_batch_outputs
        elif self.checker_fp:
            self.checker = self.get_checker(self.checker_fp)

        # compiles mailboxes from file
        self.mailboxes, num_mailboxes = file_parser.get_mailboxes_from_file(
            filepath)
        print("Compiled program uses %d/100 mailboxes." % num_mailboxes)
        self.lmc = LMC(self.potential_values, self.mailboxes, self.max_cycles)

        # change working directory to filepath so that feedback outputs in right location.
        os.chdir(ntpath.dirname(filepath) or '.')

    @staticmethod
    def get_checker(checker_filepath):
        """Gets the checker function at the given file_path."""
        os.chdir(os.path.dirname(__file__))
        spec = importlib.util.spec_from_file_location("divisors.py",
                                                      checker_filepath)
        foo = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(foo)
        return foo.checker

    def get_batch_outputs(self, _inputs):
        return self.batch_tests.pop(0)[2]

    def print_mailboxes(self):
        print(self.mailboxes)

    def run_batch(self):
        """
        Runs lmc program with given inputs (if any), until all inputs have been used.
        Checks the outputs to ensure they match those expected (--checker used).
        Plots a graph of input against cycles taken (--graph used).
        """
        if len(self.potential_values) == 0:
            self.run_once()

        # run the program repeatedly for multiple input values
        while len(self.potential_values) > 0:
            self.run_program()

        if self.checker:
            if len(self.unexpected_outputs) == 0:
                print("The program passed all tests.")
            else:
                print("The program failed %d tests." %
                      len(self.unexpected_outputs))
                for inputs, expected_outputs, outputs in self.unexpected_outputs:
                    inputs = ', '.join(str(val) for val in inputs)
                    expected_outputs = ', '.join(
                        str(val) for val in expected_outputs)
                    outputs = ', '.join(str(val) for val in outputs)
                    print("inputs: %s, expected_output: %s, output: %s" %
                          (inputs, expected_outputs, outputs))
        self.write_feedback()

        # print average and worst case number of cycles
        average_cycles = sum(self.inputs_and_cycles.values()) // len(
            self.inputs_and_cycles.keys())
        worst_cycles = max(self.inputs_and_cycles.values())
        print("Average number of cycles: %d" % average_cycles)
        print("Worst case number of cycles: %d" % worst_cycles)

        # plot graph
        if self.has_graph:
            plot.plot_graph(self.inputs_and_cycles)

    def run_once(self):
        """Runs the program once, without resetting."""
        self.run_program()
        self.write_feedback()

    def run_program(self):
        """Runs the lmc program."""
        if self.batch_tests:
            self.feedback += f"Attempting to run test {self.batch_tests[0][0]}:\n"
            self.lmc.max_cycles = self.batch_tests[0][3]

        inputs, outputs, num_cycles = self.lmc.run_cycles()
        self.lmc.reset()
        self.total_cycles += num_cycles
        self.store_feedback_msg(inputs, outputs, num_cycles)

        # matches inputs with number of cycles taken for that input
        if len(inputs) == 1:
            self.inputs_and_cycles[inputs[0]] = num_cycles
        elif len(inputs) > 1:
            self.inputs_and_cycles[tuple(inputs)] = num_cycles

    def store_feedback_msg(self, inputs, outputs, num_cycles):
        """Generates a report mentioning inputs, expected outputs (with checker) and actual outputs."""
        expect_output_msg = ''
        if self.checker:
            expected_outputs = self.checker(inputs)
            expect_output_msg = f"Expected Outputs(s):\t{', '.join(str(val) for val in expected_outputs)}"
            if outputs != expected_outputs:
                self.unexpected_outputs.append(
                    (inputs, expected_outputs, outputs))

        msg = (
            f"Input(s):\t\t{', '.join(str(val) for val in inputs)}\n"
            f"{expect_output_msg}\n"
            f"Actual Output(s):\t{', '.join(str(val) for val in outputs)}\n"
            f"Program executed in {num_cycles} cycles, cumulative {self.total_cycles}.\n\n"
        )
        self.feedback += msg

        if not self.is_quiet:
            print(msg)

    def write_feedback(self):
        """Write feedback to a txt file."""
        if self.feedback_fp is not None:
            with open(self.feedback_fp or f"feedback_{self.filename}",
                      'w') as f:
                f.write(self.feedback)