1// Copyright 2022 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package generatemanifest is a command line tool to generate an empty AndroidManifest 16package generatemanifest 17 18import ( 19 "io" 20 "strings" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24) 25 26type fakeFile struct { 27 reader *strings.Reader 28} 29 30func (f fakeFile) Read(b []byte) (int, error) { 31 return f.reader.Read(b) 32} 33 34func (f fakeFile) Close() error { 35 return nil 36} 37 38func TestExtractMinSdk(t *testing.T) { 39 tests := []struct { 40 name string 41 manifests []*strings.Reader 42 defaultMinSdk int 43 expectedMinSdk int 44 }{ 45 { 46 name: "one manifest", 47 manifests: []*strings.Reader{ 48 strings.NewReader( 49 `<?xml version="1.0" encoding="utf-8"?> 50<manifest xmlns:android="http://schemas.android.com/apk/res/android" 51 package="com.default"> 52 <uses-sdk android:minSdkVersion="20" /> 53</manifest>`)}, 54 defaultMinSdk: 14, 55 expectedMinSdk: 20, 56 }, 57 { 58 name: "one manifest, lower then default min sdk", 59 manifests: []*strings.Reader{ 60 strings.NewReader( 61 `<?xml version="1.0" encoding="utf-8"?> 62<manifest xmlns:android="http://schemas.android.com/apk/res/android" 63 package="com.default"> 64 <uses-sdk android:minSdkVersion="20" /> 65</manifest>`)}, 66 defaultMinSdk: 30, 67 expectedMinSdk: 30, 68 }, 69 { 70 name: "multiple manifests", 71 manifests: []*strings.Reader{ 72 strings.NewReader( 73 `<?xml version="1.0" encoding="utf-8"?> 74<manifest xmlns:android="http://schemas.android.com/apk/res/android" 75 package="com.default"> 76 <uses-sdk android:minSdkVersion="20" /> 77</manifest>`), 78 strings.NewReader( 79 `<?xml version="1.0" encoding="utf-8"?> 80<manifest xmlns:android="http://schemas.android.com/apk/res/android" 81 package="com.default"> 82 <uses-sdk android:minSdkVersion="5" /> 83</manifest>`), 84 strings.NewReader( 85 `<?xml version="1.0" encoding="utf-8"?> 86<manifest xmlns:android="http://schemas.android.com/apk/res/android" 87 package="com.default"> 88 <uses-sdk android:minSdkVersion="30" /> 89</manifest>`), 90 }, 91 defaultMinSdk: 14, 92 expectedMinSdk: 30, 93 }, 94 { 95 name: "multiple manifests, all lower than default min sdk", 96 manifests: []*strings.Reader{ 97 strings.NewReader( 98 `<?xml version="1.0" encoding="utf-8"?> 99<manifest xmlns:android="http://schemas.android.com/apk/res/android" 100 package="com.default"> 101 <uses-sdk android:minSdkVersion="1" /> 102</manifest>`), 103 strings.NewReader( 104 `<?xml version="1.0" encoding="utf-8"?> 105<manifest xmlns:android="http://schemas.android.com/apk/res/android" 106 package="com.default"> 107 <uses-sdk android:minSdkVersion="2" /> 108</manifest>`), 109 strings.NewReader( 110 `<?xml version="1.0" encoding="utf-8"?> 111<manifest xmlns:android="http://schemas.android.com/apk/res/android" 112 package="com.default"> 113 <uses-sdk android:minSdkVersion="3" /> 114</manifest>`), 115 }, 116 defaultMinSdk: 4, 117 expectedMinSdk: 4, 118 }, 119 } 120 for _, tc := range tests { 121 t.Run(tc.name, func(t *testing.T) { 122 files := make([]io.ReadCloser, 0, len(tc.manifests)) 123 for _, f := range tc.manifests { 124 file := fakeFile{reader: f} 125 file.reader.Seek(0, 0) 126 files = append(files, file) 127 } 128 minSdk, err := extractMinSdk(files, tc.defaultMinSdk) 129 if err != nil { 130 t.Fatalf("extractMinSdk(%v, %d) failed with err: %v", files, tc.defaultMinSdk, err) 131 } 132 if diff := cmp.Diff(tc.expectedMinSdk, minSdk); diff != "" { 133 t.Errorf("extractMinSdkFromManifest(%v) returned diff (-want, +got):\n%v", files, diff) 134 } 135 }) 136 } 137 138} 139 140func TestExtractMinSdkFromManifest(t *testing.T) { 141 tests := []struct { 142 name string 143 manifest *strings.Reader 144 expectedMinSdk int 145 }{ 146 { 147 name: "minimal manifest", 148 manifest: strings.NewReader( 149 `<?xml version="1.0" encoding="utf-8"?> 150<manifest xmlns:android="http://schemas.android.com/apk/res/android" 151 package="com.default"> 152 <uses-sdk android:minSdkVersion="1" /> 153 <application/> 154</manifest>`), 155 expectedMinSdk: 1, 156 }, 157 { 158 name: "manifest with placeholder", 159 manifest: strings.NewReader( 160 `<?xml version="1.0" encoding="utf-8"?> 161<manifest xmlns:android="http://schemas.android.com/apk/res/android" 162 package="com.default"> 163 <uses-sdk android:minSdkVersion="${minSdkVersion}" /> 164 <application/> 165</manifest>`), 166 expectedMinSdk: 0, 167 }, 168 { 169 name: "empty manifest", 170 manifest: strings.NewReader( 171 `<?xml version="1.0" encoding="utf-8"?> 172<manifest xmlns:android="http://schemas.android.com/apk/res/android" 173 package="com.default"> 174</manifest>`), 175 expectedMinSdk: 0, 176 }, 177 { 178 name: "manifest with various elements", 179 manifest: strings.NewReader( 180 `<?xml version="1.0" encoding="utf-8"?> 181<manifest xmlns:android="http://schemas.android.com/apk/res/android" 182 package="com.default"> 183 <uses-permission android:name="android.permission.INTERNET" /> 184 <application android:label="@string/app_name" 185 android:name="com.default.SomeApp" 186 android:icon="@drawable/some_icon" 187 android:theme="@style/a_theme" 188 android:banner="@drawable/banner"> 189 <service android:name="com.default.MyService" 190 android:exported="true"> 191 <intent-filter> 192 <action android:name="com.default.IntentFilter" /> 193 </intent-filter> 194 </service> 195 </application> 196 <uses-sdk android:minSdkVersion="25" /> 197</manifest>`), 198 expectedMinSdk: 25, 199 }, 200 } 201 for _, tc := range tests { 202 t.Run(tc.name, func(t *testing.T) { 203 file := fakeFile{reader: tc.manifest} 204 file.reader.Seek(0, 0) 205 results := make(chan result) 206 go func(results chan result) { 207 res := extractMinSdkFromManifest(file) 208 results <- res 209 }(results) 210 result := <-results 211 if result.err != nil { 212 t.Fatalf("extractMinSdkFromManifest(%v) failed with err: %v", file, result.err) 213 } 214 if diff := cmp.Diff(tc.expectedMinSdk, result.minSdk); diff != "" { 215 t.Errorf("extractMinSdkFromManifest(%v) returned diff (-want, +got):\n%v", file, diff) 216 } 217 }) 218 } 219 220} 221