xref: /aosp_15_r20/system/apex/tools/apex_compression_test.py (revision 33f3758387333dbd2962d7edbd98681940d895da)
1*33f37583SAndroid Build Coastguard Worker#!/usr/bin/env python
2*33f37583SAndroid Build Coastguard Worker#
3*33f37583SAndroid Build Coastguard Worker# Copyright (C) 2020 The Android Open Source Project
4*33f37583SAndroid Build Coastguard Worker#
5*33f37583SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*33f37583SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*33f37583SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*33f37583SAndroid Build Coastguard Worker#
9*33f37583SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*33f37583SAndroid Build Coastguard Worker#
11*33f37583SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*33f37583SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*33f37583SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*33f37583SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*33f37583SAndroid Build Coastguard Worker# limitations under the License.
16*33f37583SAndroid Build Coastguard Worker#
17*33f37583SAndroid Build Coastguard Worker"""Unit tests for apex_compression_tool."""
18*33f37583SAndroid Build Coastguard Workerimport hashlib
19*33f37583SAndroid Build Coastguard Workerimport logging
20*33f37583SAndroid Build Coastguard Workerimport os
21*33f37583SAndroid Build Coastguard Workerimport shutil
22*33f37583SAndroid Build Coastguard Workerimport stat
23*33f37583SAndroid Build Coastguard Workerimport subprocess
24*33f37583SAndroid Build Coastguard Workerimport tempfile
25*33f37583SAndroid Build Coastguard Workerimport unittest
26*33f37583SAndroid Build Coastguard Workerfrom importlib import resources
27*33f37583SAndroid Build Coastguard Workerfrom zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
28*33f37583SAndroid Build Coastguard Worker
29*33f37583SAndroid Build Coastguard Workerimport apex_manifest_pb2
30*33f37583SAndroid Build Coastguard Worker
31*33f37583SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
32*33f37583SAndroid Build Coastguard Worker
33*33f37583SAndroid Build Coastguard WorkerTEST_APEX = 'com.android.example.apex'
34*33f37583SAndroid Build Coastguard Worker
35*33f37583SAndroid Build Coastguard Worker# In order to debug test failures, set DEBUG_TEST to True and run the test from
36*33f37583SAndroid Build Coastguard Worker# local workstation bypassing atest, e.g.:
37*33f37583SAndroid Build Coastguard Worker# $ m apex_compression_tool_test && \
38*33f37583SAndroid Build Coastguard Worker#   out/host/linux-x86/nativetest64/apex_compression_tool_test/\
39*33f37583SAndroid Build Coastguard Worker#   apex_compression_tool_test
40*33f37583SAndroid Build Coastguard Worker#
41*33f37583SAndroid Build Coastguard Worker# the test will print out the command used, and the temporary files used by the
42*33f37583SAndroid Build Coastguard Worker# test.
43*33f37583SAndroid Build Coastguard WorkerDEBUG_TEST = False
44*33f37583SAndroid Build Coastguard Worker
45*33f37583SAndroid Build Coastguard Worker
46*33f37583SAndroid Build Coastguard Workerdef run(args, verbose=None, **kwargs):
47*33f37583SAndroid Build Coastguard Worker  """Creates and returns a subprocess.Popen object.
48*33f37583SAndroid Build Coastguard Worker
49*33f37583SAndroid Build Coastguard Worker  Args:
50*33f37583SAndroid Build Coastguard Worker    args: The command represented as a list of strings.
51*33f37583SAndroid Build Coastguard Worker    verbose: Whether the commands should be shown. Default to the global
52*33f37583SAndroid Build Coastguard Worker        verbosity if unspecified.
53*33f37583SAndroid Build Coastguard Worker    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
54*33f37583SAndroid Build Coastguard Worker        stdin, etc. stdout and stderr will default to subprocess.PIPE and
55*33f37583SAndroid Build Coastguard Worker        subprocess.STDOUT respectively unless caller specifies any of them.
56*33f37583SAndroid Build Coastguard Worker        universal_newlines will default to True, as most of the users in
57*33f37583SAndroid Build Coastguard Worker        releasetools expect string output.
58*33f37583SAndroid Build Coastguard Worker
59*33f37583SAndroid Build Coastguard Worker  Returns:
60*33f37583SAndroid Build Coastguard Worker    A subprocess.Popen object.
61*33f37583SAndroid Build Coastguard Worker  """
62*33f37583SAndroid Build Coastguard Worker  if 'stdout' not in kwargs and 'stderr' not in kwargs:
63*33f37583SAndroid Build Coastguard Worker    kwargs['stdout'] = subprocess.PIPE
64*33f37583SAndroid Build Coastguard Worker    kwargs['stderr'] = subprocess.STDOUT
65*33f37583SAndroid Build Coastguard Worker  if 'universal_newlines' not in kwargs:
66*33f37583SAndroid Build Coastguard Worker    kwargs['universal_newlines'] = True
67*33f37583SAndroid Build Coastguard Worker  if DEBUG_TEST:
68*33f37583SAndroid Build Coastguard Worker    print('\nRunning: \n%s\n' % ' '.join(args))
69*33f37583SAndroid Build Coastguard Worker  # Don't log any if caller explicitly says so.
70*33f37583SAndroid Build Coastguard Worker  if verbose:
71*33f37583SAndroid Build Coastguard Worker    logger.info('  Running: \'%s\'', ' '.join(args))
72*33f37583SAndroid Build Coastguard Worker  return subprocess.Popen(args, **kwargs)
73*33f37583SAndroid Build Coastguard Worker
74*33f37583SAndroid Build Coastguard Worker
75*33f37583SAndroid Build Coastguard Workerdef run_and_check_output(args, verbose=None, **kwargs):
76*33f37583SAndroid Build Coastguard Worker  """Runs the given command and returns the output.
77*33f37583SAndroid Build Coastguard Worker
78*33f37583SAndroid Build Coastguard Worker  Args:
79*33f37583SAndroid Build Coastguard Worker    args: The command represented as a list of strings.
80*33f37583SAndroid Build Coastguard Worker    verbose: Whether the commands should be shown. Default to the global
81*33f37583SAndroid Build Coastguard Worker        verbosity if unspecified.
82*33f37583SAndroid Build Coastguard Worker    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
83*33f37583SAndroid Build Coastguard Worker        stdin, etc. stdout and stderr will default to subprocess.PIPE and
84*33f37583SAndroid Build Coastguard Worker        subprocess.STDOUT respectively unless caller specifies any of them.
85*33f37583SAndroid Build Coastguard Worker
86*33f37583SAndroid Build Coastguard Worker  Returns:
87*33f37583SAndroid Build Coastguard Worker    The output string.
88*33f37583SAndroid Build Coastguard Worker
89*33f37583SAndroid Build Coastguard Worker  Raises:
90*33f37583SAndroid Build Coastguard Worker    ExternalError: On non-zero exit from the command.
91*33f37583SAndroid Build Coastguard Worker  """
92*33f37583SAndroid Build Coastguard Worker
93*33f37583SAndroid Build Coastguard Worker  proc = run(args, verbose=verbose, **kwargs)
94*33f37583SAndroid Build Coastguard Worker  output, _ = proc.communicate()
95*33f37583SAndroid Build Coastguard Worker  if output is None:
96*33f37583SAndroid Build Coastguard Worker    output = ''
97*33f37583SAndroid Build Coastguard Worker  # Don't log any if caller explicitly says so.
98*33f37583SAndroid Build Coastguard Worker  if verbose:
99*33f37583SAndroid Build Coastguard Worker    logger.info('%s', output.rstrip())
100*33f37583SAndroid Build Coastguard Worker  if proc.returncode != 0:
101*33f37583SAndroid Build Coastguard Worker    raise RuntimeError(
102*33f37583SAndroid Build Coastguard Worker        "Failed to run command '{}' (exit code {}):\n{}".format(
103*33f37583SAndroid Build Coastguard Worker            args, proc.returncode, output))
104*33f37583SAndroid Build Coastguard Worker  return output
105*33f37583SAndroid Build Coastguard Worker
106*33f37583SAndroid Build Coastguard Worker
107*33f37583SAndroid Build Coastguard Workerdef get_current_dir():
108*33f37583SAndroid Build Coastguard Worker  """Returns the current dir, relative to the script dir."""
109*33f37583SAndroid Build Coastguard Worker  # The script dir is the one we want, which could be different from pwd.
110*33f37583SAndroid Build Coastguard Worker  current_dir = os.path.dirname(os.path.realpath(__file__))
111*33f37583SAndroid Build Coastguard Worker  return current_dir
112*33f37583SAndroid Build Coastguard Worker
113*33f37583SAndroid Build Coastguard Worker
114*33f37583SAndroid Build Coastguard Workerdef get_sha1sum(file_path):
115*33f37583SAndroid Build Coastguard Worker  h = hashlib.sha256()
116*33f37583SAndroid Build Coastguard Worker
117*33f37583SAndroid Build Coastguard Worker  with open(file_path, 'rb') as file:
118*33f37583SAndroid Build Coastguard Worker    while True:
119*33f37583SAndroid Build Coastguard Worker      # Reading is buffered, so we can read smaller chunks.
120*33f37583SAndroid Build Coastguard Worker      chunk = file.read(h.block_size)
121*33f37583SAndroid Build Coastguard Worker      if not chunk:
122*33f37583SAndroid Build Coastguard Worker        break
123*33f37583SAndroid Build Coastguard Worker      h.update(chunk)
124*33f37583SAndroid Build Coastguard Worker
125*33f37583SAndroid Build Coastguard Worker  return h.hexdigest()
126*33f37583SAndroid Build Coastguard Worker
127*33f37583SAndroid Build Coastguard Worker
128*33f37583SAndroid Build Coastguard Workerclass ApexCompressionTest(unittest.TestCase):
129*33f37583SAndroid Build Coastguard Worker  def setUp(self):
130*33f37583SAndroid Build Coastguard Worker    self._to_cleanup = []
131*33f37583SAndroid Build Coastguard Worker    self._get_host_tools()
132*33f37583SAndroid Build Coastguard Worker
133*33f37583SAndroid Build Coastguard Worker  def tearDown(self):
134*33f37583SAndroid Build Coastguard Worker    if not DEBUG_TEST:
135*33f37583SAndroid Build Coastguard Worker      for i in self._to_cleanup:
136*33f37583SAndroid Build Coastguard Worker        if os.path.isdir(i):
137*33f37583SAndroid Build Coastguard Worker          shutil.rmtree(i, ignore_errors=True)
138*33f37583SAndroid Build Coastguard Worker        else:
139*33f37583SAndroid Build Coastguard Worker          os.remove(i)
140*33f37583SAndroid Build Coastguard Worker      del self._to_cleanup[:]
141*33f37583SAndroid Build Coastguard Worker    else:
142*33f37583SAndroid Build Coastguard Worker      print('Cleanup: ' + str(self._to_cleanup))
143*33f37583SAndroid Build Coastguard Worker
144*33f37583SAndroid Build Coastguard Worker  def _get_host_tools(self):
145*33f37583SAndroid Build Coastguard Worker    dir_name = tempfile.mkdtemp(prefix=self._testMethodName+"_host_tools_")
146*33f37583SAndroid Build Coastguard Worker    self._to_cleanup.append(dir_name)
147*33f37583SAndroid Build Coastguard Worker    for tool in ["avbtool", "conv_apex_manifest", "apex_compression_tool", "deapexer", "soong_zip"]:
148*33f37583SAndroid Build Coastguard Worker      with (
149*33f37583SAndroid Build Coastguard Worker        resources.files("apex_compression_test").joinpath(tool).open('rb') as tool_resource,
150*33f37583SAndroid Build Coastguard Worker        open(os.path.join(dir_name, tool), 'wb') as f
151*33f37583SAndroid Build Coastguard Worker      ):
152*33f37583SAndroid Build Coastguard Worker        shutil.copyfileobj(tool_resource, f)
153*33f37583SAndroid Build Coastguard Worker      os.chmod(os.path.join(dir_name, tool), stat.S_IRUSR | stat.S_IXUSR)
154*33f37583SAndroid Build Coastguard Worker    os.environ['APEX_COMPRESSION_TOOL_PATH'] = dir_name
155*33f37583SAndroid Build Coastguard Worker    path = dir_name
156*33f37583SAndroid Build Coastguard Worker    if "PATH" in os.environ:
157*33f37583SAndroid Build Coastguard Worker        path += ":" + os.environ["PATH"]
158*33f37583SAndroid Build Coastguard Worker    os.environ["PATH"] = path
159*33f37583SAndroid Build Coastguard Worker
160*33f37583SAndroid Build Coastguard Worker  def _get_test_apex(self):
161*33f37583SAndroid Build Coastguard Worker    tmpdir = tempfile.mkdtemp()
162*33f37583SAndroid Build Coastguard Worker    self._to_cleanup.append(tmpdir)
163*33f37583SAndroid Build Coastguard Worker    apexPath = os.path.join(tmpdir, TEST_APEX + '.apex')
164*33f37583SAndroid Build Coastguard Worker    with (
165*33f37583SAndroid Build Coastguard Worker      resources.files('apex_compression_test').joinpath(TEST_APEX + '.apex').open('rb') as f,
166*33f37583SAndroid Build Coastguard Worker      open(apexPath, 'wb') as f2,
167*33f37583SAndroid Build Coastguard Worker    ):
168*33f37583SAndroid Build Coastguard Worker      shutil.copyfileobj(f, f2)
169*33f37583SAndroid Build Coastguard Worker
170*33f37583SAndroid Build Coastguard Worker    return apexPath
171*33f37583SAndroid Build Coastguard Worker
172*33f37583SAndroid Build Coastguard Worker  def _get_container_files(self, apex_file_path):
173*33f37583SAndroid Build Coastguard Worker    dir_name = tempfile.mkdtemp(
174*33f37583SAndroid Build Coastguard Worker        prefix=self._testMethodName + '_container_files_')
175*33f37583SAndroid Build Coastguard Worker    self._to_cleanup.append(dir_name)
176*33f37583SAndroid Build Coastguard Worker    with ZipFile(apex_file_path, 'r') as zip_obj:
177*33f37583SAndroid Build Coastguard Worker      zip_obj.extractall(path=dir_name)
178*33f37583SAndroid Build Coastguard Worker    files = {}
179*33f37583SAndroid Build Coastguard Worker    for i in ['apex_manifest.json', 'apex_manifest.pb', 'apex_pubkey',
180*33f37583SAndroid Build Coastguard Worker              'apex_build_info.pb', 'apex_payload.img', 'apex_payload.zip',
181*33f37583SAndroid Build Coastguard Worker              'AndroidManifest.xml', 'original_apex']:
182*33f37583SAndroid Build Coastguard Worker      file_path = os.path.join(dir_name, i)
183*33f37583SAndroid Build Coastguard Worker      if os.path.exists(file_path):
184*33f37583SAndroid Build Coastguard Worker        files[i] = file_path
185*33f37583SAndroid Build Coastguard Worker
186*33f37583SAndroid Build Coastguard Worker    image_file = files.get('apex_payload.img', None)
187*33f37583SAndroid Build Coastguard Worker    if image_file is None:
188*33f37583SAndroid Build Coastguard Worker      image_file = files.get('apex_payload.zip', None)
189*33f37583SAndroid Build Coastguard Worker    else:
190*33f37583SAndroid Build Coastguard Worker      files['apex_payload'] = image_file
191*33f37583SAndroid Build Coastguard Worker      # Also retrieve the root digest of the image
192*33f37583SAndroid Build Coastguard Worker      avbtool_cmd = ['avbtool',
193*33f37583SAndroid Build Coastguard Worker        'print_partition_digests', '--image', files['apex_payload']]
194*33f37583SAndroid Build Coastguard Worker      # avbtool_cmd output has format "<name>: <value>"
195*33f37583SAndroid Build Coastguard Worker      files['digest'] = run_and_check_output(
196*33f37583SAndroid Build Coastguard Worker        avbtool_cmd, True).split(': ')[1].strip()
197*33f37583SAndroid Build Coastguard Worker
198*33f37583SAndroid Build Coastguard Worker    return files
199*33f37583SAndroid Build Coastguard Worker
200*33f37583SAndroid Build Coastguard Worker  def _get_manifest_string(self, manifest_path):
201*33f37583SAndroid Build Coastguard Worker    cmd = ['conv_apex_manifest']
202*33f37583SAndroid Build Coastguard Worker    cmd.extend([
203*33f37583SAndroid Build Coastguard Worker        'print',
204*33f37583SAndroid Build Coastguard Worker        manifest_path
205*33f37583SAndroid Build Coastguard Worker    ])
206*33f37583SAndroid Build Coastguard Worker    return run_and_check_output(cmd, 'True')
207*33f37583SAndroid Build Coastguard Worker
208*33f37583SAndroid Build Coastguard Worker  # Mutates the manifest located at |manifest_path|
209*33f37583SAndroid Build Coastguard Worker  def _unset_original_apex_digest(self, manifest_path):
210*33f37583SAndroid Build Coastguard Worker    # Open the protobuf
211*33f37583SAndroid Build Coastguard Worker    with open(manifest_path, 'rb') as f:
212*33f37583SAndroid Build Coastguard Worker      pb = apex_manifest_pb2.ApexManifest()
213*33f37583SAndroid Build Coastguard Worker      pb.ParseFromString(f.read())
214*33f37583SAndroid Build Coastguard Worker    pb.ClearField('capexMetadata')
215*33f37583SAndroid Build Coastguard Worker    with open(manifest_path, 'wb') as f:
216*33f37583SAndroid Build Coastguard Worker      f.write(pb.SerializeToString())
217*33f37583SAndroid Build Coastguard Worker
218*33f37583SAndroid Build Coastguard Worker  def _compress_apex(self, uncompressed_apex_fp):
219*33f37583SAndroid Build Coastguard Worker    """Returns file path to compressed APEX"""
220*33f37583SAndroid Build Coastguard Worker    fd, compressed_apex_fp = tempfile.mkstemp(
221*33f37583SAndroid Build Coastguard Worker        prefix=self._testMethodName + '_compressed_',
222*33f37583SAndroid Build Coastguard Worker        suffix='.capex')
223*33f37583SAndroid Build Coastguard Worker    os.close(fd)
224*33f37583SAndroid Build Coastguard Worker    self._to_cleanup.append(compressed_apex_fp)
225*33f37583SAndroid Build Coastguard Worker    run_and_check_output([
226*33f37583SAndroid Build Coastguard Worker        'apex_compression_tool',
227*33f37583SAndroid Build Coastguard Worker        'compress',
228*33f37583SAndroid Build Coastguard Worker        '--input', uncompressed_apex_fp,
229*33f37583SAndroid Build Coastguard Worker        '--output', compressed_apex_fp
230*33f37583SAndroid Build Coastguard Worker    ])
231*33f37583SAndroid Build Coastguard Worker    return compressed_apex_fp
232*33f37583SAndroid Build Coastguard Worker
233*33f37583SAndroid Build Coastguard Worker  def _decompress_apex(self, compressed_apex_fp):
234*33f37583SAndroid Build Coastguard Worker    """Returns file path to decompressed APEX"""
235*33f37583SAndroid Build Coastguard Worker    decompressed_apex_fp = tempfile. \
236*33f37583SAndroid Build Coastguard Worker      NamedTemporaryFile(prefix=self._testMethodName + '_decompressed_',
237*33f37583SAndroid Build Coastguard Worker                         suffix='.apex').name
238*33f37583SAndroid Build Coastguard Worker    # Use deapexer to decompress
239*33f37583SAndroid Build Coastguard Worker    cmd = ['deapexer']
240*33f37583SAndroid Build Coastguard Worker    cmd.extend([
241*33f37583SAndroid Build Coastguard Worker        'decompress',
242*33f37583SAndroid Build Coastguard Worker        '--input', compressed_apex_fp,
243*33f37583SAndroid Build Coastguard Worker        '--output', decompressed_apex_fp
244*33f37583SAndroid Build Coastguard Worker    ])
245*33f37583SAndroid Build Coastguard Worker    run_and_check_output(cmd, True)
246*33f37583SAndroid Build Coastguard Worker
247*33f37583SAndroid Build Coastguard Worker    self.assertTrue(os.path.exists(decompressed_apex_fp),
248*33f37583SAndroid Build Coastguard Worker                    'Decompressed APEX does not exist')
249*33f37583SAndroid Build Coastguard Worker    self._to_cleanup.append(decompressed_apex_fp)
250*33f37583SAndroid Build Coastguard Worker    return decompressed_apex_fp
251*33f37583SAndroid Build Coastguard Worker
252*33f37583SAndroid Build Coastguard Worker  def _get_type(self, apex_file_path):
253*33f37583SAndroid Build Coastguard Worker    cmd = ['deapexer', 'info', '--print-type', apex_file_path]
254*33f37583SAndroid Build Coastguard Worker    return run_and_check_output(cmd, True).strip()
255*33f37583SAndroid Build Coastguard Worker
256*33f37583SAndroid Build Coastguard Worker  def test_compression(self):
257*33f37583SAndroid Build Coastguard Worker    uncompressed_apex_fp = self._get_test_apex()
258*33f37583SAndroid Build Coastguard Worker    # TODO(samiul): try compressing a compressed APEX
259*33f37583SAndroid Build Coastguard Worker    compressed_apex_fp = self._compress_apex(uncompressed_apex_fp)
260*33f37583SAndroid Build Coastguard Worker
261*33f37583SAndroid Build Coastguard Worker    # Verify output file has been created and is smaller than input file
262*33f37583SAndroid Build Coastguard Worker    uncompressed_file_size = os.path.getsize(uncompressed_apex_fp)
263*33f37583SAndroid Build Coastguard Worker    compressed_file_size = os.path.getsize(compressed_apex_fp)
264*33f37583SAndroid Build Coastguard Worker    self.assertGreater(compressed_file_size, 0, 'Compressed APEX is empty')
265*33f37583SAndroid Build Coastguard Worker    self.assertLess(compressed_file_size, uncompressed_file_size,
266*33f37583SAndroid Build Coastguard Worker                    'Compressed APEX is not smaller than uncompressed APEX')
267*33f37583SAndroid Build Coastguard Worker
268*33f37583SAndroid Build Coastguard Worker    # Verify type of the apex is 'COMPRESSED'
269*33f37583SAndroid Build Coastguard Worker    self.assertEqual(self._get_type(compressed_apex_fp), 'COMPRESSED')
270*33f37583SAndroid Build Coastguard Worker
271*33f37583SAndroid Build Coastguard Worker    # Verify the contents of the compressed apex files
272*33f37583SAndroid Build Coastguard Worker    content_in_compressed_apex = self._get_container_files(compressed_apex_fp)
273*33f37583SAndroid Build Coastguard Worker    self.assertIsNotNone(content_in_compressed_apex['original_apex'])
274*33f37583SAndroid Build Coastguard Worker    content_in_uncompressed_apex = self._get_container_files(
275*33f37583SAndroid Build Coastguard Worker        uncompressed_apex_fp)
276*33f37583SAndroid Build Coastguard Worker    self.assertIsNotNone(content_in_uncompressed_apex['apex_payload'])
277*33f37583SAndroid Build Coastguard Worker    self.assertIsNotNone(content_in_uncompressed_apex['digest'])
278*33f37583SAndroid Build Coastguard Worker
279*33f37583SAndroid Build Coastguard Worker    # Verify that CAPEX manifest contains digest of original_apex
280*33f37583SAndroid Build Coastguard Worker    manifest_string = self._get_manifest_string(
281*33f37583SAndroid Build Coastguard Worker        content_in_compressed_apex['apex_manifest.pb'])
282*33f37583SAndroid Build Coastguard Worker    self.assertIn('originalApexDigest: "'
283*33f37583SAndroid Build Coastguard Worker         + content_in_uncompressed_apex['digest'] + '"', manifest_string)
284*33f37583SAndroid Build Coastguard Worker
285*33f37583SAndroid Build Coastguard Worker    for i in ['apex_manifest.json', 'apex_manifest.pb', 'apex_pubkey',
286*33f37583SAndroid Build Coastguard Worker              'apex_build_info.pb', 'AndroidManifest.xml']:
287*33f37583SAndroid Build Coastguard Worker      if i in content_in_uncompressed_apex:
288*33f37583SAndroid Build Coastguard Worker        if i == 'apex_manifest.pb':
289*33f37583SAndroid Build Coastguard Worker          # Get rid of originalApexDigest field, which should be the
290*33f37583SAndroid Build Coastguard Worker          # only difference
291*33f37583SAndroid Build Coastguard Worker          self._unset_original_apex_digest(content_in_compressed_apex[i])
292*33f37583SAndroid Build Coastguard Worker        self.assertEqual(get_sha1sum(content_in_compressed_apex[i]),
293*33f37583SAndroid Build Coastguard Worker                         get_sha1sum(content_in_uncompressed_apex[i]))
294*33f37583SAndroid Build Coastguard Worker
295*33f37583SAndroid Build Coastguard Worker  def test_decompression(self):
296*33f37583SAndroid Build Coastguard Worker    # setup: create compressed APEX
297*33f37583SAndroid Build Coastguard Worker    uncompressed_apex_fp = self._get_test_apex()
298*33f37583SAndroid Build Coastguard Worker    compressed_apex_fp = self._compress_apex(uncompressed_apex_fp)
299*33f37583SAndroid Build Coastguard Worker
300*33f37583SAndroid Build Coastguard Worker    # Decompress it
301*33f37583SAndroid Build Coastguard Worker    decompressed_apex_fp = self._decompress_apex(compressed_apex_fp)
302*33f37583SAndroid Build Coastguard Worker
303*33f37583SAndroid Build Coastguard Worker    # Verify type of the apex is 'UNCOMPRESSED'
304*33f37583SAndroid Build Coastguard Worker    self.assertEqual(self._get_type(decompressed_apex_fp), 'UNCOMPRESSED')
305*33f37583SAndroid Build Coastguard Worker
306*33f37583SAndroid Build Coastguard Worker    # Verify decompressed APEX is same as uncompressed APEX
307*33f37583SAndroid Build Coastguard Worker    self.assertEqual(get_sha1sum(uncompressed_apex_fp),
308*33f37583SAndroid Build Coastguard Worker                     get_sha1sum(decompressed_apex_fp),
309*33f37583SAndroid Build Coastguard Worker                     'Decompressed APEX is not same as uncompressed APEX')
310*33f37583SAndroid Build Coastguard Worker
311*33f37583SAndroid Build Coastguard Worker    # Try decompressing uncompressed APEX. It should not work.
312*33f37583SAndroid Build Coastguard Worker    with self.assertRaises(RuntimeError) as error:
313*33f37583SAndroid Build Coastguard Worker      self._decompress_apex(uncompressed_apex_fp)
314*33f37583SAndroid Build Coastguard Worker
315*33f37583SAndroid Build Coastguard Worker    self.assertIn(uncompressed_apex_fp
316*33f37583SAndroid Build Coastguard Worker                  + ' is not a compressed APEX', str(error.exception))
317*33f37583SAndroid Build Coastguard Worker
318*33f37583SAndroid Build Coastguard Worker  def test_only_original_apex_is_compressed(self):
319*33f37583SAndroid Build Coastguard Worker    uncompressed_apex_fp = self._get_test_apex()
320*33f37583SAndroid Build Coastguard Worker    compressed_apex_fp = self._compress_apex(uncompressed_apex_fp)
321*33f37583SAndroid Build Coastguard Worker
322*33f37583SAndroid Build Coastguard Worker    with ZipFile(compressed_apex_fp, 'r') as zip_obj:
323*33f37583SAndroid Build Coastguard Worker      self.assertEqual(zip_obj.getinfo('original_apex').compress_type,
324*33f37583SAndroid Build Coastguard Worker                       ZIP_DEFLATED)
325*33f37583SAndroid Build Coastguard Worker      content_in_uncompressed_apex = self._get_container_files(
326*33f37583SAndroid Build Coastguard Worker          uncompressed_apex_fp)
327*33f37583SAndroid Build Coastguard Worker      for i in ['apex_manifest.json', 'apex_manifest.pb', 'apex_pubkey',
328*33f37583SAndroid Build Coastguard Worker                'apex_build_info.pb', 'AndroidManifest.xml']:
329*33f37583SAndroid Build Coastguard Worker        if i in content_in_uncompressed_apex:
330*33f37583SAndroid Build Coastguard Worker          self.assertEqual(zip_obj.getinfo(i).compress_type, ZIP_STORED)
331*33f37583SAndroid Build Coastguard Worker
332*33f37583SAndroid Build Coastguard Workerif __name__ == '__main__':
333*33f37583SAndroid Build Coastguard Worker  unittest.main(verbosity=2)
334