blob: 88f45fc505246c43ae2d1fd99e2f2cd78e4b98e7 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2020 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import contextlib
import filecmp
import os
import shutil
import tempfile
import time
import unittest
from pathlib import Path
from unittest import mock
import output_cacher
class TempFileTransformTests(unittest.TestCase):
def test_invalid(self):
self.assertFalse(output_cacher.TempFileTransform().valid)
def test_temp_dir_only(self):
self.assertTrue(
output_cacher.TempFileTransform(temp_dir="my_tmp").valid
)
self.assertTrue(
output_cacher.TempFileTransform(temp_dir="/my_tmp").valid
)
def test_basename_prefix_only(self):
self.assertTrue(
output_cacher.TempFileTransform(basename_prefix="temp-").valid
)
def test_suffix_only(self):
self.assertTrue(output_cacher.TempFileTransform(suffix=".tmp").valid)
def test_temp_dir_and_suffix(self):
self.assertTrue(
output_cacher.TempFileTransform(
temp_dir="throw/away", suffix=".tmp"
).valid
)
def test_transform_suffix_only(self):
self.assertEqual(
output_cacher.TempFileTransform(suffix=".tmp").transform(
Path("foo/bar.o")
),
Path("foo/bar.o.tmp"),
)
def test_transform_temp_dir_only(self):
self.assertEqual(
output_cacher.TempFileTransform(temp_dir=Path("t/m/p")).transform(
Path("foo/bar.o")
),
Path("t/m/p/foo/bar.o"),
)
def test_transform_suffix_and_temp_dir(self):
self.assertEqual(
output_cacher.TempFileTransform(
temp_dir=Path("/t/m/p"), suffix=".foo"
).transform(Path("foo/bar.o")),
Path("/t/m/p/foo/bar.o.foo"),
)
def test_transform_basename_prefix_only(self):
self.assertEqual(
output_cacher.TempFileTransform(basename_prefix="fake.").transform(
Path("bar.o")
),
Path("fake.bar.o"),
)
self.assertEqual(
output_cacher.TempFileTransform(basename_prefix="fake.").transform(
Path("foo/bar.o")
),
Path("foo/fake.bar.o"),
)
def test_transform_basename_prefix_and_temp_dir(self):
self.assertEqual(
output_cacher.TempFileTransform(
temp_dir="/t/m/p", basename_prefix="xyz-"
).transform(Path("foo/bar.o")),
Path("/t/m/p/foo/xyz-bar.o"),
)
class SplitTransformJoinTest(unittest.TestCase):
def test_no_change(self):
self.assertEqual(
output_cacher.split_transform_join("text", "=", lambda x: x), "text"
)
def test_repeat(self):
self.assertEqual(
output_cacher.split_transform_join("text", "=", lambda x: x + x),
"texttext",
)
def test_with_split(self):
self.assertEqual(
output_cacher.split_transform_join("a=b", "=", lambda x: x + x),
"aa=bb",
)
def test_with_split_recorded(self):
renamed_tokens = {}
def recorded_transform(x):
new_text = x + x
renamed_tokens[x] = new_text
return new_text
self.assertEqual(
output_cacher.split_transform_join("a=b", "=", recorded_transform),
"aa=bb",
)
self.assertEqual(renamed_tokens, {"a": "aa", "b": "bb"})
class OutputSubstitutionTest(unittest.TestCase):
def test_no_prev_opt(self):
subst = output_cacher.OutputSubstitution("foo")
self.assertEqual(subst.output_name, "foo")
self.assertEqual(subst.match_previous_option, "")
def test_with_prev_opt(self):
subst = output_cacher.OutputSubstitution("substitute_after:--out:foo")
self.assertEqual(subst.output_name, "foo")
self.assertEqual(subst.match_previous_option, "--out")
def test_malformed_prev_opt(self):
with self.assertRaises(ValueError):
output_cacher.OutputSubstitution("substitute_after:--outfoo")
class LexicallyRewriteTokenTest(unittest.TestCase):
def test_repeat_text(self):
self.assertEqual(
output_cacher.lexically_rewrite_token("foo", lambda x: x + x),
"foofoo",
)
def test_delimters_only(self):
self.assertEqual(
output_cacher.lexically_rewrite_token(",,==,=,=,", lambda x: x + x),
",,==,=,=,",
)
def test_flag_with_value(self):
def transform(x):
if x.startswith("file"):
return "tmp-" + x
else:
return x
self.assertEqual(
output_cacher.lexically_rewrite_token("--foo=file1", transform),
"--foo=tmp-file1",
)
self.assertEqual(
output_cacher.lexically_rewrite_token(
"notfile,file1,file2,notfile", transform
),
"notfile,tmp-file1,tmp-file2,notfile",
)
self.assertEqual(
output_cacher.lexically_rewrite_token(
"--foo=file1,file2", transform
),
"--foo=tmp-file1,tmp-file2",
)
class EnsureFileExists(unittest.TestCase):
def test_file_exists(self):
with mock.patch.object(
Path, "exists", return_value=True
) as mock_exists:
output_cacher.ensure_file_exists(Path("some/file.txt"))
mock_exists.assert_called()
# just make sure no exception raised
def test_file_not_exists(self):
with mock.patch.object(
Path, "exists", return_value=False
) as mock_exists:
with mock.patch.object(time, "sleep") as mock_sleep:
with self.assertRaises(FileNotFoundError):
output_cacher.ensure_file_exists(Path("not/a/file.txt"))
mock_exists.assert_called()
def test_file_exists_later(self):
with mock.patch.object(
Path, "exists", side_effect=[False, True]
) as mock_exists:
with mock.patch.object(time, "sleep") as mock_sleep:
output_cacher.ensure_file_exists(Path("late/file.txt"))
mock_exists.assert_called()
mock_sleep.assert_called()
# just make sure no exception raised
@contextlib.contextmanager
def chdir_cm(d: Path):
"""FIXME: replace with contextlib.chdir(), once Python 3.11 is default."""
save_dir = os.getcwd()
os.chdir(d) # could raise OSError
try:
yield
finally:
os.chdir(save_dir)
class CopyPreserveSubpathTests(unittest.TestCase):
def test_subpath(self):
with tempfile.TemporaryDirectory() as td1:
tdp1 = Path(td1)
dest_dir = tdp1 / "backups"
with chdir_cm(tdp1): # working directory
srcdir = Path("aa/bb")
srcdir.mkdir(parents=True, exist_ok=True)
src_file = srcdir / "c.txt"
src_file.write_text("hello\n")
output_cacher.copy_preserve_subpath(src_file, dest_dir)
dest_file = dest_dir / src_file
self.assertTrue(filecmp.cmp(src_file, dest_file, shallow=False))
class DefaultDiffActionTests(unittest.TestCase):
def test_no_copy(self):
file1 = Path("srcdir-x/file1.bin")
file2 = Path("srcdir-y/file2.bin")
with mock.patch.object(output_cacher, "detail_diff") as mock_diff:
with mock.patch.object(
output_cacher, "copy_preserve_subpath"
) as mock_copy:
output_cacher.default_diff_action(file1, file2)
mock_diff.assert_called_with(file1, file2)
mock_copy.assert_not_called()
def test_with_copy(self):
file1 = Path("srcdir-x/file1.bin")
file2 = Path("srcdir-y/file2.bin")
export_dir = Path("bad/diffs")
with mock.patch.object(output_cacher, "detail_diff") as mock_diff:
with mock.patch.object(
output_cacher, "copy_preserve_subpath"
) as mock_copy:
output_cacher.default_diff_action(
file1, file2, miscomparison_export_dir=export_dir
)
mock_diff.assert_called_with(file1, file2)
mock_copy.assert_has_calls(
[
mock.call(file1, export_dir),
mock.call(file2, export_dir),
],
any_order=True,
)
class MoveIfDifferentTests(unittest.TestCase):
def test_nonexistent_source(self):
with mock.patch.object(
Path, "exists", return_value=False
) as mock_exists:
with mock.patch.object(Path, "rename") as mock_move:
with mock.patch.object(Path, "unlink") as mock_remove:
with mock.patch.object(time, "sleep") as mock_sleep:
with self.assertRaises(FileNotFoundError):
output_cacher.move_if_different(
Path("source.txt"), Path("dest.txt")
)
mock_exists.assert_called_with()
mock_move.assert_not_called()
mock_remove.assert_not_called()
def test_new_output(self):
with mock.patch.object(
Path,
"exists",
side_effect=[True, False], # source.txt, dest.txt
) as mock_exists:
with mock.patch.object(output_cacher, "files_match") as mock_diff:
with mock.patch.object(Path, "rename") as mock_move:
with mock.patch.object(Path, "unlink") as mock_remove:
moved = output_cacher.move_if_different(
Path("source.txt"), Path("dest.txt")
)
self.assertTrue(moved)
mock_exists.assert_called_with()
mock_diff.assert_not_called()
mock_move.assert_called_with(Path("dest.txt"))
mock_remove.assert_not_called()
def test_updating_output(self):
with mock.patch.object(
Path, "exists", return_value=True
) as mock_exists:
with mock.patch.object(
output_cacher, "files_match", return_value=False
) as mock_diff:
with mock.patch.object(Path, "rename") as mock_move:
with mock.patch.object(Path, "unlink") as mock_remove:
moved = output_cacher.move_if_different(
Path("source.txt"), Path("dest.txt")
)
self.assertTrue(moved)
mock_exists.assert_called_with()
mock_diff.assert_called_with(Path("dest.txt"), Path("source.txt"))
mock_move.assert_called_with(Path("dest.txt"))
mock_remove.assert_not_called()
def test_cached_output(self):
with mock.patch.object(
Path, "exists", return_value=True
) as mock_exists:
with mock.patch.object(
output_cacher, "files_match", return_value=True
) as mock_diff:
with mock.patch.object(Path, "rename") as mock_move:
with mock.patch.object(Path, "unlink") as mock_remove:
with mock.patch.object(
output_cacher, "ensure_file_exists"
) as mock_exists:
moved = output_cacher.move_if_different(
Path("source.txt"), Path("dest.txt")
)
self.assertFalse(moved)
mock_exists.assert_called()
mock_diff.assert_called()
mock_move.assert_not_called()
mock_remove.assert_called_with()
class SubstituteCommandTest(unittest.TestCase):
def test_no_substitutions(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(command=["run.sh"], substitutions={})
repl, renamed = action.substitute_command(transform)
self.assertEqual(repl, ["run.sh"])
self.assertEqual(renamed, {})
def test_one_suffix(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "out.txt"], substitutions={"out.txt": ""}
)
repl, renamed = action.substitute_command(transform)
self.assertEqual(repl, ["run.sh", "out.txt.tmp"])
self.assertEqual(renamed, {Path("out.txt"): Path("out.txt.tmp")})
def test_require_flag_match_missing_flag(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "out.txt"], substitutions={"out.txt": "-f"}
)
repl, renamed = action.substitute_command(transform)
self.assertEqual(repl, ["run.sh", "out.txt"])
self.assertEqual(renamed, {})
def test_require_flag_match_wrong_flag(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "-g", "out.txt"], substitutions={"out.txt": "-f"}
)
repl, renamed = action.substitute_command(transform)
self.assertEqual(repl, ["run.sh", "-g", "out.txt"])
self.assertEqual(renamed, {})
def test_require_flag_match_have_flag(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "-f", "out.txt"], substitutions={"out.txt": "-f"}
)
repl, renamed = action.substitute_command(transform)
self.assertEqual(repl, ["run.sh", "-f", "out.txt.tmp"])
self.assertEqual(renamed, {Path("out.txt"): Path("out.txt.tmp")})
def test_match_only_one_flag(self):
transform = output_cacher.TempFileTransform(basename_prefix="tmp-")
action = output_cacher.Action(
command=["run.sh", "-x", "out.txt", "-f", "out.txt"],
substitutions={"out.txt": "-f"},
)
repl, renamed = action.substitute_command(transform)
self.assertEqual(repl, ["run.sh", "-x", "out.txt", "-f", "tmp-out.txt"])
self.assertEqual(renamed, {Path("out.txt"): Path("tmp-out.txt")})
def test_match_only_one_flag_first_occurrence(self):
transform = output_cacher.TempFileTransform(basename_prefix="tmp-")
action = output_cacher.Action(
command=["run.sh", "out.txt", "-f", "out.txt", "out.txt"],
substitutions={"out.txt": "-f"},
)
repl, renamed = action.substitute_command(transform)
self.assertEqual(
repl, ["run.sh", "out.txt", "-f", "tmp-out.txt", "out.txt"]
)
self.assertEqual(renamed, {Path("out.txt"): Path("tmp-out.txt")})
class ReplaceOutputArgsTest(unittest.TestCase):
def test_dry_run(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(command=["run.sh"], substitutions={})
with mock.patch.object(
output_cacher, "execute_main_command"
) as mock_call:
self.assertEqual(action.run_cached(transform, dry_run=True), 0)
mock_call.assert_not_called()
def test_command_failed(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(command=["run.sh"], substitutions={})
with mock.patch.object(
output_cacher, "execute_main_command", return_value=1
) as mock_call:
with mock.patch.object(
output_cacher, "move_if_different"
) as mock_update:
self.assertEqual(action.run_cached(transform), 1)
mock_call.assert_called_with(["run.sh"])
mock_update.assert_not_called()
def test_command_passed_using_suffix(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "in.put", "out.put"],
substitutions={"out.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
# This test doesn't care if move happened.
with mock.patch.object(
output_cacher, "move_if_different", return_value=False
) as mock_update:
self.assertEqual(action.run_cached(transform), 0)
mock_call.assert_called_with(["run.sh", "in.put", "out.put.tmp"])
mock_update.assert_called_with(
src=Path("out.put.tmp"), dest=Path("out.put"), verbose=False
)
def test_command_passed_using_relative_temp_dir(self):
transform = output_cacher.TempFileTransform(temp_dir="temp/temp")
action = output_cacher.Action(
command=["run.sh", "in.put", "foo/out.put"],
substitutions={"foo/out.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
# This test doesn't care if move happened.
with mock.patch.object(
output_cacher, "move_if_different", return_value=False
) as mock_update:
with mock.patch.object(Path, "mkdir") as mock_mkdir:
self.assertEqual(action.run_cached(transform), 0)
mock_mkdir.assert_called_with(parents=True, exist_ok=True)
mock_call.assert_called_with(
["run.sh", "in.put", "temp/temp/foo/out.put"]
)
mock_update.assert_called_with(
src=Path("temp/temp/foo/out.put"),
dest=Path("foo/out.put"),
verbose=False,
)
def fake_detail_diff(left: Path, right: Path):
# Don't print anything, or attempt to call a diff script
pass
class RunRepeatedlyCompareTests(unittest.TestCase):
def test_command_failed(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(command=["run.sh"], substitutions={})
with mock.patch.object(
output_cacher, "execute_main_command", return_value=1
) as mock_call:
with mock.patch.object(output_cacher, "files_match") as mock_match:
with mock.patch.object(
os.path, "exists", return_value=True
) as mock_exists:
with mock.patch.object(shutil, "copy2") as mock_copy:
with mock.patch.object(os, "makedirs") as mock_mkdir:
with mock.patch.object(time, "sleep") as mock_zzz:
self.assertEqual(
action.run_repeatedly_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
),
1,
)
mock_call.assert_called_once_with(["run.sh"])
mock_match.assert_not_called()
mock_exists.assert_not_called()
mock_mkdir.assert_not_called()
mock_copy.assert_not_called()
def test_command_passed_and_rerun_matches(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "in.put", "out.put"],
substitutions={"out.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
with mock.patch.object(
output_cacher, "files_match", return_value=True
) as mock_match:
with mock.patch.object(
Path, "is_file", return_value=True
) as mock_isfile:
with mock.patch.object(
output_cacher, "remove_if_exists"
) as mock_remove:
with mock.patch.object(Path, "mkdir") as mock_mkdir:
with mock.patch.object(
shutil, "copy2"
) as mock_copy:
with mock.patch.object(
time, "sleep"
) as mock_zzz:
self.assertEqual(
action.run_repeatedly_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
max_attempts=2,
),
0,
)
mock_call.assert_has_calls(
[
mock.call(["run.sh", "in.put", "out.put"]), # first run
mock.call(["run.sh", "in.put", "out.put"]), # re-run 1
mock.call(["run.sh", "in.put", "out.put"]), # re-run 2
],
any_order=True,
)
mock_match.assert_called_with(Path("out.put"), Path("out.put.tmp"))
mock_remove.assert_called_with(Path("out.put.tmp"))
mock_isfile.assert_called()
mock_mkdir.assert_not_called() # using suffix, not temp_dir
mock_copy.assert_called_once_with(
Path("out.put"), Path("out.put.tmp"), follow_symlinks=False
)
def test_command_passed_and_rerun_differs(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "in.put", "out.put"],
substitutions={"out.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
with mock.patch.object(
output_cacher,
"files_match",
# mismatch on 2nd attempt
side_effect=[True, False],
) as mock_match:
with mock.patch.object(
Path, "is_file", return_value=True
) as mock_isfile:
with mock.patch.object(
output_cacher, "remove_if_exists"
) as mock_remove:
with mock.patch.object(Path, "mkdir") as mock_mkdir:
with mock.patch.object(
shutil, "copy2"
) as mock_copy:
with mock.patch.object(
time, "sleep"
) as mock_zzz:
self.assertEqual(
action.run_repeatedly_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
max_attempts=10,
),
1,
)
mock_call.assert_has_calls(
[
mock.call(["run.sh", "in.put", "out.put"]),
mock.call(["run.sh", "in.put", "out.put"]),
mock.call(["run.sh", "in.put", "out.put"]),
# Stop after first miscomparison, before max_attempts reached.
],
any_order=True,
)
mock_match.assert_called_with(Path("out.put"), Path("out.put.tmp"))
mock_remove.assert_not_called() # mismatches kept
mock_isfile.assert_called()
mock_mkdir.assert_not_called() # using suffix, not temp_dir
mock_copy.assert_called_once_with(
Path("out.put"), Path("out.put.tmp"), follow_symlinks=False
)
def test_command_passed_and_some_outputs_differ(self):
def fake_match(file1: Path, file2: Path) -> bool:
if file1 == Path("out.put"):
return True
elif file1 == Path("out2.put"):
return False
raise ValueError(f"Unhandled file name: {file1}")
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "in.put", "out.put", "out2.put"],
substitutions={"out.put": "", "out2.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
with mock.patch.object(
output_cacher, "files_match", wraps=fake_match
) as mock_match:
with mock.patch.object(
Path, "is_file", return_value=True
) as mock_isfile:
with mock.patch.object(
output_cacher, "remove_if_exists"
) as mock_remove:
with mock.patch.object(Path, "mkdir") as mock_mkdir:
with mock.patch.object(
shutil, "copy2"
) as mock_copy:
with mock.patch.object(
time, "sleep"
) as mock_zzz:
self.assertEqual(
action.run_repeatedly_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
),
1,
)
mock_call.assert_has_calls(
[
mock.call(["run.sh", "in.put", "out.put", "out2.put"]),
mock.call(["run.sh", "in.put", "out.put", "out2.put"]),
],
any_order=True,
)
mock_match.assert_has_calls(
[
mock.call(Path("out.put"), Path("out.put.tmp")),
mock.call(Path("out2.put"), Path("out2.put.tmp")),
],
any_order=True,
)
mock_remove.assert_not_called() # keep around mismatches
mock_isfile.assert_called()
mock_mkdir.assert_not_called() # using suffix, not temp_dir
mock_copy.assert_has_calls(
[
mock.call(
Path("out.put"), Path("out.put.tmp"), follow_symlinks=False
),
mock.call(
Path("out2.put"),
Path("out2.put.tmp"),
follow_symlinks=False,
),
],
any_order=True,
)
class RunTwiceWithSubstitutionCompareTests(unittest.TestCase):
def test_command_failed(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(command=["run.sh"], substitutions={})
with mock.patch.object(
output_cacher, "execute_main_command", return_value=1
) as mock_call:
with mock.patch.object(output_cacher, "files_match") as mock_match:
self.assertEqual(
action.run_twice_with_substitution_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
),
1,
)
mock_call.assert_called_once_with(["run.sh"])
mock_match.assert_not_called()
def test_command_passed_and_rerun_matches(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "in.put", "out.put"],
substitutions={"out.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
with mock.patch.object(
output_cacher, "files_match", return_value=True
) as mock_match:
with mock.patch.object(Path, "unlink") as mock_remove:
self.assertEqual(
action.run_twice_with_substitution_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
),
0,
)
mock_call.assert_has_calls(
[
mock.call(["run.sh", "in.put", "out.put"]),
mock.call(["run.sh", "in.put", "out.put.tmp"]),
],
any_order=True,
)
mock_match.assert_called_with(Path("out.put"), Path("out.put.tmp"))
mock_remove.assert_called_with()
def test_command_passed_and_rerun_differs(self):
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "in.put", "out.put"],
substitutions={"out.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
with mock.patch.object(
output_cacher, "files_match", return_value=False
) as mock_match:
with mock.patch.object(os, "remove") as mock_remove:
self.assertEqual(
action.run_twice_with_substitution_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
),
1,
)
mock_call.assert_has_calls(
[
mock.call(["run.sh", "in.put", "out.put"]),
mock.call(["run.sh", "in.put", "out.put.tmp"]),
],
any_order=True,
)
mock_match.assert_called_with(Path("out.put"), Path("out.put.tmp"))
mock_remove.assert_not_called()
def test_command_passed_and_some_outptus_differ(self):
def fake_match(file1: Path, file2: Path) -> bool:
if file1 == Path("out.put"):
return True
elif file1 == Path("out2.put"):
return False
raise ValueError(f"Unhandled file name: {file1}")
transform = output_cacher.TempFileTransform(suffix=".tmp")
action = output_cacher.Action(
command=["run.sh", "in.put", "out.put", "out2.put"],
substitutions={"out.put": "", "out2.put": ""},
)
with mock.patch.object(
output_cacher, "execute_main_command", return_value=0
) as mock_call:
with mock.patch.object(
output_cacher, "files_match", wraps=fake_match
) as mock_match:
with mock.patch.object(Path, "unlink") as mock_remove:
self.assertEqual(
action.run_twice_with_substitution_and_compare_outputs(
tempfile_transform=transform,
diff_action=fake_detail_diff,
),
1,
)
mock_call.assert_has_calls(
[
mock.call(["run.sh", "in.put", "out.put", "out2.put"]),
mock.call(["run.sh", "in.put", "out.put.tmp", "out2.put.tmp"]),
],
any_order=True,
)
mock_match.assert_has_calls(
[
mock.call(Path("out.put"), Path("out.put.tmp")),
mock.call(Path("out2.put"), Path("out2.put.tmp")),
],
any_order=True,
)
mock_remove.assert_has_calls([mock.call()])
if __name__ == "__main__":
unittest.main()