blob: 9bcaa1d67b276cc2cb276ee3bdeaa150bd483029 [file] [log] [blame] [edit]
#!/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()