blob: 4749f3df7d48641d2b46455521f564223ac1f9ee [file] [log] [blame]
# Copyright 2020 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 contextlib
import json
import mock
import re
import requests
import sys
import unittest
from urlparse import urlparse
from blinkpy.common.host_mock import MockHost
from blinkpy.common.path_finder import RELATIVE_WEB_TESTS
from blinkpy.web_tests.controllers.test_result_sink import CreateTestResultSink
from blinkpy.web_tests.controllers.test_result_sink import TestResultSink
from blinkpy.web_tests.models import test_results
from blinkpy.web_tests.models.typ_types import ResultType
from blinkpy.web_tests.port.test import add_manifest_to_mock_filesystem
from blinkpy.web_tests.port.test import TestPort
from blinkpy.web_tests.port.test import WEB_TEST_DIR
class TestResultSinkTestBase(unittest.TestCase):
def setUp(self):
super(TestResultSinkTestBase, self).setUpClass()
self.port = TestPort(MockHost())
def luci_context(self, **section_values):
if not section_values:
return
host = self.port.host
f, fname = host.filesystem.open_text_tempfile()
json.dump(section_values, f)
f.close()
host.environ['LUCI_CONTEXT'] = f.path
class TestCreateTestResultSink(TestResultSinkTestBase):
def test_without_luci_context(self):
self.assertIsNone(CreateTestResultSink(self.port))
def test_without_result_sink_section(self):
self.luci_context(app={'foo': 'bar'})
self.assertIsNone(CreateTestResultSink(self.port))
def test_auth_token(self):
ctx = {'address': 'localhost:123', 'auth_token': 'secret'}
self.luci_context(result_sink=ctx)
rs = CreateTestResultSink(self.port)
self.assertIsNotNone(rs)
self.assertEqual(rs._session.headers['Authorization'],
'ResultSink ' + ctx['auth_token'])
def test_with_result_sink_section(self):
ctx = {'address': 'localhost:123', 'auth_token': 'secret'}
self.luci_context(result_sink=ctx)
rs = CreateTestResultSink(self.port)
self.assertIsNotNone(rs)
response = requests.Response()
response.status_code = 200
with mock.patch.object(rs._session, 'post',
return_value=response) as m:
rs.sink(True, test_results.TestResult('test'))
self.assertTrue(m.called)
self.assertEqual(
urlparse(m.call_args[0][0]).netloc, ctx['address'])
class TestResultSinkMessage(TestResultSinkTestBase):
"""Tests ResulkSink.sink."""
def setUp(self):
super(TestResultSinkMessage, self).setUp()
patcher = mock.patch.object(TestResultSink, '_send')
self.mock_send = patcher.start()
self.addCleanup(patcher.stop)
ctx = {'address': 'localhost:123', 'auth_token': 'super-secret'}
self.luci_context(result_sink=ctx)
self.rs = CreateTestResultSink(self.port)
def sink(self, expected, test_result):
self.rs.sink(expected, test_result)
self.assertTrue(self.mock_send.called)
return self.mock_send.call_args[0][0]['testResults'][0]
def test_sink(self):
tr = test_results.TestResult(test_name='test-name')
tr.total_run_time = 123.456
tr.type = ResultType.Crash
sent_data = self.sink(True, tr)
self.assertEqual(sent_data['testId'], 'test-name')
self.assertEqual(sent_data['expected'], True)
self.assertEqual(sent_data['status'], 'CRASH')
self.assertEqual(sent_data['duration'], '123.456s')
def test_test_metadata(self):
tr = test_results.TestResult('')
base_path = '//' + RELATIVE_WEB_TESTS
tr.test_name = "test-name"
self.assertDictEqual(
self.sink(True, tr)['testMetadata'],
{
'name': 'test-name',
'location': {
'repo': 'https://chromium.googlesource.com/chromium/src',
'fileName': base_path + 'test-name',
},
},
)
tr.test_name = "///test-name"
self.assertDictEqual(
self.sink(True, tr)['testMetadata'],
{
'name': '///test-name',
'location': {
'repo': 'https://chromium.googlesource.com/chromium/src',
'fileName': base_path + '///test-name',
},
},
)
def test_device_failure(self):
tr = test_results.TestResult(test_name='test-name')
tr.type = ResultType.Failure
tr.device_failed = True
sent_data = self.sink(True, tr)
# If the device failed, 'expected' and 'status' must be False and 'ABORT'
self.assertEqual(sent_data['expected'], False)
self.assertEqual(sent_data['status'], 'ABORT')
def test_timeout(self):
tr = test_results.TestResult(test_name='test-name')
tr.type = ResultType.Timeout
sent_data = self.sink(True, tr)
# Timeout is considered as 'ABORT'
self.assertEqual(sent_data['status'], 'ABORT')
def test_artifacts(self):
tr = test_results.TestResult(test_name='test-name')
tr.artifacts.AddArtifact('test-image.png', '/tmp/test-image.png', True)
tr.artifacts.AddArtifact('stdout', '/tmp/stdout', True)
sent_data = self.sink(True, tr)
self.assertDictEqual(
sent_data['artifacts'], {
'test-image.png': {
'filePath': '/tmp/test-image.png'
},
'stdout': {
'filePath': '/tmp/stdout'
}
})
def test_artifacts_with_duplicate_paths(self):
tr = test_results.TestResult(test_name='test-name')
tr.artifacts.AddArtifact('artifact', '/tmp/foo', False)
tr.artifacts.AddArtifact('artifact', '/tmp/bar', False)
sent_data = self.sink(True, tr)
self.assertDictEqual(
sent_data['artifacts'], {
'artifact': {
'filePath': '/tmp/foo'
},
'artifact-1': {
'filePath': '/tmp/bar'
}
})
def test_summary_html(self):
tr = test_results.TestResult(test_name='test-name')
tr.artifacts.AddArtifact('stderr', '/tmp/stderr', False)
tr.artifacts.AddArtifact('crash_log', '/tmp/crash_log', False)
tr.artifacts.AddArtifact('command', '/tmp/cmd', False)
sent_data = self.sink(True, tr)
p = re.compile(
'<text-artifact artifact-id="(command|stderr|crash_log)" />')
self.assertListEqual(
p.findall(sent_data['summaryHtml']),
# The artifact tags should be sorted by the artifact names.
['command', 'crash_log', 'stderr'],
)
def assertFilename(self, test_name, expected_filename):
sent_data = self.sink(True, test_results.TestResult(test_name))
self.assertEqual(sent_data['testMetadata']['location']['fileName'],
'//' + RELATIVE_WEB_TESTS + expected_filename)
def test_location_filename(self):
self.assertFilename('real/test.html', 'real/test.html')
# TestPort.virtual_test_suites() has a set of hard-coded virtualized
# tests, and a test name must start with one of the virtual prefixes
# and base in order for it to be recognized as a virtual test.
self.assertFilename(
'virtual/virtual_passes/passes/does_not_exist.html',
'passes/does_not_exist.html')
self.port.host.filesystem.write_text_file(
self.port.host.filesystem.join(WEB_TEST_DIR, 'virtual',
'virtual_passes', 'passes',
'exists.html'),
'body',
)
self.assertFilename('virtual/virtual_passes/passes/exists.html',
'virtual/virtual_passes/passes/exists.html')
def test_wpt_location_filename(self):
add_manifest_to_mock_filesystem(self.port)
self.assertFilename(
'external/wpt/html/parse.html?run_type=uri',
'external/wpt/html/parse.html',
)
self.assertFilename(
'virtual/virtual_wpt/external/wpt/dom/ranges/Range-attributes.html',
'external/wpt/dom/ranges/Range-attributes.html',
)