relesetools: Enable releasetools_test in presubmit.

About half of the testcases rely on external tools (i.e. the ones in
`otatools.zip`, which are external to releasetools module, but still
built by Android). It's WAI as releasetools scripts are mostly for
gluing purpose.

However, the current support in Soong doesn't allow packing the helper
modules as part of the built releasetools_test. This CL adds a decorator
that allows declaring external dependencies in testcases, which will be
skipped while running in presubmit. It doesn't affect local invocation
of `atest releasetools_test`.

Fixes: 112080715
Test: `atest releasetools_test`
Test: TreeHugger; check that releasetools_test is invoked (and test
      passes).
Change-Id: I8fdeb6549023cf5ddeb79d610c7c37cf9f13d3cc
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 89add40..9b76734 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -315,6 +315,7 @@
     finally:
       os.remove(zip_file_name)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_ZipDelete(self):
     zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
     output_zip = zipfile.ZipFile(zip_file.name, 'w',
@@ -376,6 +377,7 @@
     common.ZipClose(output_zip)
     return zip_file
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_UnzipTemp(self):
     zip_file = self._test_UnzipTemp_createZipFile()
     unzipped_dir = common.UnzipTemp(zip_file)
@@ -385,6 +387,7 @@
     self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
     self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_UnzipTemp_withPatterns(self):
     zip_file = self._test_UnzipTemp_createZipFile()
 
@@ -425,6 +428,7 @@
     self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
     self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_UnzipTemp_withPartiallyMatchingPatterns(self):
     zip_file = self._test_UnzipTemp_createZipFile()
     unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
@@ -575,6 +579,7 @@
     wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
     self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_ExtractAvbPublicKey(self):
     privkey = os.path.join(self.testdata_dir, 'testkey.key')
     pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
@@ -594,18 +599,22 @@
       actual = common.ParseCertificate(cert_fp.read())
     self.assertEqual(expected, actual)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetMinSdkVersion(self):
     test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
     self.assertEqual('24', common.GetMinSdkVersion(test_app))
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetMinSdkVersion_invalidInput(self):
     self.assertRaises(
         common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetMinSdkVersionInt(self):
     test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
     self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetMinSdkVersionInt_invalidInput(self):
     self.assertRaises(
         common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
@@ -617,6 +626,7 @@
   def setUp(self):
     self.testdata_dir = test_utils.get_testdata_dir()
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_emptyBlockMapFile(self):
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
     with zipfile.ZipFile(target_files, 'w') as target_files_zip:
@@ -649,6 +659,7 @@
         AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
         None, False)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_missingBlockMapFile(self):
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
     with zipfile.ZipFile(target_files, 'w') as target_files_zip:
@@ -667,6 +678,7 @@
           AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
           False)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_sharedBlocks_notAllowed(self):
     """Tests the case of having overlapping blocks but disallowed."""
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
@@ -689,6 +701,7 @@
           AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
           False)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_sharedBlocks_allowed(self):
     """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
@@ -731,6 +744,7 @@
     self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
     self.assertFalse(sparse_image.file_map['/system/file1'].extra)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_incompleteRanges(self):
     """Tests the case of ext4 images with holes."""
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
@@ -754,6 +768,7 @@
     self.assertFalse(sparse_image.file_map['/system/file1'].extra)
     self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
     with zipfile.ZipFile(target_files, 'w') as target_files_zip:
@@ -781,6 +796,7 @@
     self.assertTrue(
         sparse_image.file_map['/system/app/file3'].extra['incomplete'])
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
     with zipfile.ZipFile(target_files, 'w') as target_files_zip:
@@ -803,6 +819,7 @@
     self.assertFalse(sparse_image.file_map['//system/file1'].extra)
     self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetSparseImage_fileNotFound(self):
     target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
     with zipfile.ZipFile(target_files, 'w') as target_files_zip:
@@ -822,6 +839,7 @@
           AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
           False)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetAvbChainedPartitionArg(self):
     pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
     info_dict = {
@@ -835,6 +853,7 @@
     self.assertEqual('2', args[1])
     self.assertTrue(os.path.exists(args[2]))
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetAvbChainedPartitionArg_withPrivateKey(self):
     key = os.path.join(self.testdata_dir, 'testkey.key')
     info_dict = {
@@ -848,6 +867,7 @@
     self.assertEqual('2', args[1])
     self.assertTrue(os.path.exists(args[2]))
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
     info_dict = {
         'avb_avbtool': 'avbtool',
@@ -862,6 +882,7 @@
     self.assertEqual('2', args[1])
     self.assertTrue(os.path.exists(args[2]))
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetAvbChainedPartitionArg_invalidKey(self):
     pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
     info_dict = {
@@ -922,6 +943,7 @@
       self.assertIn('/', loaded_dict['fstab'])
       self.assertIn('/system', loaded_dict['fstab'])
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_LoadInfoDict_dirInput(self):
     target_files = self._test_LoadInfoDict_createTargetFiles(
         self.INFO_DICT_DEFAULT,
@@ -933,6 +955,7 @@
     self.assertIn('/', loaded_dict['fstab'])
     self.assertIn('/system', loaded_dict['fstab'])
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
     target_files = self._test_LoadInfoDict_createTargetFiles(
         self.INFO_DICT_DEFAULT,
@@ -990,6 +1013,7 @@
       self.assertEqual(2, loaded_dict['fstab_version'])
       self.assertIsNone(loaded_dict['fstab'])
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
     target_files = self._test_LoadInfoDict_createTargetFiles(
         self.INFO_DICT_DEFAULT,
@@ -998,6 +1022,7 @@
     with zipfile.ZipFile(target_files, 'r') as target_files_zip:
       self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_LoadInfoDict_repacking(self):
     target_files = self._test_LoadInfoDict_createTargetFiles(
         self.INFO_DICT_DEFAULT,
@@ -1066,6 +1091,7 @@
     validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
                                                         self._info)
 
+  @test_utils.SkipIfExternalToolsUnavailable()
   def test_recovery_from_boot(self):
     recovery_image = common.File("recovery.img", self.recovery_data)
     self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")