def test_unprotect(self): """Save in protected format to a file, load in plaintext.""" plaintext = b'60 SAVE "test.bin"\r\n70 SAVE "test.asc",A\r\n80 LIST,"test.lst"\r\n' tokenised = ( b'\xff\x7f\x12<\x00\xbe "test.bin"\x00\x92\x12F\x00\xbe ' b'"test.asc",A\x00\xa3\x12P\x00\x93,"test.lst"\x00\x00\x00\x1a') protected = ( b'\xfe\xd0\xa9\x81T\xed\x12\xbd} f\x15\xd0\xf0:\x99\xc3\xb2!\x01(\x13\xe2\x8c%J\x91' b'\xf0\x81S\xf2IR%f\x0f\xc4\xd6\xc8H\xbf{\xf8_c\xcb<\xd2\x82\xd4\x04j\xd3\x06\xfa\x05' b'\x1a') with Session(devices={b'A': self._test_dir}, current_device='A:') as s: s.execute(plaintext) s.execute('save "prog",P') with Session(devices={b'A': self._test_dir}, current_device='A:') as s: # the program saves itself as plaintext and tokenised # in gw-basic, illegal funcion call. s.execute('run "prog"') with open(self._output_path('PROG.BAS'), 'rb') as f: assert f.read() == protected with open(self._output_path('TEST.BIN'), 'rb') as f: assert f.read() == tokenised with open(self._output_path('TEST.ASC'), 'rb') as f: assert f.read() == plaintext + b'\x1a' # execution stops after save,a ! assert not os.path.isfile(self._output_path('TEST.LST'))
def test_text_letter(self): """Save and load in plaintext to a file, explicit drive letter.""" with Session(devices={b'A': self.output_path()}) as s: s.execute('10 A%=1234') s.execute('save "A:prog",A') with Session(devices={b'A': self.output_path()}) as s: s.execute('run "A:prog"') assert s.get_variable('A%') == 1234
def test_bad_current(self): """Test bad current device.""" with Session(devices={'A': self.output_path(), 'Z': None}, current_device='B') as s: s.execute('open "test" for output as 1: print#1, 42: close 1') assert os.path.isfile(self.output_path('TEST')) with Session(devices={'A': self.output_path(), 'Z': None}, current_device='#') as s: s.execute('open "test2" for output as 1: print#1, 42: close 1') assert os.path.isfile(self.output_path('TEST2'))
def test_protected(self): """Save and load in protected format to a file.""" with Session(devices={b'A': self.output_path()}, current_device='A:') as s: s.execute('10 A%=1234') s.execute('save "prog", P') with Session(devices={b'A': self.output_path()}, current_device='A:') as s: s.execute('run "prog"') assert s.get_variable('A%') == 1234
def test_disk_data(self): """Write and read data to a text file.""" with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:data" for output as 1') s.execute('print#1, 1234') with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:data" for input as 1') s.execute('input#1, A%') assert s.get_variable('A%') == 1234
def test_bytes_mount(self): """Test specifying mount dir as bytes.""" with Session(devices={'A': self.output_path().encode('ascii'), 'Z': None}) as s: s.execute('open "test" for output as 1: print#1, 42: close 1') assert os.path.isfile(self.output_path('TEST')) # must be ascii with Session(devices={'A': b'ab\xc2', 'Z': None}) as s: s.execute('files') output = [_row.strip() for _row in self.get_text(s)] assert output[0] == b'@:\\'
def test_pickle_session_open_file(self): """Pickle Session object with open file.""" s = Session(devices={'a': self.output_path()}) s.execute('open "A:TEST" for output as 1') ps = pickle.dumps(s) s2 = pickle.loads(ps) s2.execute('print#1, "test"') s2.close() with open(self.output_path('TEST')) as f: assert f.read() == u'test\n\x1a'
def test_wav_text(self): """Save and load in plaintext to a WAV file.""" try: os.remove(_output_file('test_prog.wav')) except EnvironmentError: pass with Session(devices={b'CAS1:': _output_file('test_prog.wav')}) as s: s.execute('10 A%=1234') s.execute('save "cas1:prog",A') with Session(devices={b'CAS1:': _output_file('test_prog.wav')}) as s: s.execute('run "cas1:prog"') assert s.get_variable('A%') == 1234
def test_wav_data(self): """Write and read data to a WAV file.""" try: os.remove(_output_file('test_data.wav')) except EnvironmentError: pass with Session(devices={b'CAS1:': _output_file('test_data.wav')}) as s: s.execute('open "cas1:data" for output as 1') s.execute('print#1, 1234') with Session(devices={b'CAS1:': _output_file('test_data.wav')}) as s: s.execute('open "cas1:data" for input as 1') s.execute('input#1, A%') assert s.get_variable('A%') == 1234
def test_bad_mount(self): """Test bad mount dict specification.""" with Session(devices={b'#': self.output_path()}) as s: s.execute('open "A:test" for output as 1: print#1, 42: close 1') output = [_row.strip() for _row in self.get_text(s)] assert output[0] == b'Path not found\xff' with Session(devices={b'\0': self.output_path()}) as s: s.execute('open "A:test" for output as 1: print#1, 42: close 1') output = [_row.strip() for _row in self.get_text(s)] assert output[0] == b'Path not found\xff' with Session(devices={u'\xc4': self.output_path()}) as s: s.execute('open "A:test" for output as 1: print#1, 42: close 1') output = [_row.strip() for _row in self.get_text(s)] assert output[0] == b'Path not found\xff'
def test_match_name(self): """Test case-insensitive matching of native file name.""" # this will be case sensitive on some platforms but should be picked up correctly anyway open(self.output_path('MixCase.txt'), 'w').close() with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:mixcase.txt" for output as 1') s.execute('print#1, 1234') with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:MIXCASE.TXT" for input as 1') s.execute('input#1, A%') assert s.get_variable('A%') == 1234 # check we've used the pre-existing file with open(self.output_path('MixCase.txt'), 'rb') as f: assert f.read() == b' 1234 \r\n\x1a'
def test_disk_random(self): """Write and read data to a random access file.""" with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:data" for random as 1') s.execute('field#1, 20 as a$, 20 as b$') s.execute('lset b$="abcde"') s.execute('print#1, 1234') s.execute('put#1, 1') with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:data" for random as 1') s.execute('field#1, 20 as a$, 20 as b$') s.execute('get#1, 1') assert s.get_variable('A$') == b' 1234 \r\n'.ljust(20, b'\0') assert s.get_variable('B$') == b'abcde'.ljust(20, b' ')
def test_disk_data_append(self): """Append data to a text file.""" with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:data" for output as 1') s.execute('print#1, 1234') with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:data" for append as 1') s.execute('print#1, "abcde"') with Session(devices={b'A': self.output_path()}) as s: s.execute('open "a:data" for input as 1') s.execute('line input#1, a$') s.execute('line input#1, b$') assert s.get_variable('A$') == b' 1234 ' assert s.get_variable('B$') == b'abcde'
def test_cas_text(self): """Save and load in plaintext to a CAS file.""" try: os.remove(_output_file('test_prog.cas')) except EnvironmentError: pass with Session(devices={b'CAS1:': _output_file('test_prog.cas')}) as s: s.execute('10 A%=1234') s.execute('save "cas1:prog",A') with Session(devices={b'CAS1:': _output_file('test_prog.cas')}) as s: s.execute('run "cas1:prog"') output = [_row.strip() for _row in self.get_text(s)] assert s.get_variable('A%') == 1234 assert output[0] == b'prog .A Found.'
def test_disk_data_utf8(self): """Write and read data to a text file, utf-8 encoding.""" with Session(devices={b'A': self.output_path()}, textfile_encoding='utf-8') as s: s.execute('open "a:data" for output as 1') # we're embedding codepage in this string, so should be bytes s.execute(b'print#1, "\x9C"') # utf8-sig, followed by pound sign with open(self.output_path('DATA'), 'rb') as f: assert f.read() == b'\xef\xbb\xbf\xc2\xa3\r\n\x1a' with Session(devices={b'A': self.output_path()}, textfile_encoding='utf-8') as s: s.execute('open "a:data" for append as 1') s.execute(b'print#1, "\x9C"') with open(self.output_path('DATA'), 'rb') as f: assert f.read() == b'\xef\xbb\xbf\xc2\xa3\r\n\xc2\xa3\r\n\x1a'
def test_cas_data(self): """Write and read data to a CAS file.""" try: os.remove(_output_file('test_data.cas')) except EnvironmentError: pass with Session(devices={b'CAS1:': _output_file('test_data.cas')}) as s: s.execute('open "cas1:data" for output as 1') s.execute('print#1, 1234') with Session(devices={b'CAS1:': _output_file('test_data.cas')}) as s: s.execute('open "cas1:data" for input as 1') s.execute('input#1, A%') output = [_row.strip() for _row in self.get_text(s)] assert s.get_variable('A%') == 1234 assert output[0] == b'data .D Found.'
def test_cas_current_device(self): """Save and load to cassette as current device.""" with Session(devices={b'CAS1:': _output_file('test_current.cas')}, current_device=b'CAS1:') as s: s.execute('10 ?') s.execute('save "Test"') with Session(devices={b'CAS1:': _output_file('test_current.cas')}, current_device=b'CAS1:') as s: s.execute('load "Test"') s.execute('list') output = [_row.strip() for _row in self.get_text(s)] assert output[:2] == [ b'Test .B Found.', b'10 PRINT', ]
def test_cas_no_name(self): """Save and load to cassette without a filename.""" with Session( devices={b'CAS1:': _output_file('test_current.cas')}) as s: s.execute('10 ?') s.execute('save "cas1:"') with Session( devices={b'CAS1:': _output_file('test_current.cas')}) as s: s.execute('load "cas1:"') s.execute('list') output = [_row.rstrip() for _row in self.get_text(s)] assert output[:2] == [ b' .B Found.', b'10 PRINT', ]
def test_box(self): """Test box protection.""" cp_936 = read_codepage('936') with Session( codepage=cp_936, box_protect=True, textfile_encoding='utf-8', devices={'c': self.output_path()}, ) as s: # to file s.execute('open "c:boxtest.txt" for output as 1') s.execute('PRINT#1, CHR$(218);STRING$(10,CHR$(196));CHR$(191)') # to screen s.execute('PRINT CHR$(218);STRING$(10,CHR$(196));CHR$(191)') # bytes text output_bytes = [_row.strip() for _row in self.get_text(s)] # unicode text output_unicode = [ _row.strip() for _row in self.get_text(s, as_type=type(u'')) ] with open(self.output_path('BOXTEST.TXT'), 'r', encoding='utf-8') as f: assert f.read() == u'\ufeff┌──────────┐\n\x1a' assert output_bytes[ 0] == b'\xda\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xbf' assert output_unicode[0] == u'┌──────────┐'
def test_pickle_session(self): """Pickle Session object.""" with Session() as s: s.execute('a=1') ps = pickle.dumps(s) s2 = pickle.loads(ps) assert s2.get_variable('a!') == 1
def test_cursor_overflow_cr(self): """Test cursor movement after print char and cr on last column.""" with Session() as s: s.execute(b'locate 1,80: print"x";chr$(13);') # cursor has moved after CR assert s._impl.text_screen.current_row == 2, s._impl.text_screen.current_row assert s._impl.text_screen.current_col == 1, s._impl.text_screen.current_col
def test_cursor_overflow(self): """Test cursor movement after print on last column.""" with Session() as s: s.execute(b'locate 1,80: print"x";') # cursor has not moved to next row assert s._impl.text_screen.current_row == 1, s._impl.text_screen.current_row assert s._impl.text_screen.current_col == 80, s._impl.text_screen.current_col
def test_session_iostreams(self): """Test Session with copy to BytesIO.""" bi = io.BytesIO() with Session(input_streams=None, output_streams=bi) as s: s.execute(b'a=1') s.execute(b'print a') assert bi.getvalue() == b' 1 \r\n'
def test_session_no_streams(self): """Test Session without stream copy.""" with Session(input_streams=None, output_streams=None) as s: s.execute(b'a=1') s.execute(b'print a') output = self.get_text_stripped(s) assert output[:1] == [b' 1']
def test_session(self): """Test basic Session API.""" with Session() as s: s.execute('a=1') assert s.evaluate('a+2') == 3. assert s.evaluate('"abc"+"d"') == b'abcd' assert s.evaluate('string$(a+2, "@")') == b'@@@' # string variable s.set_variable('B$', 'abcd') assert s.get_variable('B$') == b'abcd' assert istypeval(s.evaluate('LEN(B$)'), 4) # unset variable assert s.evaluate('C!') == 0. assert istypeval(s.get_variable('D%'), 0) # unset array s.set_variable('A%()', [[0, 0, 5], [0, 0, 6]]) assert s.get_variable('A%()') == [[0, 0, 5, 0, 0, 0, 0, 0, 0, 0], [0, 0, 6, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] assert s.evaluate('A%(0,2)') == 5 assert s.evaluate('A%(1,2)') == 6 assert s.evaluate('A%(1,7)') == 0 assert s.evaluate('FRE(0)') == 60020. assert s.evaluate('CSRLIN') == 1 s.execute('print b$') assert s.evaluate('CSRLIN') == 2
def test_non_nfc(self): """Test conversion of non-NFC sequences.""" with Session() as s: # a-acute in NFD s.execute(u'a$ = "a\u0301"') # codepage 437 for a-acute assert s.get_variable('a$') == b'\xa0'
def test_missing(self): """Test codepage with missing codepoints.""" cp = {b'\xff': u'B'} with Session(codepage=cp) as s: s.execute('a$ = "abcde" + chr$(255)') assert s.get_variable('a$') == b'abcde\xff' assert s.get_variable('a$', as_type=type(u'')) == u'\0\0\0\0\0B'
def test_extension_function(self): """Test extension functions.""" class Extension(object): @staticmethod def boolfunc(): return True @staticmethod def unicodefunc(): return u'test' @staticmethod def bytesfunc(): return b'test' @staticmethod def intfunc(): return 1 @staticmethod def floatfunc(): return 1 with Session(extension=Extension) as s: assert s.evaluate('_BOOLFUNC') == -1 assert s.evaluate('_INTFUNC') == 1.0 assert s.evaluate('_FLOATFUNC') == 1.0 assert s.evaluate('_UNICODEFUNC') == b'test' assert s.evaluate('_BYTESFUNC') == b'test'
def test_no_extension(self): """Test attempting to access extensions that aren't there.""" with Session() as s: s.execute(b''' _NOPE "one", 2, 3!, 4# ''') assert self.get_text_stripped(s)[0] == b'Syntax error\xff'
def test_open_bad_number(self): """Test opening to a bad file number.""" with Session(devices={b'A': self.output_path()}, current_device='A') as s: s.execute('open "TEST" for output as 4') output = [_row.strip() for _row in self.get_text(s)] assert output[0] == b'Bad file number\xff'
def __init__(self): Session.__init__(self, extension=self)