blob: 759ab417192389db11e774b6d4033b11e48c7553 [file] [log] [blame] [edit]
# Copyright 2024 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 os
import sys
import tempfile
import typing as T
import unittest
from pathlib import Path
sys.path.insert(0, Path(__file__).parent)
import ninja_artifacts
class MockNinjaRunner(object):
def __init__(self, mock_output: str) -> None:
self.command_args: T.Sequence[str] = []
self.command_build_dir = ""
self._mock_output = mock_output
def run_and_extract_output(
self, build_dir: str, cmd_args: T.Sequence[str]
) -> str:
self.command_build_dir = build_dir
self.command_args = cmd_args
return self._mock_output
class NinjaArtifactsTest(unittest.TestCase):
def test_get_last_build_targets(self):
with tempfile.TemporaryDirectory() as temp_dir:
build_dir = Path(temp_dir)
# If the file doesn't exist, default to [":default"].
self.assertListEqual(
ninja_artifacts.get_last_build_targets(build_dir), [":default"]
)
# If the file is empty, default to [":default"] too.
(build_dir / ninja_artifacts.LAST_NINJA_TARGETS_FILE).write_text("")
self.assertListEqual(
ninja_artifacts.get_last_build_targets(build_dir), [":default"]
)
(build_dir / ninja_artifacts.LAST_NINJA_TARGETS_FILE).write_text(
" foo"
)
self.assertListEqual(
ninja_artifacts.get_last_build_targets(build_dir), ["foo"]
)
(build_dir / ninja_artifacts.LAST_NINJA_TARGETS_FILE).write_text(
"foo bar"
)
self.assertListEqual(
ninja_artifacts.get_last_build_targets(build_dir),
["foo", "bar"],
)
def test_get_build_plan_deps(self):
with tempfile.TemporaryDirectory() as temp_dir:
build_dir = Path(temp_dir)
(build_dir / ninja_artifacts.NINJA_BUILD_PLAN_DEPS_FILE).write_text(
"build.ninja.stamp: dep1 dep2 dep3 dep4\n"
)
self.assertListEqual(
ninja_artifacts.get_build_plan_deps(build_dir),
["dep1", "dep2", "dep3", "dep4"],
)
def test_check_output_needs_update(self):
with tempfile.TemporaryDirectory() as temp_dir:
build_dir = Path(temp_dir)
input_files = [build_dir / "input1", build_dir / "input2"]
output_file = build_dir / "output"
# output file does not exist, nor any input file.
self.assertTrue(
ninja_artifacts.check_output_needs_update(
output_file, input_files
)
)
# output file does not exist, but input files do.
input_files[0].write_text("one")
input_files[1].write_text("two")
self.assertTrue(
ninja_artifacts.check_output_needs_update(
output_file, input_files
)
)
# output file does exist, and is newer than inputs.
output_file.write_text("out")
self.assertFalse(
ninja_artifacts.check_output_needs_update(
output_file, input_files
)
)
# output file does exist, but is older than one input.
output_stat = output_file.stat()
os.utime(
input_files[1],
times=(output_stat.st_atime, output_stat.st_mtime + 1),
)
self.assertTrue(
ninja_artifacts.check_output_needs_update(
output_file, input_files
)
)
def test_get_last_build_artifacts(self):
with tempfile.TemporaryDirectory() as temp_dir:
# Setup fake source and build directory.
build_gn_path = Path(temp_dir) / "BUILD.gn"
build_gn_path.write_text("# Fake BUILD.gn\n")
build_dir = Path(temp_dir) / "out"
build_dir.mkdir(parents=True)
build_ninja_d_path = (
build_dir / ninja_artifacts.NINJA_BUILD_PLAN_DEPS_FILE
)
build_ninja_d_path.write_text("build.ninja.stamp: ../BUILD.gn")
last_targets_path = (
build_dir / ninja_artifacts.LAST_NINJA_TARGETS_FILE
)
last_targets_path.write_text("foo")
# Create mock NinjaRunner instance to avoid calling Ninja binary.
ninja_runner = MockNinjaRunner("bar\nfoo\nzoo\n'quoted'\n")
self.assertListEqual(
ninja_artifacts.get_last_build_artifacts(
build_dir, ninja_runner
),
["bar", "foo", "zoo", "'quoted'"],
)
self.assertEqual(ninja_runner.command_build_dir, str(build_dir))
self.assertListEqual(
ninja_runner.command_args, ["-t", "outputs", "foo"]
)
last_ninja_artifacts_path = (
build_dir / ninja_artifacts.LAST_NINJA_ARTIFACTS_FILE
)
self.assertTrue(last_ninja_artifacts_path.exists())
self.assertEqual(
last_ninja_artifacts_path.read_text(), "bar\nfoo\nzoo\n'quoted'"
)
# Modify last_ninja_build_targets.txt and verify the cache was regenerated.
last_targets_path.write_text("bar zoo")
last_targets_stat = last_targets_path.stat()
os.utime(
last_targets_path,
times=(
last_targets_stat.st_atime,
last_targets_stat.st_mtime + 1,
),
)
ninja_runner = MockNinjaRunner("second\ncall\n")
self.assertListEqual(
ninja_artifacts.get_last_build_artifacts(
build_dir, ninja_runner
),
["second", "call"],
)
self.assertEqual(ninja_runner.command_build_dir, str(build_dir))
self.assertListEqual(
ninja_runner.command_args, ["-t", "outputs", "bar", "zoo"]
)
self.assertEqual(
last_ninja_artifacts_path.read_text(), "second\ncall"
)
def test_get_last_build_sources(self):
with tempfile.TemporaryDirectory() as temp_dir:
# Setup fake source and build directory.
build_gn_path = Path(temp_dir) / "BUILD.gn"
build_gn_path.write_text("# Fake BUILD.gn\n")
build_dir = Path(temp_dir) / "out"
build_dir.mkdir(parents=True)
build_ninja_d_path = (
build_dir / ninja_artifacts.NINJA_BUILD_PLAN_DEPS_FILE
)
build_ninja_d_path.write_text("build.ninja.stamp: ../BUILD.gn")
last_targets_path = (
build_dir / ninja_artifacts.LAST_NINJA_TARGETS_FILE
)
last_targets_path.write_text("foo")
# Create mock NinjaRunner instance to avoid calling Ninja binary.
ninja_runner = MockNinjaRunner(
"../src/foo\n../src/bar\noutput_file\nout_dir/out_file\n../src/zoo\n"
)
self.assertListEqual(
ninja_artifacts.get_last_build_sources(build_dir, ninja_runner),
["../src/foo", "../src/bar", "../src/zoo"],
)
self.assertEqual(ninja_runner.command_build_dir, str(build_dir))
self.assertListEqual(
ninja_runner.command_args,
[
"-t",
"inputs",
"--no-shell-escape",
"--dependency-order",
"foo",
],
)
last_ninja_sources_path = (
build_dir / ninja_artifacts.LAST_NINJA_SOURCES_FILE
)
self.assertTrue(last_ninja_sources_path.exists())
self.assertEqual(
last_ninja_sources_path.read_text(),
"../src/foo\n../src/bar\n../src/zoo",
)
# Modify last_ninja_build_targets.txt and verify the cache was regenerated.
last_targets_path.write_text("bar zoo")
last_targets_stat = last_targets_path.stat()
os.utime(
last_targets_path,
times=(
last_targets_stat.st_atime,
last_targets_stat.st_mtime + 1,
),
)
ninja_runner = MockNinjaRunner("../second\n../call\n")
self.assertListEqual(
ninja_artifacts.get_last_build_sources(build_dir, ninja_runner),
["../second", "../call"],
)
self.assertEqual(ninja_runner.command_build_dir, str(build_dir))
self.assertListEqual(
ninja_runner.command_args,
[
"-t",
"inputs",
"--no-shell-escape",
"--dependency-order",
"bar",
"zoo",
],
)
self.assertEqual(
last_ninja_sources_path.read_text(), "../second\n../call"
)
if __name__ == "__main__":
unittest.main()