blob: fc343bf6b8d295b0d0f86cfe0922482d063546de [file] [log] [blame]
# Copyright 2016 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.
import unittest
from blinkpy.common.host_mock import MockHost
from blinkpy.common.net.results_fetcher import Build
from blinkpy.common.net.git_cl import CLStatus
from blinkpy.common.net.git_cl import GitCL
from blinkpy.common.net.git_cl import SEARCHBUILDS_RESPONSE_PREFIX
from blinkpy.common.net.git_cl import TryJobStatus
from blinkpy.common.net.web_mock import MockWeb
from blinkpy.common.system.executive_mock import MockExecutive
class GitCLTest(unittest.TestCase):
def test_run(self):
host = MockHost()
host.executive = MockExecutive(output='mock-output')
git_cl = GitCL(host)
output = git_cl.run(['command'])
self.assertEqual(output, 'mock-output')
self.assertEqual(host.executive.calls, [['git', 'cl', 'command']])
def test_run_with_auth(self):
host = MockHost()
host.executive = MockExecutive(output='mock-output')
git_cl = GitCL(host, auth_refresh_token_json='token.json')
git_cl.run(['try', '-b', 'win10_blink_rel'])
self.assertEqual(host.executive.calls, [[
'git', 'cl', 'try', '-b', 'win10_blink_rel',
'--auth-refresh-token-json', 'token.json'
]])
def test_some_commands_not_run_with_auth(self):
host = MockHost()
host.executive = MockExecutive(output='mock-output')
git_cl = GitCL(host, auth_refresh_token_json='token.json')
git_cl.run(['issue'])
self.assertEqual(host.executive.calls, [['git', 'cl', 'issue']])
def test_trigger_try_jobs_with_list(self):
# When no bucket is specified, luci.chromium.try is used by
# default. Besides, `git cl try` invocations are grouped by buckets.
host = MockHost()
git_cl = GitCL(host, auth_refresh_token_json='token.json')
git_cl.trigger_try_jobs([
'android_blink_rel', 'fake_blink_try_linux', 'fake_blink_try_win'
])
self.assertEqual(host.executive.calls, [
[
'git', 'cl', 'try', '-B', 'luci.chromium.try', '-b',
'fake_blink_try_linux', '-b', 'fake_blink_try_win',
'--auth-refresh-token-json', 'token.json'
],
[
'git', 'cl', 'try', '-B', 'luci.chromium.android', '-b',
'android_blink_rel', '--auth-refresh-token-json', 'token.json'
],
])
def test_trigger_try_jobs_with_frozenset(self):
# The trigger_try_jobs method may be called with an immutable set.
host = MockHost()
git_cl = GitCL(host, auth_refresh_token_json='token.json')
git_cl.trigger_try_jobs(
frozenset(['fake_blink_try_linux', 'fake_blink_try_win']))
self.assertEqual(host.executive.calls, [
[
'git', 'cl', 'try', '-B', 'luci.chromium.try', '-b',
'fake_blink_try_linux', '-b', 'fake_blink_try_win',
'--auth-refresh-token-json', 'token.json'
],
])
def test_trigger_try_jobs_with_explicit_bucket(self):
# An explicit bucket overrides configured or default buckets.
host = MockHost()
git_cl = GitCL(host, auth_refresh_token_json='token.json')
git_cl.trigger_try_jobs(['fake_blink_try_linux', 'android_blink_rel'],
bucket='luci.dummy')
self.assertEqual(host.executive.calls, [
[
'git', 'cl', 'try', '-B', 'luci.dummy', '-b',
'android_blink_rel', '-b', 'fake_blink_try_linux',
'--auth-refresh-token-json', 'token.json'
],
])
def test_get_issue_number(self):
host = MockHost()
host.executive = MockExecutive(
output='Foo\nIssue number: 12345 (http://crrev.com/12345)')
git_cl = GitCL(host)
self.assertEqual(git_cl.get_issue_number(), '12345')
def test_get_issue_number_none(self):
host = MockHost()
host.executive = MockExecutive(output='Issue number: None (None)')
git_cl = GitCL(host)
self.assertEqual(git_cl.get_issue_number(), 'None')
def test_get_issue_number_nothing_in_output(self):
host = MockHost()
host.executive = MockExecutive(output='Bogus output')
git_cl = GitCL(host)
self.assertEqual(git_cl.get_issue_number(), 'None')
def test_wait_for_try_jobs_timeout(self):
host = MockHost()
git_cl = GitCL(host)
response = {
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "STARTED",
"builder": {
"builder": "some-builder"
},
"number": 100
}
]
}"""
}
# Specify the same response 10 times to ensure each poll gets ones.
git_cl._host.web = MockWeb(responses=[response] * 10)
self.assertIsNone(git_cl.wait_for_try_jobs())
self.assertEqual(
host.stdout.getvalue(),
'Waiting for try jobs, timeout: 7200 seconds.\n'
'Waiting for try jobs. 600 seconds passed.\n'
'Waiting for try jobs. 1800 seconds passed.\n'
'Waiting for try jobs. 3000 seconds passed.\n'
'Waiting for try jobs. 4200 seconds passed.\n'
'Waiting for try jobs. 5400 seconds passed.\n'
'Waiting for try jobs. 6600 seconds passed.\n'
'Timed out waiting for try jobs.\n')
def test_wait_for_try_jobs_no_results_not_considered_finished(self):
host = MockHost()
git_cl = GitCL(host)
# git_cl.fetch_raw_try_job_results = lambda **_: []
response = {
'status_code': 200,
'body': SEARCHBUILDS_RESPONSE_PREFIX + "{}"
}
# Specify the same response 10 times to ensure each poll gets ones.
git_cl._host.web = MockWeb(responses=[response] * 10)
self.assertIsNone(git_cl.wait_for_try_jobs())
self.assertEqual(
host.stdout.getvalue(),
'Waiting for try jobs, timeout: 7200 seconds.\n'
'Waiting for try jobs. 600 seconds passed.\n'
'Waiting for try jobs. 1800 seconds passed.\n'
'Waiting for try jobs. 3000 seconds passed.\n'
'Waiting for try jobs. 4200 seconds passed.\n'
'Waiting for try jobs. 5400 seconds passed.\n'
'Waiting for try jobs. 6600 seconds passed.\n'
'Timed out waiting for try jobs.\n')
def test_wait_for_try_jobs_cl_closed(self):
host = MockHost()
host.executive = MockExecutive(output='closed')
git_cl = GitCL(host)
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "STARTED",
"builder": {
"builder": "some-builder"
}
}
]
}"""
}])
self.assertEqual(
git_cl.wait_for_try_jobs(),
CLStatus(
status='closed',
try_job_results={
Build('some-builder', None): TryJobStatus('STARTED', None),
},
))
self.assertEqual(host.stdout.getvalue(),
'Waiting for try jobs, timeout: 7200 seconds.\n')
def test_wait_for_try_jobs_done(self):
host = MockHost()
host.executive = MockExecutive(output='lgtm')
git_cl = GitCL(host)
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "FAILURE",
"builder": {
"builder": "some-builder"
},
"number": 100
}
]
}"""
}])
self.assertEqual(
git_cl.wait_for_try_jobs(),
CLStatus(
status='lgtm',
try_job_results={
Build('some-builder', 100):
TryJobStatus('COMPLETED', 'FAILURE'),
}))
self.assertEqual(host.stdout.getvalue(),
'Waiting for try jobs, timeout: 7200 seconds.\n')
def test_wait_for_closed_status_timeout(self):
host = MockHost()
host.executive = MockExecutive(output='commit')
git_cl = GitCL(host)
self.assertIsNone(git_cl.wait_for_closed_status())
self.assertEqual(
host.stdout.getvalue(),
'Waiting for closed status, timeout: 1800 seconds.\n'
'Waiting for closed status. 120 seconds passed.\n'
'Waiting for closed status. 360 seconds passed.\n'
'Waiting for closed status. 600 seconds passed.\n'
'Waiting for closed status. 840 seconds passed.\n'
'Waiting for closed status. 1080 seconds passed.\n'
'Waiting for closed status. 1320 seconds passed.\n'
'Waiting for closed status. 1560 seconds passed.\n'
'Waiting for closed status. 1800 seconds passed.\n'
'Timed out waiting for closed status.\n')
def test_wait_for_closed_status_closed(self):
host = MockHost()
host.executive = MockExecutive(output='closed')
git_cl = GitCL(host)
self.assertEqual(git_cl.wait_for_closed_status(), 'closed')
self.assertEqual(
host.stdout.getvalue(),
'Waiting for closed status, timeout: 1800 seconds.\n'
'CL is closed.\n')
def test_has_failing_try_results_empty(self):
self.assertFalse(GitCL.some_failed({}))
def test_has_failing_try_results_only_success_and_started(self):
self.assertFalse(
GitCL.some_failed({
Build('some-builder', 90):
TryJobStatus('COMPLETED', 'SUCCESS'),
Build('some-builder', 100):
TryJobStatus('STARTED'),
}))
def test_has_failing_try_results_with_failing_results(self):
self.assertTrue(
GitCL.some_failed({
Build('some-builder', 1):
TryJobStatus('COMPLETED', 'FAILURE'),
}))
def test_all_success_empty(self):
self.assertTrue(GitCL.all_success({}))
def test_all_success_true(self):
self.assertTrue(
GitCL.all_success({
Build('some-builder', 1):
TryJobStatus('COMPLETED', 'SUCCESS'),
}))
def test_all_success_with_started_build(self):
self.assertFalse(
GitCL.all_success({
Build('some-builder', 1):
TryJobStatus('COMPLETED', 'SUCCESS'),
Build('some-builder', 2):
TryJobStatus('STARTED'),
}))
def test_latest_try_jobs_cq_only(self):
git_cl = GitCL(MockHost())
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "SCHEDULED",
"builder": {
"builder": "cq-a"
},
"tags": [
{"key": "user_agent", "value": "cq"}
]
},
{
"status": "SCHEDULED",
"builder": {
"builder": "cq-b"
},
"tags": [
{"key": "user_agent", "value": "cq"},
{"key": "cq_experimental", "value": "false"}
]
},
{
"status": "SCHEDULED",
"builder": {
"builder": "cq-c"
},
"tags": [
{"key": "user_agent", "value": "cq"},
{"key": "cq_experimental", "value": "false"}
]
},
{
"status": "SCHEDULED",
"builder": {
"builder": "cq-a-experimental"
},
"tags": [
{"key": "user_agent", "value": "cq"},
{"key": "cq_experimental", "value": "true"}
]
},
{
"status": "SCHEDULED",
"builder": {
"builder": "cq-b-experimental"
},
"tags": [
{"key": "user_agent", "value": "cq"},
{"key": "cq_experimental", "value": "true"}
]
},
{
"status": "SCHEDULED",
"builder": {
"builder": "other-a"
},
"tags": [
{"key": "user_agent", "value": "git_cl_try"}
]
},
{
"status": "SCHEDULED",
"builder": {
"builder": "other-b"
},
"tags": [
{"key": "user_agent", "value": "git_cl_try"},
{"key": "cq_experimental", "value": "false"}
]
}
]
}"""
}])
self.assertEqual(
git_cl.latest_try_jobs(cq_only=True), {
Build('cq-a'): TryJobStatus('SCHEDULED'),
Build('cq-b'): TryJobStatus('SCHEDULED'),
Build('cq-c'): TryJobStatus('SCHEDULED'),
})
def test_latest_try_jobs(self):
# Here we have multiple builds with the same name, but we only take the
# latest one (based on build number).
git_cl = GitCL(MockHost())
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "SUCCESS",
"builder": {
"builder": "builder-b"
},
"number": 100
},
{
"status": "SUCCESS",
"builder": {
"builder": "builder-b"
},
"number": 90
},
{
"status": "SCHEDULED",
"builder": {
"builder": "builder-a"
}
},
{
"status": "SUCCESS",
"builder": {
"builder": "builder-c"
},
"number": 123
}
]
}"""
}])
self.assertEqual(
git_cl.latest_try_jobs(builder_names=['builder-a', 'builder-b']), {
Build('builder-a'): TryJobStatus('SCHEDULED'),
Build('builder-b', 100): TryJobStatus('COMPLETED', 'SUCCESS'),
})
def test_latest_try_jobs_started(self):
git_cl = GitCL(MockHost())
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "STARTED",
"builder": {
"builder": "builder-a"
},
"number": 100
}
]
}"""
}])
self.assertEqual(
git_cl.latest_try_jobs(builder_names=['builder-a']),
{Build('builder-a', 100): TryJobStatus('STARTED')})
def test_latest_try_jobs_failures(self):
git_cl = GitCL(MockHost())
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "FAILURE",
"builder": {
"builder": "builder-a"
},
"number": 100
},
{
"status": "INFRA_FAILURE",
"builder": {
"builder": "builder-b"
},
"number": 200
}
]
}"""
}])
self.assertEqual(
git_cl.latest_try_jobs(builder_names=['builder-a', 'builder-b']), {
Build('builder-a', 100): TryJobStatus('COMPLETED', 'FAILURE'),
Build('builder-b', 200): TryJobStatus('COMPLETED', 'FAILURE'),
})
def test_filter_latest(self):
try_job_results = {
Build('builder-a', 100): TryJobStatus('COMPLETED', 'FAILURE'),
Build('builder-a', 200): TryJobStatus('COMPLETED', 'SUCCESS'),
Build('builder-b', 50): TryJobStatus('SCHEDULED'),
}
self.assertEqual(
GitCL.filter_latest(try_job_results), {
Build('builder-a', 200): TryJobStatus('COMPLETED', 'SUCCESS'),
Build('builder-b', 50): TryJobStatus('SCHEDULED'),
})
def test_filter_latest_none(self):
self.assertIsNone(GitCL.filter_latest(None))
def test_try_job_results_with_other_builder(self):
git_cl = GitCL(MockHost())
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "FAILURE",
"builder": {
"builder": "builder-a"
},
"number": 100,
"tags": [
{"key": "user_agent", "value": "cq"}
]
}
]
}"""
}])
# We ignore builders that we explicitly don't care about;
# so if we only care about other-builder, not builder-a,
# then no exception is raised.
self.assertEqual(
git_cl.try_job_results(builder_names=['other-builder']), {})
def test_try_job_results(self):
git_cl = GitCL(MockHost())
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "SUCCESS",
"builder": {
"builder": "builder-a"
},
"number": 111,
"tags": [
{"key": "user_agent", "value": "cq"}
]
},
{
"status": "SCHEDULED",
"builder": {
"builder": "builder-b"
},
"number": 222
},
{
"status": "INFRA_FAILURE",
"builder": {
"builder": "builder-c"
},
"number": 333
}
]
}"""
}])
self.assertEqual(
git_cl.try_job_results(issue_number=None),
{
Build('builder-a', 111):
TryJobStatus('COMPLETED', 'SUCCESS'),
Build('builder-b', 222):
TryJobStatus('SCHEDULED', None),
# INFRA_FAILURE is mapped to FAILURE for this build.
Build('builder-c', 333):
TryJobStatus('COMPLETED', 'FAILURE'),
})
def test_try_job_results_skip_experimental_cq(self):
git_cl = GitCL(MockHost())
git_cl._host.web = MockWeb(responses=[{
'status_code':
200,
'body':
SEARCHBUILDS_RESPONSE_PREFIX + """{
"builds": [
{
"status": "SUCCESS",
"builder": {
"builder": "builder-a"
},
"number": 111,
"tags": [
{"key": "user_agent", "value": "cq"}
]
},
{
"status": "SUCCESS",
"builder": {
"builder": "builder-b"
},
"number": 222,
"tags": [
{"key": "user_agent", "value": "cq"},
{"key": "cq_experimental", "value": "true"}
]
}
]
}"""
}])
self.assertEqual(
# Only one build appears - builder-b is ignored because it is
# experimental.
git_cl.try_job_results(issue_number=None, cq_only=True),
{
Build('builder-a', 111): TryJobStatus('COMPLETED', 'SUCCESS'),
})