Index: lit/lit/TestRunner.py =================================================================== --- lit/lit/TestRunner.py +++ lit/lit/TestRunner.py @@ -1,7 +1,11 @@ from __future__ import absolute_import +import difflib +import itertools import os, signal, subprocess, sys import re +import stat import platform +import shutil import tempfile import threading @@ -302,6 +306,105 @@ return stdout.getvalue() return "" +def check_option_in_arg(option, arg, flag): + arg_without_option = filter(lambda char: char != option, arg) + flag = flag or (len(arg_without_option) != len(arg)) + return flag, arg_without_option + +def executeBuiltinMkdir(cmd): + """executeBuiltinMkdir - Create new directories.""" + # Implement mkdir flags. We only support -p option. + args = cmd.args[1:] + dirs = None + parent = False + for idx, arg in enumerate(args): + if arg[0] != '-': + dirs = args[idx:] + break + parent, remaining_arg = check_option_in_arg('p', arg[1:], parent) + if len(remaining_arg) > 0: + raise InternalShellError(cmd, "Unsupported: 'mkdir' does not " + "support -%s option" % remaining_arg) + + if dirs is None: + raise InternalShellError(cmd, "Error: 'mkdir' is missing an operand") + + for dir in dirs: + if parent: + lit.util.mkdir_p(dir) + else: + try: + os.mkdir(dir) + except OSError as e: + raise InternalShellError(cmd, "Error: 'mkdir' command failed") + +def executeBuiltinDiff(cmd): + """executeBuiltinDiff - Compare files line by line.""" + if len(cmd.args) != 3: + raise InternalShellError(cmd, "Error: 'diff' only " + "supports a fromfile and tofile") + + fromfile = cmd.args[1] + tofile = cmd.args[2] + different = False + try: + with open(fromfile, 'r') as f1, open(tofile, 'r') as f2: + for diff in difflib.context_diff(f1.readlines(), + f2.readlines(), + fromfile, + tofile): + sys.stdout.write(diff) + different = True + except IOError as e: + raise InternalShellError(cmd, "Error: 'diff' command failed") + + if different: + raise InternalShellError(cmd, "Error: The contexts " + "of fromfile and tofile are different") + +def executeBuiltinRm(cmd, cmd_shenv): + """executeBuiltinRm - Removes (deletes) files or directories.""" + # Expand all glob expressions + args = cmd.args[1:] + args = expand_glob_expressions(args, cmd_shenv.cwd) + + # Implement rm flags. We only support -f and in combination with -r. + dirs = None + force = False + recursive = False + for idx, arg in enumerate(args): + if arg [0] != '-': + dirs = args[idx:] + break + force, arg = check_option_in_arg('f', arg[1:], force) + recursive, arg = check_option_in_arg('r', arg, recursive) + if len(arg) > 0: + raise InternalShellError(cmd, "Unsupported: 'rm' does not " + "support -%s option" % arg) + + def on_rm_error(func, path, exc_info): + # path contains the path of the file that couldn't be removed + # let's just assume that it's read-only and remove it. + os.chmod(path, stat.S_IMODE( os.stat(path).st_mode) | stat.S_IWRITE) + os.remove(path) + + for dir in dirs: + if force and not os.path.exists(dir): + continue + try: + if os.path.isdir(dir): + if not recursive: + raise InternalShellError(cmd, "Error: " + "%s is a directory" % dir) + shutil.rmtree(dir, onerror = on_rm_error if force else None) + else: + if force and not os.access(dir, os.W_OK): + os.chmod(dir, + stat.S_IMODE(os.stat(dir).st_mode) | stat.S_IWRITE) + os.remove(dir) + except OSError as e: + raise InternalShellError(cmd, "Error: 'rm' command failed") + def processRedirects(cmd, stdin_source, cmd_shenv, opened_files): """Return the standard fds for cmd after applying redirects @@ -460,6 +563,27 @@ updateEnv(shenv, cmd.commands[0]) return 0 + if cmd.commands[0].args[0] == 'mkdir': + if len(cmd.commands) != 1: + raise InternalShellError(cmd.commands[0], "Unsupported: 'mkdir' " + "cannot be part of a pipeline") + executeBuiltinMkdir(cmd.commands[0]) + return 0 + + if cmd.commands[0].args[0] == 'diff': + if len(cmd.commands) != 1: + raise InternalShellError(cmd.commands[0], "Unsupported: 'diff' " + "cannot be part of a pipeline") + executeBuiltinDiff(cmd.commands[0]) + return 0 + + if cmd.commands[0].args[0] == 'rm': + if len(cmd.commands) != 1: + raise InternalShellError(cmd.commands[0], "Unsupported: 'rm' " + "cannot be part of a pipeline") + executeBuiltinRm(cmd.commands[0], shenv) + return 0 + procs = [] default_stdin = subprocess.PIPE stderrTempFiles = [] Index: lit/tests/Inputs/shtest-shell/diff-error-0.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/diff-error-0.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported diff (cannot be part of a pipeline). +# +# RUN: diff temp1.txt temp2.txt | echo Output Index: lit/tests/Inputs/shtest-shell/diff-error-1.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/diff-error-1.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported diff (only supports a fromfile and tofile). +# +# RUN: diff -u temp1.txt temp2.txt Index: lit/tests/Inputs/shtest-shell/diff-error-2.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/diff-error-2.txt @@ -0,0 +1,3 @@ +# Check error on an internal shell error (missing tofile) +# +# RUN: diff temp.txt Index: lit/tests/Inputs/shtest-shell/diff-error-3.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/diff-error-3.txt @@ -0,0 +1,3 @@ +# Check error on an internal shell error (unable to find compared files) +# +# RUN: diff temp.txt temp1.txt Index: lit/tests/Inputs/shtest-shell/diff-error-4.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/diff-error-4.txt @@ -0,0 +1,5 @@ +# Check error on an internal shell error (file's contexts are different) +# +# RUN: echo "hello-first" > %t +# RUN: echo "hello-second" > %t1 +# RUN: diff %t %t1 Index: lit/tests/Inputs/shtest-shell/mkdir-error-0.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/mkdir-error-0.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported mkdir (cannot be part of a pipeline). +# +# RUN: mkdir -p temp | rm -rf temp Index: lit/tests/Inputs/shtest-shell/mkdir-error-1.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/mkdir-error-1.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported mkdir (only does not support -i option). +# +# RUN: mkdir -i temp Index: lit/tests/Inputs/shtest-shell/mkdir-error-2.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/mkdir-error-2.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported mkdir (missing operand). +# +# RUN: mkdir -p Index: lit/tests/Inputs/shtest-shell/rm-error-0.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/rm-error-0.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported rm. (cannot be part of a pipeline) +# +# RUN: rm -rf temp | echo "hello" Index: lit/tests/Inputs/shtest-shell/rm-error-1.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/rm-error-1.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported rm (only does not support -i option). +# +# RUN: rm -f -i temp Index: lit/tests/Inputs/shtest-shell/rm-error-2.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/rm-error-2.txt @@ -0,0 +1,3 @@ +# Check error on a unsupported rm (only supports -f option and in combination with -r). +# +# RUN: rm -r hello Index: lit/tests/Inputs/shtest-shell/rm-error-3.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/rm-error-3.txt @@ -0,0 +1,4 @@ +# Check error on a unsupported rm (can't remove test since it is a directory). +# +# RUN: mkdir -p test +# RUN: rm -f test Index: lit/tests/Inputs/shtest-shell/valid-shell.txt =================================================================== --- /dev/null +++ lit/tests/Inputs/shtest-shell/valid-shell.txt @@ -0,0 +1,26 @@ +# Check mkdir and rm operations. +# +# RUN: rm -f -r %T/test +# RUN: mkdir -p %T/test +# RUN: cd %T/test +# RUN: rm -rf test1 +# RUN: mkdir test1 +# RUN: cd test1 +# RUN: rm -rf %T/test1 %T/test2 %T/test3 +# RUN: mkdir -p %T/test1 %T/test2 %T/test3 +# RUN: cd %T/test1 +# RUN: cd %T/test2 +# RUN: cd %T/test3 + +# Check diff operations. +# +# RUN: echo "hello" > %t +# RUN: echo "hello" > %t1 +# RUN: diff %t %t1 + +# Check rm operations. +# +# RUN: rm -f %T/test/temp.txt +# RUN: rm -fr %T/test +# RUN: rm -f %T/test.txt +# RUN: rm -r -f %T/* Index: lit/tests/max-failures.py =================================================================== --- lit/tests/max-failures.py +++ lit/tests/max-failures.py @@ -8,7 +8,7 @@ # # END. -# CHECK: Failing Tests (3) +# CHECK: Failing Tests (15) # CHECK: Failing Tests (1) # CHECK: Failing Tests (2) # CHECK: error: Setting --max-failures to 0 does not have any effect. Index: lit/tests/shtest-shell.py =================================================================== --- lit/tests/shtest-shell.py +++ lit/tests/shtest-shell.py @@ -10,6 +10,47 @@ # CHECK: -- Testing: + +# CHECK: FAIL: shtest-shell :: diff-error-0.txt +# CHECK: *** TEST 'shtest-shell :: diff-error-0.txt' FAILED *** +# CHECK: $ "diff" "temp1.txt" "temp2.txt" +# CHECK: # command stderr: +# CHECK: Unsupported: 'diff' cannot be part of a pipeline +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: diff-error-1.txt +# CHECK: *** TEST 'shtest-shell :: diff-error-1.txt' FAILED *** +# CHECK: $ "diff" "-u" "temp1.txt" "temp2.txt" +# CHECK: # command stderr: +# CHECK: Error: 'diff' only supports a fromfile and tofile +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: diff-error-2.txt +# CHECK: *** TEST 'shtest-shell :: diff-error-2.txt' FAILED *** +# CHECK: $ "diff" "temp.txt" +# CHECK: # command stderr: +# CHECK: Error: 'diff' only supports a fromfile and tofile +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: diff-error-3.txt +# CHECK: *** TEST 'shtest-shell :: diff-error-3.txt' FAILED *** +# CHECK: $ "diff" "temp.txt" "temp1.txt" +# CHECK: # command stderr: +# CHECK: Error: 'diff' command failed +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: diff-error-4.txt +# CHECK: *** TEST 'shtest-shell :: diff-error-4.txt' FAILED *** +# CHECK: $ "diff" +# CHECK: # command stderr: +# CHECK: Error: The contexts of fromfile and tofile are different +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + # CHECK: FAIL: shtest-shell :: error-0.txt # CHECK: *** TEST 'shtest-shell :: error-0.txt' FAILED *** # CHECK: $ "not-a-real-command" @@ -30,7 +71,65 @@ # CHECK: Unsupported redirect: # CHECK: *** +# CHECK: FAIL: shtest-shell :: mkdir-error-0.txt +# CHECK: *** TEST 'shtest-shell :: mkdir-error-0.txt' FAILED *** +# CHECK: $ "mkdir" "-p" "temp" +# CHECK: # command stderr: +# CHECK: Unsupported: 'mkdir' cannot be part of a pipeline +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: mkdir-error-1.txt +# CHECK: *** TEST 'shtest-shell :: mkdir-error-1.txt' FAILED *** +# CHECK: $ "mkdir" "-i" "temp" +# CHECK: # command stderr: +# CHECK: Unsupported: 'mkdir' does not support -i option +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: mkdir-error-2.txt +# CHECK: *** TEST 'shtest-shell :: mkdir-error-2.txt' FAILED *** +# CHECK: $ "mkdir" "-p" +# CHECK: # command stderr: +# CHECK: Error: 'mkdir' is missing an operand +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + # CHECK: PASS: shtest-shell :: redirects.txt + +# CHECK: FAIL: shtest-shell :: rm-error-0.txt +# CHECK: *** TEST 'shtest-shell :: rm-error-0.txt' FAILED *** +# CHECK: $ "rm" "-rf" "temp" +# CHECK: # command stderr: +# CHECK: Unsupported: 'rm' cannot be part of a pipeline +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: rm-error-1.txt +# CHECK: *** TEST 'shtest-shell :: rm-error-1.txt' FAILED *** +# CHECK: $ "rm" "-f" "-i" "temp" +# CHECK: # command stderr: +# CHECK: Unsupported: 'rm' does not support -i option +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: rm-error-2.txt +# CHECK: *** TEST 'shtest-shell :: rm-error-2.txt' FAILED *** +# CHECK: $ "rm" "-r" "hello" +# CHECK: # command stderr: +# CHECK: Error: 'rm' command failed +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + +# CHECK: FAIL: shtest-shell :: rm-error-3.txt +# CHECK: *** TEST 'shtest-shell :: rm-error-3.txt' FAILED *** +# CHECK: $ "rm" "-f" "test" +# CHECK: # command stderr: +# CHECK: Error: test is a directory +# CHECK: error: command failed with exit status: 127 +# CHECK: *** + # CHECK: PASS: shtest-shell :: sequencing-0.txt # CHECK: XFAIL: shtest-shell :: sequencing-1.txt -# CHECK: Failing Tests (3) +# CHECK: PASS: shtest-shell :: valid-shell.txt +# CHECK: Failing Tests (15)