def test_have_sources(self):
     action = action_tracer.Action(script="script.sh",
                                   sources=["input.src", "main.h"])
     self.assertEqual(
         action.access_constraints(),
         action_tracer.AccessConstraints(
             allowed_reads=abspaths({"script.sh", "input.src", "main.h"})))
    def test_links_are_followed(self):

        def fake_realpath(s: str) -> str:
            return f'test/realpath/{s}'

        action = action_tracer.Action(inputs=["script.sh"], depfile="foo.d")
        with mock.patch.object(os.path, 'exists',
                               return_value=True) as mock_exists:
            with mock.patch("builtins.open", mock.mock_open(
                    read_data="foo.o: foo.cc foo.h\n")) as mock_file:
                with mock.patch.object(os.path, 'realpath',
                                       wraps=fake_realpath) as mock_realpath:
                    constraints = action.access_constraints(
                        writeable_depfile_inputs=False)
        mock_exists.assert_called_once()
        mock_file.assert_called_once()
        mock_realpath.assert_called()

        self.assertEqual(
            constraints,
            action_tracer.AccessConstraints(
                allowed_reads=abspaths(
                    {
                        "test/realpath/script.sh",
                        "test/realpath/foo.d",
                        "test/realpath/foo.o",
                        "test/realpath/foo.cc",
                        "test/realpath/foo.h",
                    }),
                allowed_writes=abspaths({"foo.d", "foo.o"})))
    def test_fresh_output_with_used_input(self):

        def fake_getctime(path: str):
            if path.startswith("read"):
                return 100
            if path.startswith("write"):
                return 200
            return 0

        used_input = "read.me"
        written_output = "write.me"
        with mock.patch.object(os.path, 'exists',
                               return_value=True) as mock_exists:
            with mock.patch.object(os.path, 'getctime',
                                   wraps=fake_getctime) as mock_ctime:
                output_diagnostics = action_tracer.diagnose_stale_outputs(
                    accesses=[
                        action_tracer.Read(used_input),
                        action_tracer.Write(written_output),
                    ],
                    access_constraints=action_tracer.AccessConstraints(
                        allowed_reads={used_input},
                        required_writes={written_output}),
                )
        # There are no untouched outputs, so getctime is never called.
        mock_exists.assert_not_called()
        mock_ctime.assert_not_called()
        self.assertEqual(
            output_diagnostics,
            action_tracer.StalenessDiagnostics(
                required_writes={written_output},
                # newest_input is not evaluated
                stale_outputs=set(),
            ),
        )
 def test_have_response_file(self):
     action = action_tracer.Action(script="script.sh",
                                   response_file_name="response.out")
     self.assertEqual(
         action.access_constraints(),
         action_tracer.AccessConstraints(
             allowed_reads=abspaths({"script.sh", "response.out"})))
Beispiel #5
0
    def test_stale_output_with_used_input(self):
        def fake_read_ctime(path: str):
            if path.startswith("read"):
                return 200
            raise ValueError(f'Unexpected path: {path}')

        def fake_write_ctime(path: str):
            if path.startswith("write"):
                return 100
            raise ValueError(f'Unexpected path: {path}')

        used_input = "read.me"
        required_writes = {"write.me"}
        with mock.patch.object(os.path, 'exists',
                               return_value=True) as mock_exists:
            with mock.patch.object(os.path, 'getctime',
                                   wraps=fake_read_ctime) as mock_read_ctime:
                with mock.patch.object(
                        action_tracer, 'realpath_ctime',
                        wraps=fake_write_ctime) as mock_write_ctime:
                    output_diagnostics = action_tracer.diagnose_stale_outputs(
                        accesses=[action_tracer.Read(used_input)],
                        access_constraints=action_tracer.AccessConstraints(
                            allowed_reads={used_input},
                            required_writes=required_writes),
                    )
        mock_exists.assert_called_once()
        mock_read_ctime.assert_called()
        mock_write_ctime.assert_called()
        self.assertEqual(
            output_diagnostics,
            action_tracer.StalenessDiagnostics(required_writes=required_writes,
                                               newest_input=used_input,
                                               stale_outputs={"write.me"}),
        )
