chromium/build/clobber_unittest.py

#!/usr/bin/env python3
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
import pathlib
import shutil
import sys
import tempfile
import textwrap
import unittest
from unittest import mock

import clobber


class TestExtractBuildCommand(unittest.TestCase):
  def setUp(self):
    self.build_ninja_file, self.build_ninja_path = tempfile.mkstemp(text=True)

  def tearDown(self):
    os.close(self.build_ninja_file)
    os.remove(self.build_ninja_path)

  def test_normal_extraction(self):
    build_ninja_file_contents = textwrap.dedent("""
        ninja_required_version = 1.7.2

        rule gn
          command = ../../buildtools/gn --root=../.. -q --regeneration gen .
          pool = console
          description = Regenerating ninja files

        build build.ninja.stamp: gn
          generator = 1
          depfile = build.ninja.d

        build build.ninja: phony build.ninja.stamp
          generator = 1

        pool build_toolchain_action_pool
          depth = 72

        pool build_toolchain_link_pool
          depth = 23

        subninja toolchain.ninja
        subninja clang_newlib_x64/toolchain.ninja
        subninja glibc_x64/toolchain.ninja
        subninja irt_x64/toolchain.ninja
        subninja nacl_bootstrap_x64/toolchain.ninja
        subninja newlib_pnacl/toolchain.ninja

        build blink_python_tests: phony obj/blink_python_tests.stamp
        build blink_tests: phony obj/blink_tests.stamp

        default all
    """)  # Based off of a standard linux build dir.
    with open(self.build_ninja_path, 'w') as f:
      f.write(build_ninja_file_contents)

    expected_build_ninja_file_contents = textwrap.dedent("""
        ninja_required_version = 1.7.2

        rule gn
          command = ../../buildtools/gn --root=../.. -q --regeneration gen .
          pool = console
          description = Regenerating ninja files

        build build.ninja.stamp: gn
          generator = 1
          depfile = build.ninja.d

        build build.ninja: phony build.ninja.stamp
          generator = 1

    """)

    self.assertEqual(clobber.extract_gn_build_commands(self.build_ninja_path),
                     expected_build_ninja_file_contents)

  def test_unexpected_format(self):
    # No "build build.ninja:" line should make it return an empty string.
    build_ninja_file_contents = textwrap.dedent("""
        ninja_required_version = 1.7.2

        rule gn
          command = ../../buildtools/gn --root=../.. -q --regeneration gen .
          pool = console
          description = Regenerating ninja files

        subninja toolchain.ninja

        build blink_python_tests: phony obj/blink_python_tests.stamp
        build blink_tests: phony obj/blink_tests.stamp

    """)
    with open(self.build_ninja_path, 'w') as f:
      f.write(build_ninja_file_contents)

    self.assertEqual(clobber.extract_gn_build_commands(self.build_ninja_path),
                     '')


class TestDelete(unittest.TestCase):
  def setUp(self):
    self.build_dir = tempfile.mkdtemp()

    pathlib.Path(os.path.join(self.build_dir, 'build.ninja')).touch()
    pathlib.Path(os.path.join(self.build_dir, 'build.ninja.d')).touch()

  def tearDown(self):
    shutil.rmtree(self.build_dir)

  def test_delete_build_dir_full(self):
    # Create a dummy file in the build dir and ensure it gets removed.
    dummy_file = os.path.join(self.build_dir, 'dummy')
    pathlib.Path(dummy_file).touch()

    clobber.delete_build_dir(self.build_dir)

    self.assertFalse(os.path.exists(dummy_file))

  def test_delete_build_dir_fail(self):
    # Make delete_dir() throw to ensure it's handled gracefully.

    with mock.patch('clobber._clean_dir', side_effect=OSError):
      with self.assertRaises(OSError):
        clobber.delete_build_dir(self.build_dir)

  @unittest.skipIf(sys.platform == 'win32', 'Symlinks are not allowed on Windows by default')
  def test_delete_build_dir_link(self):
    with tempfile.TemporaryDirectory() as tmpdir:
      # create a symlink.
      build_dir = os.path.join(tmpdir, 'link')
      os.symlink(self.build_dir, build_dir)

      # create a dummy file.
      dummy_file = os.path.join(build_dir, 'dummy')
      pathlib.Path(dummy_file).touch()
      clobber.delete_build_dir(build_dir)

      self.assertFalse(os.path.exists(dummy_file))


if __name__ == '__main__':
  unittest.main()