def testNoSymbolsExtracted(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='x86_64',
                                             bitness='64',
                                             version_code='358923',
                                             modules=None)
    match_arch_folder = 'x86_64'
    symbol_fetcher._FetchGCSFile = mock.Mock(
        side_effect=self._mockVersionCodeFetcher(match_arch_folder, metadata))
    # Don't run |self._setUpBasicRunDumpSyms()| because we want no symbols
    # to be extracted.

    exception = (
        'No breakpad symbols could be extracted from files in the subtree: ' +
        self.breakpad_output_dir)
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir,
                                             self.dump_syms_path)
    self.assertIn(exception, str(e.exception))
    expected_calls = self._getExpectedSymbolFileFetches(match_arch_folder,
                                                        metadata)
    symbol_fetcher._FetchAndUnzipGCSFile.assert_has_calls(expected_calls,
                                                          any_order=True)

    self._ensureRunDumpSymsAndRenameNotCalled()
  def testFailsToFetchSymbolFiles(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='armv7',
                                             bitness='64',
                                             version_code='358923',
                                             modules=None)
    match_arch_folder = 'next-arm_64'
    symbol_fetcher._FetchGCSFile = mock.Mock(
        side_effect=self._mockVersionCodeFetcher(match_arch_folder, metadata))
    # Fails to fetch all symbol files from GCS.
    symbol_fetcher._FetchAndUnzipGCSFile = mock.MagicMock(return_value=False)
    self._setUpBasicRunDumpSyms()

    gcs_folder = ('android-B0urB0N/' + metadata.version_number + '/' +
                  match_arch_folder)
    exception_msg = 'No symbol files could be found on GCS:' + gcs_folder
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir,
                                             self.dump_syms_path)
    self.assertIn(exception_msg, str(e.exception))

    # Expect to try to download all symbol files.
    expected_calls = self._getExpectedSymbolFileFetches(match_arch_folder,
                                                        metadata)
    symbol_fetcher._FetchAndUnzipGCSFile.assert_has_calls(expected_calls,
                                                          any_order=True)

    self._ensureRunDumpSymsAndRenameNotCalled()
  def testNoOSName(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=None,
                                             architecture='x86_64',
                                             bitness='64')

    exception_msg = 'Failed to extract trace OS name: ' + self.trace_file
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir)
    self.assertIn(exception_msg, str(e.exception))
  def testOSNameNotRecognized(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name='blah',
                                             architecture='x86_64',
                                             bitness='64')

    exception_msg = 'Trace OS "blah" is not supported: ' + self.trace_file
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir)
    self.assertIn(exception_msg, str(e.exception))
  def testLinux32BitFailure(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.LINUX,
                                             architecture='x86_64',
                                             bitness='32')

    with self.assertRaises(ValueError):
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir)

    cloud_storage.Exists.assert_not_called()
    cloud_storage.Get.assert_not_called()
  def testLinuxFetchFailure(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.LINUX,
                                             architecture='x86_64',
                                             bitness='64')
    # Cloud storage does not have |gcs_file| or |gcs_file|.zip extension.
    cloud_storage.Exists.side_effect = [False, False]

    with self.assertRaises(Exception):
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir)
    self.assertEqual(cloud_storage.Exists.call_count, 2)
    cloud_storage.Get.assert_not_called()
  def testMacNoArchitecture(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.MAC,
                                             architecture=None,
                                             bitness='64')

    symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket, metadata,
                                           self.breakpad_output_dir)

    cloud_storage.Get.assert_called_once_with(
        self.cloud_storage_bucket, 'desktop-*/123/mac64/breakpad-info.zip',
        self.breakpad_zip_file)
    cloud_storage.Exists.assert_called_once()

    self._ensureUnzipAndRenameCalls()
  def testMissingVersionCode(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='x86_64',
                                             bitness='64',
                                             version_code=None,
                                             modules=None)
    match_arch_folder = 'x86_64'
    symbol_fetcher._FetchGCSFile = mock.Mock(
        side_effect=self._mockVersionCodeFetcher(match_arch_folder, metadata))
    self._setUpBasicRunDumpSyms()

    exception_msg = 'Failed to extract version code: ' + self.trace_file
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir,
                                             self.dump_syms_path)
    self.assertIn(exception_msg, str(e.exception))
    self._ensureRunDumpSymsAndRenameNotCalled()
  def testMacNotZip(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.MAC,
                                             architecture='x86_64',
                                             bitness='64')
    # Cloud storage has |gcs_file| without a .zip extension.
    cloud_storage.Exists.side_effect = [False, True]

    symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket, metadata,
                                           self.breakpad_output_dir)

    cloud_storage.Get.assert_called_once_with(
        self.cloud_storage_bucket, 'desktop-*/123/mac64/breakpad-info',
        self.breakpad_zip_file)
    # |cloud_storage.Exists| called with |gcs_file|.zip, and with |gcs_file|
    # without the .zip extension.
    self.assertEqual(cloud_storage.Exists.call_count, 2)

    self._ensureUnzipAndRenameCalls()
  def testFailsToFetchAllVersionCodesFiles(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='x86_64',
                                             bitness='64',
                                             version_code='358923',
                                             modules=None)

    # Fails to fetch all 'version_codes.txt' files from GCS.
    symbol_fetcher._FetchGCSFile = mock.MagicMock(return_value=False)
    self._setUpBasicRunDumpSyms()

    exception_msg = ('Failed to determine architecture folder: ' +
                     self.trace_file)
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir,
                                             self.dump_syms_path)
    self.assertIn(exception_msg, str(e.exception))
    self._ensureRunDumpSymsAndRenameNotCalled()
  def testNoFilesMatchTraceVersionCode(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='x86_64',
                                             bitness='64',
                                             version_code='358923',
                                             modules=None)
    # None of the 'version_codes.txt' files match the trace's version code.
    match_arch_folder = None  # No valid paths can be None.
    symbol_fetcher._FetchGCSFile = mock.Mock(
        side_effect=self._mockVersionCodeFetcher(match_arch_folder, metadata))
    self._setUpBasicRunDumpSyms()

    exception_msg = ('Failed to determine architecture folder: ' +
                     self.trace_file)
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir,
                                             self.dump_syms_path)
    self.assertIn(exception_msg, str(e.exception))
    self._ensureRunDumpSymsAndRenameNotCalled()
  def testMissingDumpsymsPath(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='x86_64',
                                             bitness='64',
                                             version_code='328954',
                                             modules=None)
    match_arch_folder = 'x86_64'
    symbol_fetcher._FetchGCSFile = mock.Mock(
        side_effect=self._mockVersionCodeFetcher(match_arch_folder, metadata))
    self._setUpBasicRunDumpSyms()

    exception_msg = ('Path to dump_syms binary is required for symbolizing '
                     'official Android traces.')
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata,
                                             self.breakpad_output_dir,
                                             dump_syms_path=None)
    self.assertIn(exception_msg, str(e.exception))
    self._ensureRunDumpSymsAndRenameNotCalled()
  def testCrossArchitecture(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='x86_64',
                                             bitness='64',
                                             version_code='358923',
                                             modules=None)
    match_arch_folder = 'next-x86'
    symbol_fetcher._FetchGCSFile = mock.Mock(
        side_effect=self._mockVersionCodeFetcher(match_arch_folder, metadata))
    extract_files = self._setUpBasicRunDumpSyms()

    symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket, metadata,
                                           self.breakpad_output_dir,
                                           self.dump_syms_path)

    expected_calls = self._getExpectedSymbolFileFetches(match_arch_folder,
                                                        metadata)
    symbol_fetcher._FetchAndUnzipGCSFile.assert_has_calls(expected_calls,
                                                          any_order=True)
    self._ensureRunDumpSymsAndRenameCalls(extract_files)
  def testIgnoreInvalidSymbolFiles(self):
    metadata = self._createMetadataExtractor(version_number='123',
                                             os_name=OSName.ANDROID,
                                             architecture='x86_64',
                                             bitness='64',
                                             version_code='358923',
                                             modules=None)
    match_arch_folder = 'x86_64'
    symbol_fetcher._FetchGCSFile = mock.Mock(
        side_effect=self._mockVersionCodeFetcher(match_arch_folder, metadata))

    # Setup invalid symbol files. No breakpad files should be extracted.
    zip_file = os.path.join(self.subdir1, 'monochrome_symbols.zip')
    apk_file = os.path.join(self.subdir2, 'chrome_apk')
    dwp_file = os.path.join(self.unstripped_dir, 'chrome.so.dwp')
    dwo_file = os.path.join(self.release, 'name.so.dwo')
    version_file = os.path.join(self.breakpad_output_dir, 'version_codes.txt')

    unextract_files = {zip_file, apk_file, dwp_file, dwo_file, version_file}
    for new_file in unextract_files:
      with open(new_file, 'w') as f:
        f.write('MODULE mac x86_64 329FDEA987BC name.so')

    exception_msg = (
        'No breakpad symbols could be extracted from files in the subtree: ' +
        self.breakpad_output_dir)
    with self.assertRaises(Exception) as e:
      symbol_fetcher.GetTraceBreakpadSymbols(self.cloud_storage_bucket,
                                             metadata, self.breakpad_output_dir,
                                             self.dump_syms_path)
    self.assertIn(exception_msg, str(e.exception))
    expected_calls = self._getExpectedSymbolFileFetches(match_arch_folder,
                                                        metadata)
    symbol_fetcher._FetchAndUnzipGCSFile.assert_has_calls(expected_calls,
                                                          any_order=True)

    # There should be no extracted files.
    self._ensureRunDumpSymsAndRenameNotCalled()