Beispiel #6
0
 def test_no_accesses(self):
     self.assertEqual(
         action_tracer.check_access_permissions(
             action_tracer.FSAccessSet(),
             action_tracer.AccessConstraints()),
         action_tracer.FSAccessSet(),
     )
 def test_have_inputs(self):
     action = action_tracer.Action(
         inputs=["script.sh", "input.txt", "main.cc"])
     self.assertEqual(
         action.access_constraints(),
         action_tracer.AccessConstraints(
             allowed_reads=abspaths({"script.sh", "input.txt", "main.cc"})))
 def test_ok_write(self):
     self.assertEqual(
         action_tracer.check_access_permissions(
             action_tracer.FSAccessSet(writes={"writeable.txt"}),
             action_tracer.AccessConstraints(
                 allowed_writes={"writeable.txt"})),
         action_tracer.FSAccessSet(),
     )
 def test_forbidden_read(self):
     read = "unreadable.txt"
     self.assertEqual(
         action_tracer.check_access_permissions(
             action_tracer.FSAccessSet(reads={read}),
             action_tracer.AccessConstraints()),
         action_tracer.FSAccessSet(reads={read}),
     )
Beispiel #10
0
 def test_ok_read(self):
     self.assertEqual(
         action_tracer.check_access_permissions(
             action_tracer.FSAccessSet(reads={"readable.txt"}),
             action_tracer.AccessConstraints(
                 allowed_reads={"readable.txt"})),
         action_tracer.FSAccessSet(),
     )
 def test_have_outputs(self):
     action = action_tracer.Action(script="script.sh", outputs=["main.o"])
     self.assertEqual(
         action.access_constraints(),
         action_tracer.AccessConstraints(
             allowed_reads=abspaths({"script.sh", "main.o"}),
             allowed_writes=abspaths({"main.o"}),
             required_writes=abspaths({"main.o"})))
Beispiel #12
0
 def test_no_accesses_no_constraints(self):
     output_diagnostics = action_tracer.diagnose_stale_outputs(
         accesses=[],
         access_constraints=action_tracer.AccessConstraints(),
     )
     self.assertEqual(
         output_diagnostics,
         action_tracer.StalenessDiagnostics(),
     )
Beispiel #13
0
 def test_have_nonexistent_depfile(self):
     action = action_tracer.Action(depfile="foo.d")
     with mock.patch.object(os.path, 'exists',
                            return_value=False) as mock_exists:
         constraints = action.access_constraints()
     mock_exists.assert_called()
     self.assertEqual(
         constraints,
         action_tracer.AccessConstraints(allowed_writes=abspaths({"foo.d"}),
                                         allowed_reads=abspaths({"foo.d"})))
Beispiel #14
0
 def test_forbidden_writes(self):
     # make sure multiple violations accumulate
     bad_writes = {
         "unwriteable.txt",
         "you-shall-not-pass.txt",
     }
     self.assertEqual(
         action_tracer.check_access_permissions(
             action_tracer.FSAccessSet(writes=bad_writes),
             action_tracer.AccessConstraints()),
         action_tracer.FSAccessSet(writes=bad_writes),
     )
Beispiel #15
0
    def test_no_inputs(self):
        access_constraints = action_tracer.AccessConstraints(
            allowed_reads={"input"}, required_writes={"output"})
        with mock.patch.object(os.path, 'exists',
                               return_value=False) as mock_exists:
            with mock.patch.object(os.path, 'getctime',
                                   return_value=0) as mock_ctime:
                fresh_outputs = access_constraints.fresh_outputs()

        mock_exists.assert_called()
        mock_ctime.assert_not_called()
        self.assertEqual(fresh_outputs, set())
Beispiel #16
0
 def test_missing_write_no_inputs(self):
     required_writes = {"write.me"}
     output_diagnostics = action_tracer.diagnose_stale_outputs(
         accesses=[],
         access_constraints=action_tracer.AccessConstraints(
             required_writes=required_writes),
     )
     self.assertEqual(
         output_diagnostics,
         action_tracer.StalenessDiagnostics(
             required_writes=required_writes,
             nonexistent_outputs={"write.me"}),
     )
Beispiel #17
0
 def test_read_from_temporary_writes_ok(self):
     temp_file = "__file.tmp"
     reads = {temp_file}
     writes = {
         "unwriteable.txt",
         temp_file,
     }
     self.assertEqual(
         action_tracer.check_access_permissions(
             action_tracer.FSAccessSet(reads=reads, writes=writes),
             action_tracer.AccessConstraints()),
         action_tracer.FSAccessSet(reads=set(), writes=writes),
     )
