1 /* 2 * Copyright 2022 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.libraries.mobiledatadownload.populator; 17 18 import static com.google.android.libraries.mobiledatadownload.populator.ManifestConfigHelper.URL_TEMPLATE_CHECKSUM_PLACEHOLDER; 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.Mockito.times; 23 import static org.mockito.Mockito.verify; 24 import static org.mockito.Mockito.when; 25 26 import com.google.android.libraries.mobiledatadownload.AddFileGroupRequest; 27 import com.google.android.libraries.mobiledatadownload.MobileDataDownload; 28 import com.google.common.base.Optional; 29 import com.google.common.collect.ImmutableList; 30 import com.google.common.collect.Iterables; 31 import com.google.common.util.concurrent.Futures; 32 import com.google.common.util.concurrent.ListenableFuture; 33 import com.google.mobiledatadownload.DownloadConfigProto.BaseFile; 34 import com.google.mobiledatadownload.DownloadConfigProto.DataFile; 35 import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup; 36 import com.google.mobiledatadownload.DownloadConfigProto.DeltaFile; 37 import com.google.mobiledatadownload.DownloadConfigProto.DeltaFile.DiffDecoder; 38 import com.google.mobiledatadownload.DownloadConfigProto.ManifestConfig; 39 import com.google.mobiledatadownload.DownloadConfigProto.ManifestConfig.UrlTemplate; 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.concurrent.ExecutionException; 43 import org.junit.Before; 44 import org.junit.Rule; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 import org.mockito.ArgumentCaptor; 48 import org.mockito.Captor; 49 import org.mockito.Mock; 50 import org.mockito.junit.MockitoJUnit; 51 import org.mockito.junit.MockitoRule; 52 import org.robolectric.RobolectricTestRunner; 53 54 /** Tests for {@link ManifestConfigFlagPopulator}. */ 55 @RunWith(RobolectricTestRunner.class) 56 public class ManifestConfigFlagPopulatorTest { 57 @Rule public final MockitoRule mocks = MockitoJUnit.rule(); 58 59 @Mock MobileDataDownload mockMobileDataDownload; 60 @Captor private ArgumentCaptor<AddFileGroupRequest> addFileGroupRequestCaptor; 61 62 private static final DeltaFile DELTA_FILE = 63 DeltaFile.newBuilder() 64 .setDiffDecoder(DiffDecoder.VC_DIFF) 65 .setUrlToDownload("https://standard.file.url") 66 .setChecksum("standardDeltaFileChecksum") 67 .setBaseFile(BaseFile.newBuilder().setChecksum("standardBaseFileChecksum").build()) 68 .build(); 69 70 private static final DataFile DATA_FILE = 71 DataFile.newBuilder() 72 .setFileId("standard-file") 73 .setUrlToDownload("https://standard.file.url") 74 .setChecksum("standardFileChecksum") 75 .addDeltaFile(DELTA_FILE) 76 .build(); 77 78 private static final DataFileGroup DATA_FILE_GROUP = 79 DataFileGroup.newBuilder().setGroupName("groupName").setOwnerPackage("ownerPackage").build(); 80 81 @Before setUp()82 public void setUp() { 83 when(mockMobileDataDownload.addFileGroup(any(AddFileGroupRequest.class))) 84 .thenReturn(Futures.immediateFuture(true)); 85 } 86 87 @Test refreshFromManifestConfig_absentOverrider()88 public void refreshFromManifestConfig_absentOverrider() 89 throws ExecutionException, InterruptedException { 90 ManifestConfig manifestConfig = createManifestConfig(); 91 92 ManifestConfigHelper.refreshFromManifestConfig( 93 mockMobileDataDownload, 94 manifestConfig, 95 /* overriderOptional= */ Optional.absent(), 96 /* accounts= */ ImmutableList.of(), 97 /* addGroupsWithVariantId= */ false) 98 .get(); 99 verify(mockMobileDataDownload, times(2)).addFileGroup(addFileGroupRequestCaptor.capture()); 100 101 assertThat(addFileGroupRequestCaptor.getAllValues().get(0).dataFileGroup().getGroupName()) 102 .isEqualTo("groupNameLocaleEnUS"); 103 assertThat(addFileGroupRequestCaptor.getAllValues().get(1).dataFileGroup().getGroupName()) 104 .isEqualTo("groupNameLocaleEnUK"); 105 } 106 107 @Test refreshFromManifestConfig_withUrlTemplate_absentOverrider()108 public void refreshFromManifestConfig_withUrlTemplate_absentOverrider() 109 throws ExecutionException, InterruptedException { 110 ManifestConfig manifestConfigWithUrlTemplate = createManifestConfigWithUrlTemplate(); 111 // File url_to_download populated using url templates. 112 DataFileGroup dataFileGroup1 = 113 DATA_FILE_GROUP.toBuilder() 114 .addFile( 115 DATA_FILE.toBuilder() 116 .setUrlToDownload("https://standard.file.url/standardFileChecksum") 117 .setDeltaFile( 118 0, 119 DELTA_FILE.toBuilder() 120 .setUrlToDownload( 121 "https://standard.file.url/standardDeltaFileChecksum"))) 122 .build(); 123 DataFileGroup dataFileGroup2 = DATA_FILE_GROUP.toBuilder().addFile(DATA_FILE).build(); 124 125 ManifestConfigHelper.refreshFromManifestConfig( 126 mockMobileDataDownload, 127 manifestConfigWithUrlTemplate, 128 /* overriderOptional= */ Optional.absent(), 129 /* accounts= */ ImmutableList.of(), 130 /* addGroupsWithVariantId= */ false) 131 .get(); 132 verify(mockMobileDataDownload, times(2)).addFileGroup(addFileGroupRequestCaptor.capture()); 133 134 List<AddFileGroupRequest> addFileGroupRequests = addFileGroupRequestCaptor.getAllValues(); 135 assertThat(Iterables.transform(addFileGroupRequests, AddFileGroupRequest::dataFileGroup)) 136 .containsExactly(dataFileGroup1, dataFileGroup2) 137 .inOrder(); 138 } 139 140 @Test refreshFromManifestConfig_noUrlTemplate_urlToDownloadEmpty_absentOverrider()141 public void refreshFromManifestConfig_noUrlTemplate_urlToDownloadEmpty_absentOverrider() 142 throws ExecutionException, InterruptedException { 143 // Files with no url_to_download will fail since url template is not available. 144 ManifestConfig manifestConfigWithoutUrlTemplate = 145 createManifestConfigWithUrlTemplate().toBuilder().clearUrlTemplate().build(); 146 147 IllegalArgumentException illegalArgumentException = 148 assertThrows( 149 IllegalArgumentException.class, 150 () -> 151 ManifestConfigHelper.refreshFromManifestConfig( 152 mockMobileDataDownload, 153 manifestConfigWithoutUrlTemplate, 154 /* overriderOptional= */ Optional.absent(), 155 /* accounts= */ ImmutableList.of(), 156 /* addGroupsWithVariantId= */ false) 157 .get()); 158 159 assertThat(illegalArgumentException) 160 .hasMessageThat() 161 .isEqualTo("DataFile standard-file url_to_download is missing."); 162 } 163 164 @Test refreshFromManifestConfig_identityOverrider()165 public void refreshFromManifestConfig_identityOverrider() 166 throws ExecutionException, InterruptedException { 167 ManifestConfig manifestConfig = createManifestConfig(); 168 169 // Use a ManifestConfigOverrider that does not filter/change anything. 170 ManifestConfigOverrider overrider = 171 new ManifestConfigOverrider() { 172 @Override 173 public ListenableFuture<List<DataFileGroup>> override(ManifestConfig config) { 174 List<DataFileGroup> overriderResults = new ArrayList<>(); 175 for (ManifestConfig.Entry entry : config.getEntryList()) { 176 overriderResults.add(entry.getDataFileGroup()); 177 } 178 179 return Futures.immediateFuture(overriderResults); 180 } 181 }; 182 183 ManifestConfigHelper.refreshFromManifestConfig( 184 mockMobileDataDownload, 185 manifestConfig, 186 Optional.of(overrider), 187 /* accounts= */ ImmutableList.of(), 188 /* addGroupsWithVariantId= */ false) 189 .get(); 190 verify(mockMobileDataDownload, times(2)).addFileGroup(addFileGroupRequestCaptor.capture()); 191 192 assertThat(addFileGroupRequestCaptor.getAllValues().get(0).dataFileGroup().getGroupName()) 193 .isEqualTo("groupNameLocaleEnUS"); 194 assertThat(addFileGroupRequestCaptor.getAllValues().get(1).dataFileGroup().getGroupName()) 195 .isEqualTo("groupNameLocaleEnUK"); 196 } 197 198 @Test refreshFromManifestConfig_en_USLocaleOverrider()199 public void refreshFromManifestConfig_en_USLocaleOverrider() 200 throws ExecutionException, InterruptedException { 201 ManifestConfig manifestConfig = createManifestConfig(); 202 203 // Use a ManifestConfigOverrider that only keeps entry with locale en_US. 204 ManifestConfigOverrider overrider = 205 new ManifestConfigOverrider() { 206 @Override 207 public ListenableFuture<List<DataFileGroup>> override(ManifestConfig config) { 208 List<DataFileGroup> overriderResults = new ArrayList<>(); 209 for (ManifestConfig.Entry entry : config.getEntryList()) { 210 if ("en_US".equals(entry.getModifier().getLocaleList().get(0))) { 211 overriderResults.add(entry.getDataFileGroup()); 212 } 213 } 214 215 return Futures.immediateFuture(overriderResults); 216 } 217 }; 218 219 ManifestConfigHelper.refreshFromManifestConfig( 220 mockMobileDataDownload, 221 manifestConfig, 222 Optional.of(overrider), 223 /* accounts= */ ImmutableList.of(), 224 /* addGroupsWithVariantId= */ false) 225 .get(); 226 verify(mockMobileDataDownload, times(1)).addFileGroup(addFileGroupRequestCaptor.capture()); 227 228 assertThat(addFileGroupRequestCaptor.getAllValues().get(0).dataFileGroup().getGroupName()) 229 .isEqualTo("groupNameLocaleEnUS"); 230 } 231 232 @Test refreshFileGroups_usesSupplier()233 public void refreshFileGroups_usesSupplier() throws ExecutionException, InterruptedException { 234 ManifestConfig manifestConfig = createManifestConfig(); 235 236 ManifestConfigFlagPopulator populator = 237 ManifestConfigFlagPopulator.builder() 238 .setManifestConfigSupplier(() -> manifestConfig) 239 .build(); 240 241 populator.refreshFileGroups(mockMobileDataDownload).get(); 242 verify(mockMobileDataDownload, times(2)).addFileGroup(addFileGroupRequestCaptor.capture()); 243 244 assertThat(addFileGroupRequestCaptor.getAllValues().get(0).dataFileGroup().getGroupName()) 245 .isEqualTo("groupNameLocaleEnUS"); 246 assertThat(addFileGroupRequestCaptor.getAllValues().get(1).dataFileGroup().getGroupName()) 247 .isEqualTo("groupNameLocaleEnUK"); 248 } 249 250 @Test manifestConfigFlagPopulatorBuilder_rejectsNotBothPackageAndFlag()251 public void manifestConfigFlagPopulatorBuilder_rejectsNotBothPackageAndFlag() { 252 assertThrows( 253 IllegalArgumentException.class, () -> ManifestConfigFlagPopulator.builder().build()); 254 } 255 createManifestConfig()256 private static ManifestConfig createManifestConfig() { 257 // Create a ManifestConfig with 2 entries for locale en_US and en_UK. 258 ManifestConfig.Entry entryUs = 259 ManifestConfig.Entry.newBuilder() 260 .setModifier(ManifestConfig.Entry.Modifier.newBuilder().addLocale("en_US").build()) 261 .setDataFileGroup( 262 DataFileGroup.newBuilder() 263 .setGroupName("groupNameLocaleEnUS") 264 .setOwnerPackage("ownerPackage")) 265 .build(); 266 267 ManifestConfig.Entry entryUk = 268 ManifestConfig.Entry.newBuilder() 269 .setModifier(ManifestConfig.Entry.Modifier.newBuilder().addLocale("en_UK").build()) 270 .setDataFileGroup( 271 DataFileGroup.newBuilder() 272 .setGroupName("groupNameLocaleEnUK") 273 .setOwnerPackage("ownerPackage")) 274 .build(); 275 276 return ManifestConfig.newBuilder().addEntry(entryUs).addEntry(entryUk).build(); 277 } 278 createManifestConfigWithUrlTemplate()279 private static ManifestConfig createManifestConfigWithUrlTemplate() { 280 DataFileGroup dataFileGroupWithoutUrlDownload = 281 DATA_FILE_GROUP.toBuilder() 282 .addFile( 283 DATA_FILE.toBuilder() 284 .clearUrlToDownload() 285 .setDeltaFile(0, DELTA_FILE.toBuilder().clearUrlToDownload())) 286 .build(); 287 DataFileGroup dataFileGroup2 = DATA_FILE_GROUP.toBuilder().addFile(DATA_FILE).build(); 288 289 return ManifestConfig.newBuilder() 290 .setUrlTemplate( 291 UrlTemplate.newBuilder() 292 .setFileUrlTemplate( 293 "https://standard.file.url/" + URL_TEMPLATE_CHECKSUM_PLACEHOLDER) 294 .build()) 295 .addEntry( 296 ManifestConfig.Entry.newBuilder().setDataFileGroup(dataFileGroupWithoutUrlDownload)) 297 .addEntry(ManifestConfig.Entry.newBuilder().setDataFileGroup(dataFileGroup2)) 298 .build(); 299 } 300 } 301