Index: test/tools/llvm-cov/multithreaded-report.test =================================================================== --- test/tools/llvm-cov/multithreaded-report.test +++ test/tools/llvm-cov/multithreaded-report.test @@ -1,8 +1,5 @@ # Test "report" command with and without multiple threads. -# Temporarily disable the test on Windows as it doesn't support "diff -r". -REQUIRES: shell - RUN: llvm-cov report -num-threads=1 \ RUN: -path-equivalence=/tmp,%S/Inputs \ RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \ Index: utils/lit/lit/TestRunner.py =================================================================== --- utils/lit/lit/TestRunner.py +++ utils/lit/lit/TestRunner.py @@ -346,14 +346,15 @@ """executeBuiltinDiff - Compare files line by line.""" args = expand_glob_expressions(cmd.args, cmd_shenv.cwd)[1:] try: - opts, args = getopt.gnu_getopt(args, "wbu", ["strip-trailing-cr"]) + opts, args = getopt.gnu_getopt(args, "wbur", ["strip-trailing-cr"]) except getopt.GetoptError as err: raise InternalShellError(cmd, "Unsupported: 'diff': %s" % str(err)) - filelines, filepaths = ([] for i in range(2)) + filelines, filepaths, dir_trees = ([] for i in range(3)) ignore_all_space = False ignore_space_change = False unified_diff = False + recursive_diff = False strip_trailing_cr = False for o, a in opts: if o == "-w": @@ -362,6 +363,8 @@ ignore_space_change = True elif o == "-u": unified_diff = True + elif o == "-r": + recursive_diff = True elif o == "--strip-trailing-cr": strip_trailing_cr = True else: @@ -370,17 +373,24 @@ if len(args) != 2: raise InternalShellError(cmd, "Error: missing or extra operand") - stderr = StringIO() - stdout = StringIO() - exitCode = 0 - try: - for file in args: - if not os.path.isabs(file): - file = os.path.realpath(os.path.join(cmd_shenv.cwd, file)) - filepaths.append(file) + def getDirTree(path, basedir=""): + # Tree is a tuple of form (dirname, child_trees). + # An empty dir has child_trees = [], a file has child_trees = None. + child_trees = [] + for dirname, child_dirs, files in os.walk(os.path.join(basedir, path)): + for child_dir in child_dirs: + child_trees.append(getDirTree(child_dir, dirname)) + for filename in files: + child_trees.append((filename, None)) + return path, sorted(child_trees) + + def compareTwoFiles(filepaths): + filelines = [] + for file in filepaths: with open(file, 'r') as f: filelines.append(f.readlines()) + exitCode = 0 def compose2(f, g): return lambda x: f(g(x)) @@ -399,6 +409,99 @@ for diff in func(filelines[0], filelines[1], filepaths[0], filepaths[1]): stdout.write(diff) exitCode = 1 + return exitCode + + def printDirVsFile(dir_path, file_path): + if os.path.getsize(file_path): + msg = "File %s is a directory while file %s is a regular file" + else: + msg = "File %s is a directory while file %s is a regular empty file" + stdout.write(msg % (dir_path, file_path) + "\n") + + def printFileVsDir(file_path, dir_path): + if os.path.getsize(file_path): + msg = "File %s is a regular file while file %s is a directory" + else: + msg = "File %s is a regular empty file while file %s is a directory" + stdout.write(msg % (file_path, dir_path) + "\n") + + def printOnlyIn(basedir, path, name): + stdout.write("Only in %s: %s\n" % (os.path.join(basedir, path), name)) + + def compareDirTrees(dir_trees, base_paths=["", ""]): + # Dirnames of the trees are not checked, it's caller's responsibility, + # as top-level dirnames are always different. Base paths are important + # for doing os.walk, but we don't put it into tree's dirname in order + # to speed up string comparison below and while sorting in getDirTree. + left_tree, right_tree = dir_trees[0], dir_trees[1] + left_base, right_base = base_paths[0], base_paths[1] + + # Compare two files or report file vs. directory mismatch. + if left_tree[1] is None and right_tree[1] is None: + return compareTwoFiles([os.path.join(left_base, left_tree[0]), + os.path.join(right_base, right_tree[0])]) + + if left_tree[1] is None and right_tree[1] is not None: + printFileVsDir(os.path.join(left_base, left_tree[0]), + os.path.join(right_base, right_tree[0])) + return 1 + + if left_tree[1] is not None and right_tree[1] is None: + printDirVsFile(os.path.join(left_base, left_tree[0]), + os.path.join(right_base, right_tree[0])) + return 1 + + # Compare two directories via recursive use of compareDirTrees. + exitCode = 0 + left_names = [node[0] for node in left_tree[1]] + right_names = [node[0] for node in right_tree[1]] + l, r = 0, 0 + while l < len(left_names) and r < len(right_names): + # Names are sorted in getDirTree, rely on that order. + if left_names[l] < right_names[r]: + exitCode = 1 + printOnlyIn(left_base, left_tree[0], left_names[l]) + l += 1 + elif left_names[l] > right_names[r]: + exitCode = 1 + printOnlyIn(right_base, right_tree[0], right_names[r]) + r += 1 + else: + exitCode |= compareDirTrees([left_tree[1][l], right_tree[1][r]], + [os.path.join(left_base, left_tree[0]), + os.path.join(right_base, right_tree[0])]) + l += 1 + r += 1 + + # At least one of the trees has ended. Report names from the other tree. + while l < len(left_names): + exitCode = 1 + printOnlyIn(left_base, left_tree[0], left_names[l]) + l += 1 + while r < len(right_names): + exitCode = 1 + printOnlyIn(right_base, right_tree[0], right_names[r]) + r += 1 + return exitCode + + stderr = StringIO() + stdout = StringIO() + exitCode = 0 + try: + for file in args: + if not os.path.isabs(file): + file = os.path.realpath(os.path.join(cmd_shenv.cwd, file)) + + if recursive_diff: + dir_trees.append(getDirTree(file)) + else: + filepaths.append(file) + + if not recursive_diff: + exitCode = compareTwoFiles(filepaths) + else: + exitCode = compareDirTrees(dir_trees) + except IOError as err: stderr.write("Error: 'diff' command failed, %s\n" % str(err)) exitCode = 1 Index: utils/lit/tests/Inputs/shtest-shell/diff-r-error-0.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r-error-0.txt @@ -0,0 +1,8 @@ +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# Add two empty files with different names, "diff -r" should fail. +# RUN: touch %t/dir1/dir1unique +# RUN: touch %t/dir2/dir2unique +# RUN: diff -r %t/dir1 %t/dir2 Index: utils/lit/tests/Inputs/shtest-shell/diff-r-error-1.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r-error-1.txt @@ -0,0 +1,9 @@ +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# Same filenames in subdirs with different content, "diff -r" should fail. +# RUN: mkdir -p %t/dir1/subdir %t/dir2/subdir +# RUN: echo "12345" > %t/dir1/subdir/f01 +# RUN: echo "00000" > %t/dir2/subdir/f01 +# RUN: diff -r %t/dir1 %t/dir2 Index: utils/lit/tests/Inputs/shtest-shell/diff-r-error-2.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r-error-2.txt @@ -0,0 +1,7 @@ +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# An extra file in one of the directories, "diff -r" should fail. +# RUN: echo extra > %t/dir2/extrafile +# RUN: diff -r %t/dir1 %t/dir2 \ No newline at end of file Index: utils/lit/tests/Inputs/shtest-shell/diff-r-error-3.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r-error-3.txt @@ -0,0 +1,7 @@ +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# An extra directory in one of the directories, "diff -r" should fail. +# RUN: mkdir -p %t/dir1/extra_subdir +# RUN: diff -r %t/dir1 %t/dir2 Index: utils/lit/tests/Inputs/shtest-shell/diff-r-error-4.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r-error-4.txt @@ -0,0 +1,8 @@ +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# Directory vs. File mismatch, "diff -r" should fail. +# RUN: mkdir -p %t/dir1/extra_subdir +# RUN: echo ZYX > %t/dir2/extra_subdir +# RUN: diff -r %t/dir1 %t/dir2 Index: utils/lit/tests/Inputs/shtest-shell/diff-r-error-5.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r-error-5.txt @@ -0,0 +1,8 @@ +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# Non-empty extra directory, "diff -r" should fail. +# RUN: mkdir -p %t/dir1/extra_subdir +# RUN: echo ZYX > %t/dir1/extra_subdir/extra_file +# RUN: diff -r %t/dir1 %t/dir2 Index: utils/lit/tests/Inputs/shtest-shell/diff-r-error-6.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r-error-6.txt @@ -0,0 +1,8 @@ +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# Empty file vs directory mismatch, diff -r should fail. +# RUN: echo -n > %t/dir1/extra_file +# RUN: mkdir -p %t/dir2/extra_file +# RUN: diff -r %t/dir1 %t/dir2 Index: utils/lit/tests/Inputs/shtest-shell/diff-r.txt =================================================================== --- /dev/null +++ utils/lit/tests/Inputs/shtest-shell/diff-r.txt @@ -0,0 +1,20 @@ +# Check recursive diff ("diff -r"). + +# Create two directories for further comparison. +# RUN: rm -rf %t/dir1 %t/dir2 +# RUN: mkdir -p %t/dir1 %t/dir2 + +# Create same files in both of the dirs. +# RUN: echo "hello" > %t/dir1/f1 +# RUN: echo "hello" > %t/dir2/f1 + +# Create same subdirectories with same contents. +# RUN: mkdir -p %t/dir1/subdir %t/dir2/subdir +# RUN: echo "12345" > %t/dir1/subdir/f01 +# RUN: echo "12345" > %t/dir2/subdir/f01 +# RUN: echo -e "xxx\nzzz\nyyy" > %t/dir1/subdir/f02 +# RUN: echo -e "xxx\nzzz\nyyy" > %t/dir2/subdir/f02 + +# Create empty subdirectories with same names. +# RUN: mkdir -p %t/dir1/empty_subdir %t/dir2/empty_subdir +# RUN: diff -r %t/dir1 %t/dir2 Index: utils/lit/tests/max-failures.py =================================================================== --- utils/lit/tests/max-failures.py +++ utils/lit/tests/max-failures.py @@ -8,7 +8,7 @@ # # END. -# CHECK: Failing Tests (17) +# CHECK: Failing Tests (24) # CHECK: Failing Tests (1) # CHECK: Failing Tests (2) # CHECK: error: Setting --max-failures to 0 does not have any effect. Index: utils/lit/tests/shtest-shell.py =================================================================== --- utils/lit/tests/shtest-shell.py +++ utils/lit/tests/shtest-shell.py @@ -71,6 +71,61 @@ # CHECK: error: command failed with exit status: 127 # CHECK: *** +# CHECK: FAIL: shtest-shell :: diff-r-error-0.txt +# CHECK: *** TEST 'shtest-shell :: diff-r-error-0.txt' FAILED *** +# CEHCK: $ "diff" "-r" +# CHECK: # command output: +# CHECK: Only in {{.*}}dir1: dir1unique +# CHECK: Only in {{.*}}dir2: dir2unique +# CHECK: error: command failed with exit status: 1 + +# CHECK: FAIL: shtest-shell :: diff-r-error-1.txt +# CHECK: *** TEST 'shtest-shell :: diff-r-error-1.txt' FAILED *** +# CEHCK: $ "diff" "-r" +# CHECK: # command output: +# CHECK: *** {{.*}}dir1{{.*}}subdir{{.*}}f01 +# CHECK: --- {{.*}}dir2{{.*}}subdir{{.*}}f01 +# CHECK: 12345 +# CHECK: 00000 +# CHECK: error: command failed with exit status: 1 + +# CHECK: FAIL: shtest-shell :: diff-r-error-2.txt +# CHECK: *** TEST 'shtest-shell :: diff-r-error-2.txt' FAILED *** +# CEHCK: $ "diff" "-r" +# CHECK: # command output: +# CHECK: Only in {{.*}}dir2: extrafile +# CHECK: error: command failed with exit status: 1 + +# CHECK: FAIL: shtest-shell :: diff-r-error-3.txt +# CHECK: *** TEST 'shtest-shell :: diff-r-error-3.txt' FAILED *** +# CEHCK: $ "diff" "-r" +# CHECK: # command output: +# CHECK: Only in {{.*}}dir1: extra_subdir +# CHECK: error: command failed with exit status: 1 + +# CHECK: FAIL: shtest-shell :: diff-r-error-4.txt +# CHECK: *** TEST 'shtest-shell :: diff-r-error-4.txt' FAILED *** +# CEHCK: $ "diff" "-r" +# CHECK: # command output: +# CHECK: File {{.*}}dir1{{.*}}extra_subdir is a directory while file {{.*}}dir2{{.*}}extra_subdir is a regular file +# CHECK: error: command failed with exit status: 1 + +# CHECK: FAIL: shtest-shell :: diff-r-error-5.txt +# CHECK: *** TEST 'shtest-shell :: diff-r-error-5.txt' FAILED *** +# CEHCK: $ "diff" "-r" +# CHECK: # command output: +# CHECK: Only in {{.*}}dir1: extra_subdir +# CHECK: error: command failed with exit status: 1 + +# CHECK: FAIL: shtest-shell :: diff-r-error-6.txt +# CHECK: *** TEST 'shtest-shell :: diff-r-error-6.txt' FAILED *** +# CEHCK: $ "diff" "-r" +# CHECK: # command output: +# CHECK: File {{.*}}dir1{{.*}}extra_file is a regular empty file while file {{.*}}dir2{{.*}}extra_file is a directory +# CHECK: error: command failed with exit status: 1 + +# CHECK: PASS: shtest-shell :: diff-r.txt + # CHECK: FAIL: shtest-shell :: error-0.txt # CHECK: *** TEST 'shtest-shell :: error-0.txt' FAILED *** # CHECK: $ "not-a-real-command" @@ -149,4 +204,4 @@ # CHECK: PASS: shtest-shell :: sequencing-0.txt # CHECK: XFAIL: shtest-shell :: sequencing-1.txt # CHECK: PASS: shtest-shell :: valid-shell.txt -# CHECK: Failing Tests (17) +# CHECK: Failing Tests (24)