blob: 0b34e6631fce493c06633b62a8cd3ef5cddef18c [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for afdo module."""
from __future__ import print_function
import collections
import datetime
import mock
import os
import time
from chromite.cbuildbot import afdo
from chromite.lib import cros_build_lib
from chromite.lib import cros_test_lib
from chromite.lib import gs
from chromite.lib import osutils
from chromite.lib import path_util
from chromite.lib import portage_util
class AfdoTest(cros_test_lib.MockTempDirTestCase):
"""Unit test of afdo module."""
def runCreateAndUploadMergedAFDOProfileOnce(self, upload_ok=True, **kwargs):
if 'unmerged_name' not in kwargs:
# Match everything; 'z' > 'foo'
kwargs['unmerged_name'] = 'z'
Mocks = collections.namedtuple('Mocks', [
'gs_context',
'run_command',
'uncompress_file',
'compress_file',
'upload',
])
def MockList(*_args, **_kwargs):
MockGsFile = collections.namedtuple('MockGsFile',
['url', 'creation_time'])
num_files = 7
results = []
for i in range(1, num_files+1):
now = datetime.datetime(year=1990, month=1, day=1+i)
url = os.path.join(afdo.GSURL_BASE_BENCH, 'foo-%d%s%s' %
(i, afdo.AFDO_SUFFIX, afdo.COMPRESSION_SUFFIX))
results.append(MockGsFile(url=url, creation_time=now))
return results
mock_gs = mock.Mock()
mock_gs.List = MockList
run_command = self.PatchObject(cros_build_lib, 'RunCommand')
uncompress_file = self.PatchObject(cros_build_lib, 'UncompressFile')
compress_file = self.PatchObject(cros_build_lib, 'CompressFile')
upload = self.PatchObject(afdo, 'GSUploadIfNotPresent')
upload.return_value = upload_ok
merged_name, uploaded = afdo.CreateAndUploadMergedAFDOProfile(mock_gs,
'/buildroot',
**kwargs)
return merged_name, uploaded, Mocks(
gs_context=mock_gs,
run_command=run_command,
uncompress_file=uncompress_file,
compress_file=compress_file,
upload=upload
)
def testCreateAndUploadMergedAFDOProfileWorksInTheHappyCase(self):
merged_name, uploaded, mocks = \
self.runCreateAndUploadMergedAFDOProfileOnce(recent_to_merge=5)
self.assertTrue(uploaded)
# Note that we always return the *basename*
self.assertEqual(merged_name, 'foo-7-merged' + afdo.AFDO_SUFFIX)
self.assertTrue(uploaded)
mocks.run_command.assert_called_once()
# Note that these should all be in-chroot names.
expected_ordered_args = ['llvm-profdata', 'merge', '-sample']
expected_unordered_args = [
'-output=/tmp/foo-7-merged' + afdo.AFDO_SUFFIX,
'/tmp/foo-3' + afdo.AFDO_SUFFIX,
'/tmp/foo-4' + afdo.AFDO_SUFFIX,
'/tmp/foo-5' + afdo.AFDO_SUFFIX,
'/tmp/foo-6' + afdo.AFDO_SUFFIX,
'/tmp/foo-7' + afdo.AFDO_SUFFIX,
]
args = mocks.run_command.call_args[0][0]
ordered_args = args[:len(expected_ordered_args)]
self.assertEqual(ordered_args, expected_ordered_args)
unordered_args = args[len(expected_ordered_args):]
self.assertItemsEqual(unordered_args, expected_unordered_args)
self.assertEqual(mocks.gs_context.Copy.call_count, 5)
self.assertEqual(mocks.uncompress_file.call_count, 5)
def call_for(n):
basis = '/buildroot/chroot/tmp/foo-%d%s' % (n, afdo.AFDO_SUFFIX)
return mock.call(basis + afdo.COMPRESSION_SUFFIX, basis)
mocks.uncompress_file.assert_has_calls(
any_order=True, calls=[call_for(x) for x in range(3, 8)])
compressed_target = '/buildroot/chroot/tmp/foo-7-merged%s%s' % \
(afdo.AFDO_SUFFIX, afdo.COMPRESSION_SUFFIX)
mocks.compress_file.assert_called_once()
args = mocks.compress_file.call_args[0]
self.assertEqual(args, (
compressed_target[:-len(afdo.COMPRESSION_SUFFIX)],
compressed_target,
))
mocks.upload.assert_called_once()
args = mocks.upload.call_args[0]
self.assertEqual(args, (
mocks.gs_context,
compressed_target,
'%s/foo-7-merged%s%s' %
(afdo.GSURL_BASE_BENCH, afdo.AFDO_SUFFIX, afdo.COMPRESSION_SUFFIX),
))
def testCreateAndUploadMergedAFDOProfileSucceedsIfUploadFails(self):
merged_name, uploaded, _ = \
self.runCreateAndUploadMergedAFDOProfileOnce(upload_ok=False)
self.assertIsNotNone(merged_name)
self.assertFalse(uploaded)
def testMergeIsOKIfWeFindFewerProfilesThanWeWant(self):
merged_name, uploaded, mocks = \
self.runCreateAndUploadMergedAFDOProfileOnce(recent_to_merge=1000,
max_age_days=1000)
self.assertTrue(uploaded)
self.assertIsNotNone(merged_name)
self.assertEqual(mocks.gs_context.Copy.call_count, 7)
def testNoProfileIsGeneratedIfNoFilesBeforeMergedNameExist(self):
merged_name, uploaded, _ = \
self.runCreateAndUploadMergedAFDOProfileOnce(
unmerged_name='foo-0' + afdo.AFDO_SUFFIX)
self.assertIsNone(merged_name)
self.assertFalse(uploaded)
merged_name, uploaded, _ = \
self.runCreateAndUploadMergedAFDOProfileOnce(
unmerged_name='foo-1' + afdo.AFDO_SUFFIX)
self.assertIsNone(merged_name)
self.assertFalse(uploaded)
merged_name, uploaded, _ = \
self.runCreateAndUploadMergedAFDOProfileOnce(
unmerged_name='foo-2' + afdo.AFDO_SUFFIX)
self.assertIsNotNone(merged_name)
self.assertTrue(uploaded)
def testNoFilesAfterUnmergedNameAreIncluded(self):
max_name = 'foo-3' + afdo.AFDO_SUFFIX
merged_name, uploaded, mocks = \
self.runCreateAndUploadMergedAFDOProfileOnce(unmerged_name=max_name)
self.assertEqual('foo-3-merged' + afdo.AFDO_SUFFIX, merged_name)
self.assertTrue(uploaded)
# Note that these should all be in-chroot names.
expected_ordered_args = ['llvm-profdata', 'merge', '-sample']
expected_unordered_args = [
'-output=/tmp/foo-3-merged' + afdo.AFDO_SUFFIX,
'/tmp/foo-1' + afdo.AFDO_SUFFIX,
'/tmp/foo-2' + afdo.AFDO_SUFFIX,
'/tmp/foo-3' + afdo.AFDO_SUFFIX,
]
args = mocks.run_command.call_args[0][0]
ordered_args = args[:len(expected_ordered_args)]
self.assertEqual(ordered_args, expected_ordered_args)
unordered_args = args[len(expected_ordered_args):]
self.assertItemsEqual(unordered_args, expected_unordered_args)
self.assertEqual(mocks.gs_context.Copy.call_count, 3)
self.assertEqual(mocks.uncompress_file.call_count, 3)
def testMergeDoesntHappenIfNoProfilesAreMerged(self):
runs = [
self.runCreateAndUploadMergedAFDOProfileOnce(recent_to_merge=1),
self.runCreateAndUploadMergedAFDOProfileOnce(max_age_days=0),
]
for merged_name, uploaded, mocks in runs:
self.assertIsNone(merged_name)
self.assertFalse(uploaded)
mocks.gs_context.Copy.assert_not_called()
mocks.run_command.assert_not_called()
mocks.uncompress_file.assert_not_called()
mocks.compress_file.assert_not_called()
mocks.upload.assert_not_called()
def testFindLatestProfile(self):
versions = [[1, 0, 0, 0], [1, 2, 3, 4], [2, 2, 2, 2]]
self.assertEqual(afdo.FindLatestProfile([0, 0, 0, 0], versions), None)
self.assertEqual(afdo.FindLatestProfile([1, 0, 0, 0], versions),
[1, 0, 0, 0])
self.assertEqual(afdo.FindLatestProfile([1, 2, 0, 0], versions),
[1, 0, 0, 0])
self.assertEqual(afdo.FindLatestProfile([9, 9, 9, 9], versions),
[2, 2, 2, 2])
def testPatchKernelEbuild(self):
before = [
'The following line contains the version:',
'AFDO_PROFILE_VERSION="R63-9901.21-1506581597"',
'It should be changed.'
]
after = [
'The following line contains the version:',
'AFDO_PROFILE_VERSION="R12-3456.78-9876543210"',
'It should be changed.'
]
tf = os.path.join(self.tempdir, 'test.ebuild')
osutils.WriteFile(tf, '\n'.join(before))
afdo.PatchKernelEbuild(tf, [12, 3456, 78, 9876543210])
x = osutils.ReadFile(tf).splitlines()
self.assertEqual(after, x)
def testGetAvailableKernelProfiles(self):
def MockGsList(path):
unused = {'content_length':None,
'creation_time':None,
'generation':None,
'metageneration':None}
path = path.replace('*', '%s')
return [
gs.GSListResult(
url=(path % ('4.4', 'R63-9901.21-1506581597')), **unused),
gs.GSListResult(
url=(path % ('3.8', 'R61-9765.70-1506575230')), **unused),
]
self.PatchObject(gs.GSContext, 'List',
lambda _, path, **kwargs: MockGsList(path))
profiles = afdo.GetAvailableKernelProfiles()
self.assertIn([63, 9901, 21, 1506581597], profiles['4.4'])
self.assertIn([61, 9765, 70, 1506575230], profiles['3.8'])
def testFindKernelEbuilds(self):
ebuilds = [(os.path.basename(ebuild[0]), ebuild[1])
for ebuild in afdo.FindKernelEbuilds()]
self.assertIn(('chromeos-kernel-4_4-9999.ebuild', '4.4'), ebuilds)
self.assertIn(('chromeos-kernel-3_8-9999.ebuild', '3.8'), ebuilds)
def testProfileAge(self):
self.assertEqual(
0,
afdo.ProfileAge([0, 0, 0, int(time.time())])
)
self.assertEqual(
1,
afdo.ProfileAge([0, 0, 0, int(time.time() - 86400)])
)
def testGetCWPProfile(self):
profiles = ['R62-3202.43-320243.afdo.xz',
'R63-3223.0-233200.afdo.xz',
'R63-3239.20-323920.afdo.xz',
'R63-3239.42-323942.afdo.xz',
'R63-3239.50-323950.afdo.xz',
'R63-3239.50-323999.afdo.xz',
'R64-3280.5-328005.afdo.xz',
'R64-3282.41-328241.afdo.xz',
'R65-3299.0-329900.afdo.xz']
def MockGsList(path):
unused = {'content_length':None,
'creation_time':None,
'generation':None,
'metageneration':None}
return [gs.GSListResult(url=os.path.join(path, f),
**unused) for f in profiles]
self.PatchObject(gs.GSContext, 'List',
lambda _, path, **kwargs: MockGsList(path))
def _test(version, idx):
unused = {'pv':None,
'package':None,
'version_no_rev':None,
'rev':None,
'category':None,
'cpv': None,
'cp': None,
'cpf': None}
cpv = portage_util.CPV(version=version, **unused)
profile = afdo.GetCWPProfile(cpv, 'silvermont', 'unused', gs.GSContext())
# Expect the most recent profile on the same branch.
self.assertEqual(profile, profiles[idx][:-3])
_test('66.0.3300.0_rc-r1', 8)
_test('65.0.3283.0_rc-r1', 7)
_test('65.0.3283.1_rc-r1', 7)
_test('64.0.3282.42_rc-r1', 7)
_test('64.0.3282.40_rc-r1', 6)
_test('63.0.3239.30_rc-r1', 2)
_test('63.0.3239.42_rc-r0', 2)
_test('63.0.3239.10_rc-r1', 1)
def testCWPProfileToVersionTuple(self):
self.assertEqual(
afdo.CWPProfileToVersionTuple('gs://chromeos-prebuilt/afdo-job/cwp/'
'chrome/R66-3325.65-1519321598.afdo.xz'),
[66, 3325, 65, 1519321598])
self.assertEqual(
afdo.CWPProfileToVersionTuple('R66-3325.65-1519321598.afdo.xz'),
[66, 3325, 65, 1519321598])
def testPatchChromeEbuildAFDOFile(self):
before = [
'The following line contains the version:',
'AFDO_FILE["benchmark"]="chromeos-chrome-amd64-67.0.3379.0_rc-r1.afdo"',
'AFDO_FILE["silvermont"]="R67-3359.31-1522059092.afdo"',
'AFDO_FILE["airmont"]="airmont_before.afdo"',
'AFDO_FILE["haswell"]="haswell_before.afdo"',
'AFDO_FILE["broadwell"]="broadwell_before.afdo"',
'It should be changed.'
]
after = [
'The following line contains the version:',
'AFDO_FILE["benchmark"]="chromeos-chrome-amd64-67.0.3388.0_rc-r1.afdo"',
'AFDO_FILE["silvermont"]="R67-3360.42-153456789.afdo"',
'AFDO_FILE["airmont"]="airmont_after.afdo"',
'AFDO_FILE["haswell"]="haswell_after.afdo"',
'AFDO_FILE["broadwell"]="broadwell_after.afdo"',
'It should be changed.'
]
self.PatchObject(path_util, 'FromChrootPath', lambda x: x)
tf = os.path.join(self.tempdir, 'test.ebuild')
osutils.WriteFile(tf, '\n'.join(before))
afdo.PatchChromeEbuildAFDOFile(
tf,
{'benchmark': 'chromeos-chrome-amd64-67.0.3388.0_rc-r1.afdo',
'haswell': 'haswell_after.afdo',
'broadwell': 'broadwell_after.afdo',
'airmont': 'airmont_after.afdo',
'silvermont': 'R67-3360.42-153456789.afdo'})
x = osutils.ReadFile(tf).splitlines()
self.assertEqual(after, x)