#!/usr/bin/env python # igcc - a read-eval-print loop for C/C++ programmers # # Copyright (C) 2009 Andy Balaam # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. import re import libigcc.run from libigcc.run import UserInput import libigcc.source_code import libigcc.version class FakeWriteableFile: def __init__(self): self.lines = [] def write(self, line): self.lines.append(line) class FakeReadableFile: def __init__(self, lines): self.lines = lines def readline(self): if len(self.lines) == 0: return None line = self.lines[0] self.lines = self.lines[1:] return line def assert_strings_equal(str1, str2): if str1 == str2: return raise AssertionError("\n" + str1 + "\n!=\n" + str2) def assert_strings_match(string, re_string): cmp_re = re.compile(re_string, re.DOTALL) if cmp_re.match(string): return raise AssertionError( "\n" + string + "\ndoes not match regular expression\n" + re_string ) def run_program(commands, expected_output, print_welcome=False, argv=None): outputfile = FakeWriteableFile() stdinfile = FakeReadableFile(commands) ret = libigcc.run.run(outputfile, stdinfile, print_welcome, argv) assert_strings_equal("".join(outputfile.lines), expected_output) return ret def run_program_regex_output(commands, expected_output_re): outputfile = FakeWriteableFile() stdinfile = FakeReadableFile(commands) libigcc.run.run(outputfile, stdinfile, False) assert_strings_match("".join(outputfile.lines), expected_output_re) def test_declare_var(): commands = ["int a;"] expected_output = "g++> int a;\ng++> \n" run_program(commands, expected_output) def test_declare_var_assign(): commands = ["int a;", "a = 10;"] expected_output = "g++> int a;\ng++> a = 10;\ng++> \n" run_program(commands, expected_output) def test_declare_and_assign_var_then_print(): commands = ["int a = 10;", 'printf( "%d\\n", a );'] expected_output = """g++> int a = 10; g++> printf( "%d\\n", a ); 10 g++> """ run_program(commands, expected_output) def test_print_twice(): commands = ["int a = 10;", 'printf( "%d\\n", a );', "++a;", 'printf( "%d\\n", a );'] expected_output = """g++> int a = 10; g++> printf( "%d\\n", a ); 10 g++> ++a; g++> printf( "%d\\n", a ); 11 g++> """ run_program(commands, expected_output) def test_compile_error(): commands = ["int a"] # no semicolon expected_output = """g++> int a [Compile error - type .e to see it.] g++> """ run_program(commands, expected_output) def test_compile_error_display(): commands = ["int a", ".e"] # no semicolon # Just look for a string "xpected" in the compiler output. expected_output_re = """g\+\+\> int a \[Compile error - type .e to see it\.\] g\+\+\> \.e .*error:.* g\+\+\> $""" run_program_regex_output(commands, expected_output_re) def test_include(): commands = [ "#include ", "std::vector vec;", r'printf( "%d\n", vec.size() );', ] expected_output = r"""g++> #include g++> std::vector vec; g++> printf( "%d\n", vec.size() ); 0 g++> """ run_program(commands, expected_output) def test_multiple_repeated_includes(): commands = [ " # include ", "std::vector vec;", "#include ", " # include ", "#include ", "using namespace std;", "cout << vec.size() << std::endl;", ] expected_output = r"""g++> # include g++> std::vector vec; g++> #include g++> # include g++> #include g++> using namespace std; g++> cout << vec.size() << std::endl; 0 g++> """ run_program(commands, expected_output) def test_list_program(): commands = ["int a;", "a += 2;", "#include ", ".l"] expected_output = r"""g++> int a; g++> a += 2; g++> #include g++> .l #include int a; a += 2; g++> """ run_program(commands, expected_output) class FakeRunner: def __init__(self, user_commands_string, user_includes_string): self.user_commands_string = user_commands_string self.user_includes_string = user_includes_string def get_user_commands_string(self): return self.user_commands_string def get_user_includes_string(self): return self.user_includes_string def test_list_full_program(): commands = ["int a;", "a += 2;", "#include ", ".L"] fakerunner = FakeRunner("int a;\na += 2;\n", "#include \n") expected_output = """g++> int a; g++> a += 2; g++> #include g++> .L %s g++> """ % libigcc.source_code.get_full_source( fakerunner ) run_program(commands, expected_output) def test_help_message(): commands = [".h"] expected_output_re = ".*Show this help message" run_program_regex_output(commands, expected_output_re) def test_quit(): commands = [".q"] expected_output = "g++> .q\n" ret = run_program(commands, expected_output) assert_strings_equal(ret, "quit") def test_welcome_message(): commands = [] expected_output = ( """igcc $version Released under GNU GPL version 2 or later, with NO WARRANTY. Type ".h" for help. g++> """ ).replace("$version", libigcc.version.VERSION) run_program(commands, expected_output, True) def test_include_dir_on_cmd_line(): commands = ['#include "hello.h"', "hello();"] expected_output = """g++> #include "hello.h" g++> hello(); Hello, g++> """ argv = ["-I", "test/cpp"] run_program(commands, expected_output, argv=argv) def test_2_include_dirs_on_cmd_line(): commands = [ '#include "hello.h"', "hello();", '#include "world.h"', "world();", ] expected_output = """g++> #include "hello.h" g++> hello(); Hello, g++> #include "world.h" g++> world(); world! g++> """ argv = ["-I", "test/cpp", "-I", "test/cpp2"] run_program(commands, expected_output, argv=argv) def test_lib(): commands = [ '#include "math.h"', "int a = max( 4, 6 );", r'printf( "%d\n", a );', ] expected_output = r"""g++> #include "math.h" g++> int a = max( 4, 6 ); g++> printf( "%d\n", a ); 6 g++> """ argv = ["-lm"] run_program(commands, expected_output, argv=argv) def test_print_license(): commands = [".c"] expected_output_re = r"""g\+\+\> \.c .*GNU GENERAL PUBLIC LICENSE.* g\+\+\> """ run_program_regex_output(commands, expected_output_re) def test_print_warranty(): commands = [".w"] expected_output_re = r"""g\+\+\> \.w .*Disclaimer of Warranty.* g\+\+\> """ run_program_regex_output(commands, expected_output_re) def test_runner_get_user_input(): runner = libigcc.run.Runner(None, None, None) runner.user_input = [ UserInput("0", UserInput.COMMAND), UserInput("1", UserInput.COMMAND), UserInput("2", UserInput.COMMAND), ] runner.input_num = 3 assert tuple(runner.get_user_input()) == ( UserInput("0", UserInput.COMMAND), UserInput("1", UserInput.COMMAND), UserInput("2", UserInput.COMMAND), ) runner.input_num = 2 assert tuple(runner.get_user_input()) == ( UserInput("0", UserInput.COMMAND), UserInput("1", UserInput.COMMAND), ) runner.input_num = 1 assert tuple(runner.get_user_input()) == (UserInput("0", UserInput.COMMAND),) def test_runner_get_user_commands(): runner = libigcc.run.Runner(None, None, None) runner.user_input = [ UserInput("0", UserInput.COMMAND), UserInput("1", UserInput.COMMAND), UserInput("X", UserInput.INCLUDE), UserInput("2", UserInput.COMMAND), ] runner.input_num = 4 assert tuple(runner.get_user_commands()) == ("0", "1", "2") runner.input_num = 2 assert tuple(runner.get_user_commands()) == ("0", "1") runner.input_num = 1 assert tuple(runner.get_user_commands()) == ("0",) def test_undo_1(): commands = [ "int a;", "a = 5;", "foobar", ".u", "cout << a << endl;", ] expected_output = r"""g++> int a; g++> a = 5; g++> foobar [Compile error - type .e to see it.] g++> .u [Undone 'foobar'.] g++> cout << a << endl; 5 g++> """ run_program(commands, expected_output) def test_undo_2(): commands = [ "int a = 5;", "++a;", "++a;", "++a;", ".u", ".u", "cout << a << endl;", ] expected_output = r"""g++> int a = 5; g++> ++a; g++> ++a; g++> ++a; g++> .u [Undone '++a;'.] g++> .u [Undone '++a;'.] g++> cout << a << endl; 6 g++> """ run_program(commands, expected_output) def test_undo_before_beginning(): commands = [ "int a = 5;", ".u", ".u", ".u", "cout << a << endl;", ".u", "int b = 7;", "cout << b << endl;", ] expected_output = r"""g++> int a = 5; g++> .u [Undone 'int a = 5;'.] g++> .u [Nothing to undo.] g++> .u [Nothing to undo.] g++> cout << a << endl; [Compile error - type .e to see it.] g++> .u [Undone 'cout << a << endl;'.] g++> int b = 7; g++> cout << b << endl; 7 g++> """ run_program(commands, expected_output) def test_undo_includes_and_commands(): commands = [ "int a = 5;", "#include ", "++a;", ".u", ".u", ".u", ] expected_output = r"""g++> int a = 5; g++> #include g++> ++a; g++> .u [Undone '++a;'.] g++> .u [Undone '#include '.] g++> .u [Undone 'int a = 5;'.] g++> """ run_program(commands, expected_output) def test_redo_1(): commands = [ "int a = 1;", "++a;", ".u", ".r", "cout << a << endl;", ] expected_output = r"""g++> int a = 1; g++> ++a; g++> .u [Undone '++a;'.] g++> .r [Redone '++a;'.] g++> cout << a << endl; 2 g++> """ run_program(commands, expected_output) def test_redo_2(): commands = [ "int a = 1;", "++a;", ".u", ".u", ".r", ".r", "cout << a << endl;", ] expected_output = r"""g++> int a = 1; g++> ++a; g++> .u [Undone '++a;'.] g++> .u [Undone 'int a = 1;'.] g++> .r [Redone 'int a = 1;'.] g++> .r [Redone '++a;'.] g++> cout << a << endl; 2 g++> """ run_program(commands, expected_output) def test_redo_before_beginning(): commands = [ "int a = 1;", "++a;", ".u", ".u", ".u", ".r", ".r", "cout << a << endl;", ] expected_output = r"""g++> int a = 1; g++> ++a; g++> .u [Undone '++a;'.] g++> .u [Undone 'int a = 1;'.] g++> .u [Nothing to undo.] g++> .r [Redone 'int a = 1;'.] g++> .r [Redone '++a;'.] g++> cout << a << endl; 2 g++> """ run_program(commands, expected_output) def test_redo_after_end(): commands = [ "int a = 1;", "++a;", ".u", ".r", ".r", "cout << a << endl;", ] expected_output = r"""g++> int a = 1; g++> ++a; g++> .u [Undone '++a;'.] g++> .r [Redone '++a;'.] g++> .r [Nothing to redo.] g++> cout << a << endl; 2 g++> """ run_program(commands, expected_output) def test_redo_includes_and_commands(): commands = [ "int a = 5;", "#include ", "++a;", ".u", ".u", ".u", ".r", ".r", ".r", ] expected_output = r"""g++> int a = 5; g++> #include g++> ++a; g++> .u [Undone '++a;'.] g++> .u [Undone '#include '.] g++> .u [Undone 'int a = 5;'.] g++> .r [Redone 'int a = 5;'.] g++> .r [Redone '#include '.] g++> .r [Redone '++a;'.] g++> """ run_program(commands, expected_output) def test_undo_then_new_commands(): commands = [ "int a = 5;", "++a;", ".u", "--a;", "--a;", ".u", "cout << a << endl;", ] expected_output = r"""g++> int a = 5; g++> ++a; g++> .u [Undone '++a;'.] g++> --a; g++> --a; g++> .u [Undone '--a;'.] g++> cout << a << endl; 4 g++> """ run_program(commands, expected_output) def test_undo_redo_with_output(): commands = [ "int a = 56;", "cout << a << endl;", ".u", ".r", ".u", "cout << 12 << endl;", ] expected_output = r"""g++> int a = 56; g++> cout << a << endl; 56 g++> .u [Undone 'cout << a << endl;'.] g++> .r [Redone 'cout << a << endl;'.] 56 g++> .u [Undone 'cout << a << endl;'.] g++> cout << 12 << endl; 12 g++> """ run_program(commands, expected_output) def test_print_stderr_twice(): commands = ["int a = 10;", "cerr << a << endl;", "++a;", "cerr << a << endl;"] expected_output = """g++> int a = 10; g++> cerr << a << endl; 10 g++> ++a; g++> cerr << a << endl; 11 g++> """ run_program(commands, expected_output) def test_print_stderr_stdout(): commands = ["int a = 10;", "cerr << a << endl;", "++a;", "cout << a << endl;"] expected_output = """g++> int a = 10; g++> cerr << a << endl; 10 g++> ++a; g++> cout << a << endl; 11 g++> """ run_program(commands, expected_output) def test_undo_stderr_then_new_commands(): commands = [ "int a = 5;", "++a;", ".u", "--a;", "--a;", ".u", "cerr << a << endl;", ] expected_output = r"""g++> int a = 5; g++> ++a; g++> .u [Undone '++a;'.] g++> --a; g++> --a; g++> .u [Undone '--a;'.] g++> cerr << a << endl; 4 g++> """ run_program(commands, expected_output) def main(): test_declare_var() test_declare_var_assign() test_declare_and_assign_var_then_print() test_print_twice() test_compile_error() test_compile_error_display() test_include() test_multiple_repeated_includes() test_list_program() test_list_full_program() test_help_message() test_quit() test_welcome_message() test_include_dir_on_cmd_line() test_2_include_dirs_on_cmd_line() test_lib() test_print_license() test_print_warranty() test_runner_get_user_input() test_runner_get_user_commands() test_undo_1() test_undo_2() test_undo_before_beginning() test_undo_includes_and_commands() test_redo_1() test_redo_2() test_redo_before_beginning() test_redo_after_end() test_redo_includes_and_commands() test_undo_then_new_commands() test_undo_redo_with_output() test_print_stderr_twice() test_print_stderr_stdout() test_undo_stderr_then_new_commands() # test_readline_history(); # test_print_command(); # test_edit_in_vim(); # test_compile_warning() # test_remove_compile_error_message() print("All tests passed.") if __name__ == "__main__": main()