chromium/tools/perf/core/results_processor/command_line_unittest.py

# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Unit tests for results_processor.

These tests mostly test that argument parsing and processing work as expected.
They mock out accesses to the operating system, so no files are actually read
nor written.
"""

import datetime
import posixpath
import re
import unittest
from unittest import mock

from core.results_processor import command_line


# To easily mock module level symbols within the command_line module.
def module(symbol):
  return 'core.results_processor.command_line.' + symbol


class ProcessOptionsTestCase(unittest.TestCase):
  def setUp(self):
    self.standalone = False

    # Mock os module within results_processor so path manipulations do not
    # depend on the file system of the test environment.
    mock_os = mock.patch(module('os')).start()

    def realpath(path):
      return posixpath.normpath(posixpath.join(mock_os.getcwd(), path))

    def expanduser(path):
      return re.sub(r'~', '/path/to/home', path)

    mock_os.getcwd.return_value = '/path/to/curdir'
    mock_os.path.realpath.side_effect = realpath
    mock_os.path.expanduser.side_effect = expanduser
    mock_os.path.dirname.side_effect = posixpath.dirname
    mock_os.path.join.side_effect = posixpath.join
    mock.patch(module('_DefaultOutputDir'),
               return_value='/path/to/output_dir').start()

  def tearDown(self):
    mock.patch.stopall()

  def ParseArgs(self, args):
    parser = command_line.ArgumentParser(standalone=self.standalone)
    options = parser.parse_args(args)
    command_line.ProcessOptions(options)
    return options


class TestProcessOptions(ProcessOptionsTestCase):
  def testOutputDir_default(self):
    options = self.ParseArgs([])
    self.assertEqual(options.output_dir, '/path/to/output_dir')

  def testOutputDir_homeDir(self):
    options = self.ParseArgs(['--output-dir', '~/my_outputs'])
    self.assertEqual(options.output_dir, '/path/to/home/my_outputs')

  def testOutputDir_relPath(self):
    options = self.ParseArgs(['--output-dir', 'my_outputs'])
    self.assertEqual(options.output_dir, '/path/to/curdir/my_outputs')

  def testOutputDir_absPath(self):
    options = self.ParseArgs(['--output-dir', '/path/to/somewhere/else'])
    self.assertEqual(options.output_dir, '/path/to/somewhere/else')

  @mock.patch(module('datetime'))
  def testIntermediateDir_default(self, mock_datetime):
    mock_datetime.datetime.utcnow.return_value = (
        datetime.datetime(2015, 10, 21, 7, 28))
    options = self.ParseArgs(['--output-dir', '/output'])
    self.assertEqual(options.intermediate_dir,
                     '/output/artifacts/run_20151021T072800Z')

  @mock.patch(module('datetime'))
  def testIntermediateDir_withResultsLabel(self, mock_datetime):
    mock_datetime.datetime.utcnow.return_value = (
        datetime.datetime(2015, 10, 21, 7, 28))
    options = self.ParseArgs(
        ['--output-dir', '/output', '--results-label', 'test my feature'])
    self.assertEqual(options.intermediate_dir,
                     '/output/artifacts/test_my_feature_20151021T072800Z')

  def testUploadBucket_noUploadResults(self):
    options = self.ParseArgs([])
    self.assertFalse(options.upload_results)
    self.assertIsNone(options.upload_bucket)

  @mock.patch(module('cloud_storage'))
  def testUploadBucket_uploadResultsToDefaultBucket(self, mock_storage):
    mock_storage.BUCKET_ALIASES = {'output': 'default-bucket'}
    options = self.ParseArgs(['--upload-results'])
    self.assertTrue(options.upload_results)
    self.assertEqual(options.upload_bucket, 'default-bucket')

  @mock.patch(module('cloud_storage'))
  def testUploadBucket_uploadResultsToBucket(self, mock_storage):
    mock_storage.BUCKET_ALIASES = {'output': 'default-bucket'}
    options = self.ParseArgs(
        ['--upload-results', '--upload-bucket', 'my_bucket'])
    self.assertTrue(options.upload_results)
    self.assertEqual(options.upload_bucket, 'my_bucket')

  @mock.patch(module('cloud_storage'))
  def testUploadBucket_uploadResultsToAlias(self, mock_storage):
    mock_storage.BUCKET_ALIASES = {
        'output': 'default-bucket', 'special': 'some-special-bucket'}
    options = self.ParseArgs(
        ['--upload-results', '--upload-bucket', 'special'])
    self.assertTrue(options.upload_results)
    self.assertEqual(options.upload_bucket, 'some-special-bucket')

  def testDefaultOutputFormat(self):
    options = self.ParseArgs([])
    self.assertEqual(options.output_formats, ['html'])

  def testUnkownOutputFormatRaises(self):
    with self.assertRaises(SystemExit):
      self.ParseArgs(['--output-format', 'unknown'])

  def testNoDuplicateOutputFormats(self):
    options = self.ParseArgs(
        ['--output-format', 'html', '--output-format', 'csv',
         '--output-format', 'html', '--output-format', 'csv'])
    self.assertEqual(options.output_formats, ['csv', 'html'])


class StandaloneTestProcessOptions(ProcessOptionsTestCase):
  def setUp(self):
    super(StandaloneTestProcessOptions, self).setUp()
    self.standalone = True

  def testOutputFormatRequired(self):
    with self.assertRaises(SystemExit):
      self.ParseArgs([])

  def testIntermediateDirRequired(self):
    with self.assertRaises(SystemExit):
      self.ParseArgs(['--output-format', 'json-test-results'])

  def testSuccessful(self):
    options = self.ParseArgs(
        ['--output-format', 'json-test-results',
         '--intermediate-dir', 'some_dir'])
    self.assertEqual(options.output_formats, ['json-test-results'])
    self.assertEqual(options.intermediate_dir, '/path/to/curdir/some_dir')
    self.assertEqual(options.output_dir, '/path/to/output_dir')