1#!/usr/bin/env python3
2#
3# Copyright (C) 2021 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import io
18from pathlib import Path
19import tempfile
20import unittest
21import zipfile
22
23import merge_annotation_zips
24
25
26zip_a = {
27  'android/provider/annotations.xml':
28  """<?xml version="1.0" encoding="UTF-8"?>
29<root>
30  <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)">
31    <annotation name="androidx.annotation.WorkerThread"/>
32  </item>
33  <item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2">
34    <annotation name="androidx.annotation.IntRange">
35      <val name="from" val="1" />
36    </annotation>
37  </item>
38</root>""",
39  'android/os/annotations.xml':
40  """<?xml version="1.0" encoding="UTF-8"?>
41<root>
42  <item name="android.app.ActionBar void setCustomView(int) 0">
43    <annotation name="androidx.annotation.LayoutRes"/>
44  </item>
45</root>
46"""
47}
48
49zip_b = {
50  'android/provider/annotations.xml':
51  """<?xml version="1.0" encoding="UTF-8"?>
52<root>
53  <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE">
54    <annotation name="androidx.annotation.IntDef">
55      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
56      <val name="flag" val="true" />
57    </annotation>
58  </item>
59  <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING">
60    <annotation name="androidx.annotation.IntDef">
61      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
62      <val name="flag" val="true" />
63    </annotation>
64  </item>
65</root>"""
66}
67
68zip_c = {
69  'android/app/annotations.xml':
70  """<?xml version="1.0" encoding="UTF-8"?>
71<root>
72  <item name="android.app.ActionBar void setCustomView(int) 0">
73    <annotation name="androidx.annotation.LayoutRes"/>
74  </item>
75</root>"""
76}
77
78merged_provider = """<?xml version='1.0' encoding='UTF-8'?>
79<root>
80  <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)">
81    <annotation name="androidx.annotation.WorkerThread" />
82  </item>
83  <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE">
84    <annotation name="androidx.annotation.IntDef">
85      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
86      <val name="flag" val="true" />
87    </annotation>
88  </item>
89  <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING">
90    <annotation name="androidx.annotation.IntDef">
91      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
92      <val name="flag" val="true" />
93    </annotation>
94  </item>
95<item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2">
96    <annotation name="androidx.annotation.IntRange">
97      <val name="from" val="1" />
98    </annotation>
99  </item>
100</root>"""
101
102
103
104class MergeAnnotationZipsTest(unittest.TestCase):
105
106  def test_merge_zips(self):
107    with tempfile.TemporaryDirectory() as out_dir:
108      for zip_content in [zip_a, zip_b, zip_c]:
109        f = io.BytesIO()
110        with zipfile.ZipFile(f, "w") as zip_file:
111          for filename, content in zip_content.items():
112            zip_file.writestr(filename, content)
113          merge_annotation_zips.merge_zip_file(out_dir, zip_file)
114
115      # Unchanged
116      self.assertEqual(zip_a['android/os/annotations.xml'], Path(out_dir, 'android/os/annotations.xml').read_text())
117      self.assertEqual(zip_c['android/app/annotations.xml'], Path(out_dir, 'android/app/annotations.xml').read_text())
118
119      # Merged
120      self.assertEqual(merged_provider, Path(out_dir, 'android/provider/annotations.xml').read_text())
121
122
123if __name__ == "__main__":
124  unittest.main(verbosity=2)
125