| # 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. |
| """Tests for upload_test_result_artifacts.""" |
| |
| import json |
| import mock |
| import os |
| import random |
| import string |
| import tempfile |
| import unittest |
| |
| import upload_test_result_artifacts |
| |
| |
| class UploadTestResultArtifactsTest(unittest.TestCase): |
| def setUp(self): |
| # Used for load tests |
| self._temp_files = [] |
| |
| def tearDown(self): |
| # Used for load tests |
| for fname in self._temp_files: |
| os.unlink(fname) |
| |
| ### These are load tests useful for seeing how long it takes to upload |
| ### different kinds of test results files. They won't be run as part of |
| ### presubmit testing, since they take a while and talk to the network, |
| ### but the code will stay here in case anyone wants to edit the code |
| ### and wants to check performance. Change the test names from 'loadTestBlah' |
| ### to 'testBlah' to get them to run. |
| |
| def makeTemp(self, size): |
| _, fname = tempfile.mkstemp() |
| with open(fname, 'w') as f: |
| f.write(random.choice(string.ascii_letters) * size) |
| self._temp_files.append(fname) |
| |
| return os.path.basename(fname) |
| |
| def makeTestJson(self, num_tests, artifact_size): |
| return { |
| 'tests': { |
| 'suite': { |
| 'test%d' % i: { |
| 'artifacts': { |
| 'artifact': self.makeTemp(artifact_size), |
| }, |
| 'expected': 'PASS', |
| 'actual': 'PASS', |
| } for i in range(num_tests) |
| } |
| }, |
| 'artifact_type_info': { |
| 'artifact': 'text/plain' |
| } |
| } |
| |
| def _loadTest(self, json_data, upload): |
| return upload_test_result_artifacts.upload_artifacts( |
| json_data, '/tmp', upload, 'test-bucket') |
| |
| |
| def loadTestEndToEndSimple(self): |
| test_data = self.makeTestJson(1, 10) |
| print self._loadTest(test_data, False) |
| |
| def loadTestEndToEndManySmall(self): |
| test_data = self.makeTestJson(1000, 10) |
| self._loadTest(test_data, False) |
| |
| def loadTestEndToEndSomeBig(self): |
| test_data = self.makeTestJson(100, 10000000) |
| self._loadTest(test_data, False) |
| |
| def loadTestEndToEndVeryBig(self): |
| test_data = self.makeTestJson(2, 1000000000) |
| self._loadTest(test_data, False) |
| |
| ### End load test section. |
| |
| def testGetTestsSimple(self): |
| self.assertEqual(upload_test_result_artifacts.get_tests({ |
| 'foo': { |
| 'expected': 'PASS', |
| 'actual': 'PASS', |
| }, |
| }), { |
| ('foo',): { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| } |
| }) |
| |
| def testGetTestsNested(self): |
| self.assertEqual(upload_test_result_artifacts.get_tests({ |
| 'foo': { |
| 'bar': { |
| 'baz': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| }, |
| 'bam': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| }, |
| }, |
| }, |
| }), { |
| ('foo', 'bar', 'baz'): { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| }, |
| ('foo', 'bar', 'bam'): { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| } |
| }) |
| |
| def testGetTestsError(self): |
| with self.assertRaises(ValueError): |
| upload_test_result_artifacts.get_tests([]) |
| |
| def testUploadArtifactsMissingType(self): |
| """Tests that the type information is used for validation.""" |
| data = { |
| 'artifact_type_info': { |
| 'log': 'text/plain' |
| }, |
| 'tests': { |
| 'foo': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| 'artifacts': { |
| 'screenshot': 'foo.png', |
| } |
| } |
| } |
| } |
| with self.assertRaises(ValueError): |
| upload_test_result_artifacts.upload_artifacts( |
| data, '/tmp', True, 'test-bucket') |
| |
| @mock.patch('upload_test_result_artifacts.get_file_digest') |
| @mock.patch('upload_test_result_artifacts.tempfile.mkdtemp') |
| @mock.patch('upload_test_result_artifacts.shutil.rmtree') |
| @mock.patch('upload_test_result_artifacts.shutil.copyfile') |
| def testUploadArtifactsNoUpload( |
| self, copy_patch, rmtree_patch, mkd_patch, digest_patch): |
| """Simple test; no artifacts, so data shouldn't change.""" |
| mkd_patch.return_value = 'foo_dir' |
| data = { |
| 'artifact_type_info': { |
| 'log': 'text/plain' |
| }, |
| 'tests': { |
| 'foo': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| } |
| } |
| } |
| self.assertEqual(upload_test_result_artifacts.upload_artifacts( |
| data, '/tmp', True, 'test-bucket'), data) |
| mkd_patch.assert_called_once_with(prefix='upload_test_artifacts') |
| digest_patch.assert_not_called() |
| copy_patch.assert_not_called() |
| rmtree_patch.assert_called_once_with('foo_dir') |
| |
| @mock.patch('upload_test_result_artifacts.get_file_digest') |
| @mock.patch('upload_test_result_artifacts.tempfile.mkdtemp') |
| @mock.patch('upload_test_result_artifacts.shutil.rmtree') |
| @mock.patch('upload_test_result_artifacts.shutil.copyfile') |
| @mock.patch('upload_test_result_artifacts.os.path.exists') |
| def testUploadArtifactsBasic( |
| self, exists_patch, copy_patch, rmtree_patch, mkd_patch, digest_patch): |
| """Upload a single artifact.""" |
| mkd_patch.return_value = 'foo_dir' |
| exists_patch.return_value = False |
| digest_patch.return_value = 'deadbeef' |
| |
| data = { |
| 'artifact_type_info': { |
| 'log': 'text/plain' |
| }, |
| 'tests': { |
| 'foo': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| 'artifacts': { |
| 'log': 'foo.txt', |
| } |
| } |
| } |
| } |
| self.assertEqual(upload_test_result_artifacts.upload_artifacts( |
| data, '/tmp', True, 'test-bucket'), { |
| 'artifact_type_info': { |
| 'log': 'text/plain' |
| }, |
| 'tests': { |
| 'foo': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| 'artifacts': { |
| 'log': 'deadbeef', |
| } |
| } |
| }, |
| 'artifact_permanent_location': 'gs://chromium-test-artifacts/sha1', |
| }) |
| mkd_patch.assert_called_once_with(prefix='upload_test_artifacts') |
| digest_patch.assert_called_once_with('/tmp/foo.txt') |
| copy_patch.assert_called_once_with('/tmp/foo.txt', 'foo_dir/deadbeef') |
| rmtree_patch.assert_called_once_with('foo_dir') |
| |
| @mock.patch('upload_test_result_artifacts.get_file_digest') |
| @mock.patch('upload_test_result_artifacts.tempfile.mkdtemp') |
| @mock.patch('upload_test_result_artifacts.shutil.rmtree') |
| @mock.patch('upload_test_result_artifacts.shutil.copyfile') |
| @mock.patch('upload_test_result_artifacts.os.path.exists') |
| def testUploadArtifactsComplex( |
| self, exists_patch, copy_patch, rmtree_patch, mkd_patch, digest_patch): |
| """Upload multiple artifacts.""" |
| mkd_patch.return_value = 'foo_dir' |
| exists_patch.return_value = False |
| digest_patch.side_effect = [ |
| 'deadbeef1', 'deadbeef2', 'deadbeef3', 'deadbeef4'] |
| |
| data = { |
| 'artifact_type_info': { |
| 'log': 'text/plain', |
| 'screenshot': 'image/png', |
| }, |
| 'tests': { |
| 'bar': { |
| 'baz': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| 'artifacts': { |
| 'log': 'baz.log.txt', |
| 'screenshot': 'baz.png', |
| } |
| } |
| }, |
| 'foo': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| 'artifacts': { |
| 'log': 'foo.log.txt', |
| 'screenshot': 'foo.png', |
| } |
| }, |
| } |
| } |
| self.assertEqual(upload_test_result_artifacts.upload_artifacts( |
| data, '/tmp', True, 'test-bucket'), { |
| 'artifact_type_info': { |
| 'log': 'text/plain', |
| 'screenshot': 'image/png', |
| }, |
| 'tests': { |
| 'bar': { |
| 'baz': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| 'artifacts': { |
| 'log': 'deadbeef1', |
| 'screenshot': 'deadbeef2', |
| } |
| } |
| }, |
| 'foo': { |
| 'actual': 'PASS', |
| 'expected': 'PASS', |
| 'artifacts': { |
| 'log': 'deadbeef3', |
| 'screenshot': 'deadbeef4', |
| } |
| }, |
| }, |
| 'artifact_permanent_location': 'gs://chromium-test-artifacts/sha1', |
| }) |
| mkd_patch.assert_called_once_with(prefix='upload_test_artifacts') |
| digest_patch.assert_has_calls([ |
| mock.call('/tmp/baz.log.txt'), mock.call('/tmp/baz.png'), |
| mock.call('/tmp/foo.log.txt'), mock.call('/tmp/foo.png')]) |
| copy_patch.assert_has_calls([ |
| mock.call('/tmp/baz.log.txt', 'foo_dir/deadbeef1'), |
| mock.call('/tmp/baz.png', 'foo_dir/deadbeef2'), |
| mock.call('/tmp/foo.log.txt', 'foo_dir/deadbeef3'), |
| mock.call('/tmp/foo.png', 'foo_dir/deadbeef4'), |
| ]) |
| rmtree_patch.assert_called_once_with('foo_dir') |
| |
| def testFileDigest(self): |
| _, path = tempfile.mkstemp(prefix='file_digest_test') |
| with open(path, 'w') as f: |
| f.write('a') |
| |
| self.assertEqual( |
| upload_test_result_artifacts.get_file_digest(path), |
| '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8') |
| |
| if __name__ == '__main__': |
| unittest.main() |