def test_insufficient_operands(self): for instruction in [ 'Jn', 'St', 'Cp', 'Pp', 'Nt', 'Ng', 'Ab', 'Cl', 'Fl', 'Rd' ]: with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program('a;;' + instruction).machine().run() self.assertEqual( str(raised.exception), 'instruction "{0}" requires 1 operand(s), but the stack only has 0' .format(instruction)) for instruction in [ 'Eq', 'Gt', 'Ge', 'Ad', 'Sb', 'Ml', 'Dv', 'Pw', 'Mn', 'Mx' ]: with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program('a;;' + instruction).machine().run() self.assertEqual( str(raised.exception), 'instruction "{0}" requires 2 operand(s), but the stack only has 0' .format(instruction)) with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';123;Lc0' + instruction).machine().run() self.assertEqual( str(raised.exception), 'instruction "{0}" requires 2 operand(s), but the stack only has 1' .format(instruction))
def test_execution_out_of_bounds(self): with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';123;Lc0').machine().run() self.assertEqual(str(raised.exception), 'current execution location 1 is out-of-bounds') with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';;Ju2Xx').machine().run() self.assertEqual(str(raised.exception), 'current execution location 2 is out-of-bounds')
def test_variables_positional_parameter(self): with self.assertRaises(TypeError) as raised: dsm.Program('foo|bar;;Xx').machine(32) self.assertEqual(str(raised.exception), 'machine() does not accept positional parameters') machine = dsm.Program('foo|bar;;Xx').machine() with self.assertRaises(TypeError) as raised: machine.reset(32) self.assertEqual(str(raised.exception), 'reset() does not accept positional parameters')
def test_invalid_instruction(self): with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;?') self.assertEqual(str(raised.exception), 'invalid instruction "?"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;XX') self.assertEqual(str(raised.exception), 'invalid instruction "X"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;X0') self.assertEqual(str(raised.exception), 'invalid instruction "X"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;Xy') self.assertEqual(str(raised.exception), 'invalid instruction "Xy"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;0') self.assertEqual(str(raised.exception), 'invalid instruction "0"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;01') self.assertEqual(str(raised.exception), 'invalid instruction "0"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;0X') self.assertEqual(str(raised.exception), 'invalid instruction "0"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;Ju1Lx') self.assertEqual(str(raised.exception), 'invalid instruction "Lx"')
def test_invalid_constant_slot(self): with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;Lc0Xx') self.assertEqual(str(raised.exception), 'reference to nonexistent constant slot 0') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';100|200|300;Lc3Xx') self.assertEqual(str(raised.exception), 'reference to nonexistent constant slot 3') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';100|200|300;Lc123Xx') self.assertEqual(str(raised.exception), 'reference to nonexistent constant slot 123')
def run_binop_instruction(self, instruction, cases): machine = dsm.Program('a|b|result;;Lv0Lv1' + instruction + 'St2Xx').machine() for machine['a'], machine['b'], expected_result in cases: self.assertEqual(machine.run(), 5) self.assertEqual(Decimal(machine['result']), Decimal(expected_result))
def test_Sb(self): self.run_binop_instruction('Sb', [ ('0', '0', '0'), ('0.000000', '0', '0'), ('-0.0', '0', '0'), ('1', '1', '0'), ('42', '9', '33'), ('42', '0', '42'), ('1.000000', '1', '0'), ('42.001', '42', '0.001'), ('-42.001', '-42', '-0.001'), ('3.14', '0.0', '3.14'), ('42', '42', '0'), ('0', '42', '-42'), ('0', '-3.14', '3.14'), ]) with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program( ';9999999999999999999999999999999999e+6111|-1e+6111;Lc0Lc1SbXx' ).machine().run() self.assertEqual(str(raised.exception), 'result of Sb at instruction 2 was too large') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Sb')
def test_invalid_variable_value(self): for v in ['NaN', 'Inf', 'Infinity', '-Inf', '-Infinity']: with self.assertRaises(ValueError) as raised: dsm.Program('foo;;Xx').machine(foo=v) self.assertEqual(str(raised.exception), 'invalid variable value "{0}"'.format(v)) with self.assertRaises(ValueError) as raised: dsm.Program('foo;;Xx').machine(foo='bogus') self.assertEqual(str(raised.exception), 'invalid variable value "bogus"') with self.assertRaises(ValueError) as raised: dsm.Program('foo;;Xx').machine()['foo'] = 'bogus' self.assertEqual(str(raised.exception), 'invalid variable value "bogus"')
def test_run(self): machine = dsm.Program( 'radius|area|diameter;3.14|2;Lv0Lv0MlLc0MlSt1Lv0Lc1MlLc0MlSt2Xx' ).machine(radius=3) self.assertEqual(machine.run(), 13) self.assertEqual(machine['area'], '28.26') self.assertEqual(machine['diameter'], '18.84')
def coverage_check(): machine = dsm.Program(dsmal).machine() self.assertNotIn(False, [ any(v) for v in zip(*[ machine.reset(**case).run_with_coverage() for case in cases ]) ])
def test_missing_program_section(self): for program in ['', 'foo', ';123']: with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(program) self.assertEqual( str(raised.exception), 'program must have variables, constants, and instructions sections' )
def test_infinite_loop(self): with self.assertRaises(dsm.InstructionLimitExceededError) as raised: machine = dsm.Program(';;Ju0').machine() machine.instruction_limit = 100 machine.run() self.assertEqual( str(raised.exception), 'execution forcibly terminated after 100 instructions')
def test_pickle(self): program1 = dsm.Program( 'radius|area|diameter;3.14|2;Lv0Lv0MlLc0MlSt1Lv0Lc1MlLc0MlSt2Xx') for protocol in (2, 3, 4): if protocol <= pickle.HIGHEST_PROTOCOL: p = pickle.dumps(program1, protocol=protocol) program2 = pickle.loads(p) self.assertEqual(program1.dsmal, program2.dsmal)
def test_Dv(self): with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';0;Lc0CpDvXx').machine().run() self.assertEqual(str(raised.exception), 'illegal Dv at instruction 2') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Dv') with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';5|0;Lc0Lc1DvXx').machine().run() self.assertEqual(str(raised.exception), 'illegal Dv at instruction 2') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Dv') with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';5.00000|0.000000;Lc0Lc1DvXx').machine().run() self.assertEqual(str(raised.exception), 'illegal Dv at instruction 2') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Dv') with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';5|-0;Lc0Lc1DvXx').machine().run() self.assertEqual(str(raised.exception), 'illegal Dv at instruction 2') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Dv') self.run_binop_instruction('Dv', [ ('0', '1', '0'), ('0', '0.3', '0'), ('0.00', '100', '0'), ('0.9', '0.1', '9'), ('2', '1', '2'), ('-3', '1', '-3'), ('5', '5.0', '1'), ('20', '4', '5'), ('1.000000', '1', '1'), ('42.001', '42', '1.00002380952380952380952380952381'), ('-42.001', '-42', '1.00002380952380952380952380952381'), ]) with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';1e+6144|0.1;Lc0Lc1DvXx').machine().run() self.assertEqual(str(raised.exception), 'result of Dv at instruction 2 was too large') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Dv')
def test_heap_overflow(self): with self.assertRaises(dsm.ExecutionError) as raised: variable_names = '|'.join(('v' + str(i)) for i in range(1000)) instructions = ''.join( 'Lv{0}Lv{1}AdSt{2}'.format(i, i + 1, i + 2) # look, Fibonacci! for i in range(998)) dsm.Program(variable_names + ';;LoSt0LoSt1' + instructions + 'Xx').machine().run() self.assertEqual(str(raised.exception), 'ran out of space')
def test_Lr(self): machine = dsm.Program('a|b|c|d;;LrSt0LrSt1LrSt2LrSt3Xx').machine() default_random_number_iterator = dsm.random_number_iterator try: # No PRNG at all del dsm.random_number_iterator self.assertEqual(machine.run(), 9) self.assertEqual(machine['a'], '0') self.assertEqual(machine['b'], '0') self.assertEqual(machine['c'], '0') self.assertEqual(machine['d'], '0') # Default PRNG dsm.random_number_iterator = itertools.cycle(['0', '1', '2']) self.assertEqual(machine.run(), 9) self.assertEqual(machine['a'], '0') self.assertEqual(machine['b'], '1') self.assertEqual(machine['c'], '2') self.assertEqual(machine['d'], '0') finally: dsm.random_number_iterator = default_random_number_iterator # Machine-specific PRNG machine.random_number_iterator = itertools.cycle(['0.5', '3.14']) self.assertEqual(machine.run(), 9) self.assertEqual(machine['a'], '0.5') self.assertEqual(machine['b'], '3.14') self.assertEqual(machine['c'], '0.5') self.assertEqual(machine['d'], '3.14') with self.assertRaises(dsm.ExecutionError) as raised: machine.random_number_iterator = 42 machine.run() self.assertEqual(str(raised.exception), 'random_number_iterator is not an iterator') with self.assertRaises(dsm.ExecutionError) as raised: machine.random_number_iterator = iter(['bogus']) machine.run() self.assertEqual(str(raised.exception), 'invalid random number value "bogus"') with self.assertRaises(dsm.ExecutionError) as raised: machine.random_number_iterator = iter(['1']) machine.run() self.assertEqual(str(raised.exception), 'random_number_iterator ran out of values') def bad_iterator(): yield 1 raise Exception('boom!') with self.assertRaises(Exception) as raised: machine.random_number_iterator = bad_iterator() machine.run() self.assertEqual(str(raised.exception), 'boom!')
def test_Jz(self): machine = dsm.Program('a|b;42;Lv0Jz4Lc0St1Xx').machine(a=1) self.assertEqual(machine.run(), 5) self.assertEqual(machine['a'], '1') self.assertEqual(machine['b'], '42') machine.reset(a=0) self.assertEqual(machine.run(), 3) self.assertEqual(machine['a'], '0') self.assertEqual(machine['b'], '0')
def test_invalid_variable_slot(self): with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;Lv0Xx') self.assertEqual(str(raised.exception), 'reference to nonexistent variable slot 0') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program('a|b|c;;Lv3Xx') self.assertEqual(str(raised.exception), 'reference to nonexistent variable slot 3') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program('a|b|c;;Lv123Xx') self.assertEqual(str(raised.exception), 'reference to nonexistent variable slot 123') with self.assertRaises(KeyError) as raised: dsm.Program('a|b|c;;Xx').machine()['d'] = 42 self.assertEqual(str(raised.exception), "'d'")
def test_Pw(self): for a, b in [ ('-1', '-0.5'), ('0', '-1'), ('0', '-2'), ]: with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';{0}|{1};Lc0Lc1PwXx'.format(a, b)).machine().run() self.assertEqual(str(raised.exception), 'illegal Pw at instruction 2') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Pw') with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';2e+256;Lc0CpPwXx').machine().run() self.assertEqual(str(raised.exception), 'result of Pw at instruction 2 was too large') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Pw') with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';2e-256|2e+256;Lc0Lc1PwXx').machine().run() self.assertEqual(str(raised.exception), 'result of Pw at instruction 2 was too small') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Pw') self.run_binop_instruction('Pw', [ ('0', '0', '0'), ('-0.0', '-0.00', '0'), ('0', '1', '0'), ('3.14', '1', '3.14'), ('-3.14', '1', '-3.14'), ('1', '1', '1'), ('1', '2', '1'), ('1', '3.14', '1'), ('3', '2', '9'), ('2.5', '2', '6.25'), ('1.000000', '1', '1'), ('2', '3', '8'), ('9', '0.5', '3'), ])
def test_variables(self): machine = dsm.Program(';;Xx').machine() self.assertEqual(len(machine), 0) machine = dsm.Program('foo|bar;;Xx').machine() self.assertEqual(machine['foo'], '0') self.assertEqual(machine['bar'], '0') self.assertEqual(len(machine), 2) machine['foo'] = False machine['bar'] = True self.assertEqual(machine['foo'], '0') self.assertEqual(machine['bar'], '1') for value in range(-100, 100): machine['foo'] = value self.assertEqual(machine['foo'], str(value)) for value in [0, '.0', '-0.0', '0.000']: machine['foo'] = value self.assertEqual(machine['foo'], '0') for value in [-42, -42.0, '-42.000', '-4.20e+1', '-.4200e+2']: machine['foo'] = value self.assertEqual(machine['foo'], '-42') for value in [ '3.14159', '3.141590', '3.1415900', '.314159e+1', '.0314159e+2' ]: machine['foo'] = value self.assertEqual(machine['foo'], '3.14159') machine['foo'] = '123e+13' self.assertEqual(machine['foo'], '1.23e+15') machine['foo'] = Decimal('-10000000.00000001') self.assertEqual(machine['foo'], '-10000000.00000001') machine['foo'] = 2 << 65 # overflows an int64_t self.assertIn(machine['foo'], ['73786976294838206464'])
def test_invalid_constant_value(self): for v in ['NaN', 'Inf', 'Infinity', '-Inf', '-Infinity']: with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';{0};Xx'.format(v)) self.assertEqual(str(raised.exception), 'invalid constant value "{0}"'.format(v)) for program in [';|123;Xx', ';123|;Xx']: with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(program) self.assertEqual(str(raised.exception), 'invalid constant value ""') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';bogus;Xx') self.assertEqual(str(raised.exception), 'invalid constant value "bogus"') with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';123|3.14159|bogus;Xx') self.assertEqual(str(raised.exception), 'invalid constant value "bogus"')
def test_coverage(self): machine = dsm.Program( 'a|b|multiply|c;;Lv0Lv1Lv2Jn6AdJu7MlSt3Xx').machine(a=3, b=5, multiply=0) self.assertEqual( machine.run_with_coverage(), (True, True, True, True, True, True, False, True, True)) self.assertEqual(machine['c'], '8') machine.reset(multiply=1) self.assertEqual( machine.run_with_coverage(), (True, True, True, True, False, False, True, True, True)) self.assertEqual(machine['c'], '15')
def test_nonexistent_variable_key(self): program = dsm.Program('foo;;Xx') with self.assertRaises(KeyError) as raised: program.machine(bogus=42) self.assertEqual(str(raised.exception), repr('bogus')) with self.assertRaises(KeyError) as raised: program.machine().reset(bogus=42) self.assertEqual(str(raised.exception), repr('bogus')) for key in ['bogus', 0, object()]: with self.assertRaises(KeyError) as raised: _ = program.machine()[key] self.assertEqual(str(raised.exception), repr(key)) with self.assertRaises(KeyError) as raised: program.machine()[key] = 42 self.assertEqual(str(raised.exception), repr(key))
def test_Ml(self): self.run_binop_instruction('Ml', [ ('0', '0', '0'), ('1.5', '0', '0'), ('0', '1.5', '0'), ('1.5', '1', '1.5'), ('1', '2.5', '2.5'), ('0.000000', '0', '0'), ('-0.0', '0', '0'), ('1', '1', '1'), ('1.000000', '1', '1'), ('42.001', '42', '1764.042'), ('-42.001', '-42', '1764.042'), ]) with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program(';1e+6144|10;Lc0Lc1MlXx').machine().run() self.assertEqual(str(raised.exception), 'result of Ml at instruction 2 was too large') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Ml')
def test_Ad(self): self.run_binop_instruction('Ad', [ ('0', '0', '0'), ('42', '0', '42'), ('0', '42', '42'), ('0.000000', '0', '0'), ('-0.0', '0', '0'), ('1', '1', '2'), ('1.000000', '1', '2'), ('42.001', '42', '84.001'), ('-42.001', '-42', '-84.001'), ]) with self.assertRaises(dsm.ExecutionError) as raised: dsm.Program( ';9999999999999999999999999999999999e+6111|1e+6111;Lc0Lc1AdXx' ).machine().run() self.assertEqual(str(raised.exception), 'result of Ad at instruction 2 was too large') self.assertEqual(raised.exception.instruction, 2) self.assertEqual(raised.exception.opcode, 'Ad')
def test_too_many_instructions(self): with self.assertRaises(dsm.InvalidProgramError) as raised: dsm.Program(';;' + 'Xx' * (2**16)) self.assertEqual(str(raised.exception), 'too many instructions')
def test_constants(self): self.assertEqual( dsm.Program(';0|-0|1|-1|0.3|3.13159|-100000000.0000000001;Xx'). machine().run(), 1)
def test_invalid_program_type(self): with self.assertRaises(TypeError): dsm.Program(None)
def test_Pp(self): machine = dsm.Program('a;;LoLzPpSt0Xx').machine() self.assertEqual(machine.run(), 5) self.assertEqual(machine['a'], '1')
def test_Cp(self): machine = dsm.Program('a;;Lv0CpAdSt0Xx').machine(a=3) self.assertEqual(machine.run(), 5) self.assertEqual(machine['a'], '6')