chromium/tools/utr/builders_test.py

#!/usr/bin/env vpython3
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for builders.py"""

import json
import os
import pathlib
import shutil
import tempfile
import unittest
from unittest import mock

import builders


class BuilderPropsTests(unittest.TestCase):

  def setUp(self):
    self.tmp_dir = pathlib.Path(tempfile.mkdtemp())
    self.tmp_internal_dir = pathlib.Path(tempfile.mkdtemp())

    patch_props_dir = mock.patch('builders._BUILDER_PROP_DIRS', self.tmp_dir)
    patch_props_dir.start()
    self.addCleanup(patch_props_dir.stop)

    patch_internal_props_dir = mock.patch(
        'builders._INTERNAL_BUILDER_PROP_DIRS', self.tmp_internal_dir)
    patch_internal_props_dir.start()
    self.addCleanup(patch_internal_props_dir.stop)

  def tearDown(self):
    shutil.rmtree(self.tmp_dir)
    if self.tmp_internal_dir.exists():
      shutil.rmtree(self.tmp_internal_dir)

  def testNoProps(self):
    # Empty base dir
    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket')
    self.assertIsNone(props)

    # Empty bucket dir
    os.makedirs(self.tmp_dir.joinpath('some-bucket'))
    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket')
    self.assertIsNone(props)

    # Empty builder dir
    os.makedirs(self.tmp_dir.joinpath('some-bucket', 'some-builder'))
    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket')
    self.assertIsNone(props)

    # No src-internal checkout.
    shutil.rmtree(self.tmp_internal_dir)
    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket')
    self.assertIsNone(props)

  def testSomeProps(self):
    builder_dir = self.tmp_dir.joinpath('some-bucket', 'some-builder')
    os.makedirs(builder_dir)
    with open(builder_dir.joinpath('properties.json'), 'w') as f:
      json.dump({'some-key': 'some-val'}, f)

    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket')
    self.assertEqual(props['some-key'], 'some-val')

  def testBucketName(self):
    # Missing bucket_name should be fine since there's only one builder that
    # matches the bulder name.
    builder_dir = self.tmp_dir.joinpath('some-bucket1', 'some-builder')
    os.makedirs(builder_dir)
    with open(builder_dir.joinpath('properties.json'), 'w') as f:
      json.dump({'some-key': 'some-val'}, f)
    props, _ = builders.find_builder_props('some-builder')
    self.assertEqual(props['some-key'], 'some-val')

    # Missing bucket_name should fail since there's now multiple builders that
    # match the builder name under different projects.
    builder_dir = self.tmp_dir.joinpath('some-bucket2', 'some-builder')
    os.makedirs(builder_dir)
    with open(builder_dir.joinpath('properties.json'), 'w') as f:
      json.dump({'some-key': 'some-val'}, f)
    props, _ = builders.find_builder_props('some-builder')
    self.assertIsNone(props)

  def testInternalProps(self):
    # Create props for a public and internal builder, then fetch those props.
    builder_dir = self.tmp_dir.joinpath('some-bucket', 'some-builder')
    os.makedirs(builder_dir)
    with open(builder_dir.joinpath('properties.json'), 'w') as f:
      json.dump({'some-key': 'some-public-val'}, f)

    internal_builder_dir = self.tmp_internal_dir.joinpath(
        'some-bucket', 'some-builder')
    os.makedirs(internal_builder_dir)
    with open(internal_builder_dir.joinpath('properties.json'), 'w') as f:
      json.dump({'some-key': 'some-internal-val'}, f)

    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket',
                                           project_name='chromium')
    self.assertEqual(props['some-key'], 'some-public-val')

    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket',
                                           project_name='chrome')
    self.assertEqual(props['some-key'], 'some-internal-val')

    # Missing project_name should return the internal builder since that's what
    # we want to default to.
    props, project = builders.find_builder_props('some-builder',
                                                 bucket_name='some-bucket')
    self.assertEqual(props['some-key'], 'some-internal-val')
    self.assertEqual(project, 'chrome')

  @mock.patch("subprocess.run")
  def testBuildbucketFallback(self, mock_subprocess_run):
    mock_p = mock.MagicMock()
    mock_subprocess_run.return_value = mock_p

    # Failed bb RPC.
    mock_p.returncode = 1
    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket',
                                           project_name='some-project')
    self.assertIsNone(props)

    # Empty json from bb RPC.
    mock_p.returncode = 0
    mock_p.stdout = '{}'
    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket',
                                           project_name='some-project')
    self.assertIsNone(props)

    # Successful bb RPC.
    props = {'key1': 1, 'key2': 'val2'}
    mock_p.returncode = 0
    mock_p.stdout = json.dumps({
        'config': {
            'properties': json.dumps(props),
        },
    })
    props, _ = builders.find_builder_props('some-builder',
                                           bucket_name='some-bucket',
                                           project_name='some-project')
    self.assertEqual(props, {'key1': 1, 'key2': 'val2'})


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