Beispiel #18
0
 def test_stale_output_no_inputs(self):
     required_writes = {"write.me"}
     with mock.patch.object(os.path, 'exists',
                            return_value=True) as mock_exists:
         output_diagnostics = action_tracer.diagnose_stale_outputs(
             accesses=[],
             access_constraints=action_tracer.AccessConstraints(
                 required_writes=required_writes),
         )
     mock_exists.assert_called_once()
     self.assertEqual(
         output_diagnostics,
         action_tracer.StalenessDiagnostics(required_writes=required_writes),
     )
Beispiel #19
0
 def test_missing_write_with_used_input(self):
     used_input = "read.me"
     required_writes = {"write.me"}
     output_diagnostics = action_tracer.diagnose_stale_outputs(
         accesses=[action_tracer.Read(used_input)],
         access_constraints=action_tracer.AccessConstraints(
             allowed_reads={used_input},
             required_writes=required_writes,
         ),
     )
     self.assertEqual(
         output_diagnostics,
         action_tracer.StalenessDiagnostics(
             required_writes=required_writes,
             nonexistent_outputs={"write.me"}),
     )
    def test_have_depfile(self):
        action = action_tracer.Action(script="script.sh", depfile="foo.d")
        with mock.patch.object(os.path, 'exists',
                               return_value=True) as mock_exists:
            with mock.patch(
                    "builtins.open",
                    mock.mock_open(
                        read_data="foo.o: foo.cc foo.h\n")) as mock_file:
                constraints = action.access_constraints()

        self.assertEqual(
            constraints,
            action_tracer.AccessConstraints(
                allowed_reads=abspaths(
                    {"script.sh", "foo.d", "foo.o", "foo.cc", "foo.h"}),
                allowed_writes=abspaths({"foo.d", "foo.o", "foo.cc",
                                         "foo.h"})))
Beispiel #21
0
    def test_stale_output_with_multiple_used_inputs(self):

        def fake_read_ctime(path: str):
            if path == "read.me":
                return 200
            if path == "read.me.newer":
                return 300
            raise Exception(f'fake_read_ctime for unexpected path: {path}')

        def fake_write_ctime(path: str):
            if path.startswith("write"):
                return 250
            raise Exception(f'fake_write_ctime for unexpected path: {path}')

        used_input = "read.me"
        # Make sure the timestamp of the newest input is used for comparison.
        used_input_newer = "read.me.newer"
        required_writes = {"write.me"}
        with mock.patch.object(os.path, 'exists',
                               return_value=True) as mock_exists:
            with mock.patch.object(os.path, 'getctime',
                                   wraps=fake_read_ctime) as mock_read_ctime:
                with mock.patch.object(
                        action_tracer, 'realpath_ctime',
                        wraps=fake_write_ctime) as mock_write_ctime:
                    output_diagnostics = action_tracer.diagnose_stale_outputs(
                        accesses=[
                            action_tracer.Read(used_input),
                            action_tracer.Read(used_input_newer),
                        ],
                        access_constraints=action_tracer.AccessConstraints(
                            allowed_reads={used_input, used_input_newer},
                            required_writes=required_writes),
                    )
        mock_exists.assert_called_once()
        mock_read_ctime.assert_called()
        mock_write_ctime.assert_called()
        self.assertEqual(
            output_diagnostics,
            action_tracer.StalenessDiagnostics(
                required_writes=required_writes,
                # newer input is used for comparison
                newest_input=used_input_newer,
                stale_outputs={"write.me"}),
        )
Beispiel #22
0
    def test_readable_output_is_fresh(self):
        def fake_getctime(path: str):
            if path == "input":
                return 200
            if path == "output":
                return 300
            raise ValueError(f'Unexpected path: {path}')

        access_constraints = action_tracer.AccessConstraints(
            allowed_reads={"input", "output"}, required_writes={"output"})
        with mock.patch.object(os.path, 'exists',
                               return_value=True) as mock_exists:
            with mock.patch.object(action_tracer,
                                   'realpath_ctime',
                                   wraps=fake_getctime) as mock_ctime:
                fresh_outputs = access_constraints.fresh_outputs()

        mock_exists.assert_called()
        mock_ctime.assert_called()
        self.assertEqual(fresh_outputs, {"output"})
 def test_empty_action(self):
     action = action_tracer.Action(script="script.sh")
     self.assertEqual(
         action.access_constraints(),
         action_tracer.AccessConstraints(
             allowed_reads=abspaths({"script.sh"})))