blob: 63c63ae4fcdfe5b8348dd43170171a2ff93d2df1 [file] [log] [blame]
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from collections import OrderedDict
import json
import os
import sys
import unittest
from StringIO import StringIO
import extract_components
SRC = os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir)
sys.path.append(os.path.join(SRC, 'third_party', 'pymock'))
import mock
def mock_file_tree(tree):
os_walk_mocks = []
file_mocks = {}
for path in tree:
if tree[path] is not None:
os_walk_mocks.append((path, ('ignored'), ('OWNERS', 'dummy.cc')))
file_mocks[os.path.join(path, 'OWNERS')] = tree[path]
else:
os_walk_mocks.append((path, ('ignored'), ('dummy.cc')))
def custom_mock_open(files_data):
def inner_open(path, mode='r'):
ret_val = mock.MagicMock()
if path in files_data and mode == 'r':
class mock_opened_file(object):
def __enter__(self, *args, **kwargs):
return self
def __iter__(self, *args, **kwargs):
return iter(files_data[path].splitlines())
def __exit__(self, *args, **kwargs):
pass
ret_val = mock_opened_file()
return ret_val
return inner_open
def wrapper(func):
@mock.patch('owners_file_tags.open', custom_mock_open(file_mocks),
create=True)
@mock.patch('os.walk', mock.MagicMock(return_value=os_walk_mocks))
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
return wrapper
class ExtractComponentsTest(unittest.TestCase):
@mock_file_tree({
'src': 'boss@chromium.org\n',
'src/dummydir1': 'dummy@chromium.org\n'
'# TEAM: dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir2': 'dummy2@chromium.org\n'
'# TEAM: other-dummy-team@chromium.org\n'
'# COMPONENT: Components>Component2',
'src/dummydir1/innerdir1': 'dummy@chromium.org\n'
'# TEAM: dummy-specialist-team@chromium.org\n'
'# COMPONENT: Dummy>Component>Subcomponent'})
def testBaseCase(self):
saved_output = StringIO()
with mock.patch('sys.stdout', saved_output):
error_code = extract_components.main(['%prog'])
self.assertEqual(0, error_code)
result_minus_readme = json.loads(saved_output.getvalue())
del result_minus_readme['AAA-README']
self.assertEqual(result_minus_readme, {
'component-to-team': {
'Components>Component2': 'other-dummy-team@chromium.org',
'Dummy>Component': 'dummy-team@chromium.org',
'Dummy>Component>Subcomponent': 'dummy-specialist-team@chromium.org'
},
'dir-to-component': {
'tools/checkteamtags/src/dummydir1': 'Dummy>Component',
'tools/checkteamtags/src/dummydir1/innerdir1':
'Dummy>Component>Subcomponent',
'tools/checkteamtags/src/dummydir2': 'Components>Component2'
}})
@mock_file_tree({
'src': 'boss@chromium.org\n',
'src/dummydir1': 'dummy@chromium.org\n'
'# TEAM: dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir2': 'dummy2@chromium.org\n'
'# TEAM: other-dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir1/innerdir1': 'dummy@chromium.org\n'
'# TEAM: dummy-specialist-team@chromium.org\n'
'# COMPONENT: Dummy>Component>Subcomponent'})
def testMultipleTeamsOneComponent(self):
saved_output = StringIO()
with mock.patch('sys.stdout', saved_output):
error_code = extract_components.main(['%prog', '-w'])
self.assertNotEqual(0, error_code)
output = saved_output.getvalue()
self.assertIn('has more than one team assigned to it', output)
self.assertIn('Not writing to file', output)
@mock_file_tree({
'src': 'boss@chromium.org\n',
'src/dummydir1': 'dummy@chromium.org\n'
'# TEAM: dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir2': 'dummy2@chromium.org\n'
'# TEAM: other-dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir1/innerdir1': 'dummy@chromium.org\n'
'# TEAM: dummy-specialist-team@chromium.org\n'
'# COMPONENT: Dummy>Component>Subcomponent'})
def testVerbose(self):
saved_output = StringIO()
with mock.patch('sys.stdout', saved_output):
extract_components.main(['%prog', '-v'])
output = saved_output.getvalue()
self.assertIn('src/OWNERS has no COMPONENT tag', output)
@mock_file_tree({
'src': 'boss@chromium.org\n',
'src/dummydir1': 'dummy@chromium.org\n'
'# TEAM: dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir2': 'dummy2@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir1/innerdir1': 'dummy@chromium.org\n'
'# TEAM: dummy-specialist-team@chromium.org\n'
'# COMPONENT: Dummy>Component>Subcomponent'})
def testCoverage(self):
saved_output = StringIO()
with mock.patch('sys.stdout', saved_output):
extract_components.main(['%prog', '-s 2'])
output = saved_output.getvalue()
self.assertIn('4 OWNERS files in total.', output)
self.assertIn('3 (75.00%) OWNERS files have COMPONENT', output)
self.assertIn('2 (50.00%) OWNERS files have TEAM and COMPONENT', output)
@mock_file_tree({
'src': 'boss@chromium.org\n',
'src/dummydir1': 'dummy@chromium.org\n'
'# TEAM: dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir2': 'dummy2@chromium.org\n'
'# COMPONENT: Dummy>Component',
'src/dummydir1/innerdir1': 'dummy@chromium.org\n'
'# TEAM: dummy-specialist-team@chromium.org\n'
'# COMPONENT: Dummy>Component>Subcomponent'})
def testCompleteCoverage(self):
saved_output = StringIO()
with mock.patch('sys.stdout', saved_output):
extract_components.main(['%prog', '-c'])
output = saved_output.getvalue()
self.assertIn('4 OWNERS files in total.', output)
self.assertIn('3 (75.00%) OWNERS files have COMPONENT', output)
self.assertIn('2 (50.00%) OWNERS files have TEAM and COMPONENT', output)
self.assertIn('4 OWNERS files at depth 0', output)
# We use OrderedDict here to guarantee that mocked version of os.walk returns
# directories in the specified order (top-down).
@mock_file_tree(OrderedDict([
('chromium/src', 'boss@chromium.org\n'),
('chromium/src/dir1', 'dummy@chromium.org\n'
'# TEAM: dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component'),
('chromium/src/dir2', 'dummy2@chromium.org\n'
'# TEAM: other-dummy-team@chromium.org\n'
'# COMPONENT: Dummy>Component2'),
('chromium/src/dir1/subdir', 'dummy@chromium.org'),
('chromium/src/dir2/subdir', None),
('third_party/WebKit/LayoutTests/foo',
'# TEAM: dummy-team-3@chromium.org\n'),
('third_party/WebKit/LayoutTests/bar',
'# TEAM: dummy-team-3@chromium.org\n'
'# COMPONENT: Dummy>Component3\n'),
]))
def testIncludesSubdirectoriesWithNoOwnersFileOrNoComponentTag(self):
self.maxDiff = None # This helps to see assertDictEqual errors in full.
saved_output = StringIO()
with mock.patch('sys.stdout', saved_output):
error_code = extract_components.main(['%prog', '--include-subdirs', ''])
self.assertEqual(0, error_code)
result_minus_readme = json.loads(saved_output.getvalue())
del result_minus_readme['AAA-README']
self.assertDictEqual(result_minus_readme, {
u'component-to-team': {
u'Dummy>Component': u'dummy-team@chromium.org',
u'Dummy>Component2': u'other-dummy-team@chromium.org',
u'Dummy>Component3': u'dummy-team-3@chromium.org',
},
u'dir-to-component': {
u'chromium/src/dir1': u'Dummy>Component',
u'chromium/src/dir1/subdir': u'Dummy>Component',
u'chromium/src/dir2': u'Dummy>Component2',
u'chromium/src/dir2/subdir': u'Dummy>Component2',
u'third_party/WebKit/LayoutTests/bar': u'Dummy>Component3',
},
u'dir-to-team': {
u'third_party/WebKit/LayoutTests/foo': u'dummy-team-3@chromium.org',
}})