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.internal.util; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import android.content.Context; 21 import android.net.Uri; 22 import androidx.test.core.app.ApplicationProvider; 23 import com.google.mobiledatadownload.internal.MetadataProto; 24 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupBookkeeping; 25 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal; 26 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; 27 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidFileBackend; 28 import com.google.android.libraries.mobiledatadownload.file.backends.JavaFileBackend; 29 import com.google.android.libraries.mobiledatadownload.file.openers.ReadByteArrayOpener; 30 import com.google.android.libraries.mobiledatadownload.internal.MddTestUtil; 31 import com.google.common.collect.ImmutableList; 32 import com.google.common.truth.Correspondence; 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.DataFileGroup.AllowedReaders; 37 import com.google.mobiledatadownload.DownloadConfigProto.DeltaFile; 38 import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions; 39 import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions.DeviceNetworkPolicy; 40 import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions.DeviceStoragePolicy; 41 import com.google.mobiledatadownload.DownloadConfigProto.ExtraHttpHeader; 42 import com.google.mobiledatadownload.TransformProto.CompressTransform; 43 import com.google.mobiledatadownload.TransformProto.Transform; 44 import com.google.mobiledatadownload.TransformProto.Transforms; 45 import com.google.mobiledatadownload.TransformProto.ZipTransform; 46 import com.google.protobuf.ExtensionRegistryLite; 47 import com.google.protobuf.contrib.android.ProtoParsers; 48 import com.google.testing.util.TestUtil; 49 import java.io.File; 50 import org.junit.Before; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.junit.MockitoJUnit; 55 import org.mockito.junit.MockitoRule; 56 import org.robolectric.RobolectricTestRunner; 57 58 /** Unit tests for {@link ProtoConversionUtil}. */ 59 @RunWith(RobolectricTestRunner.class) 60 public final class ProtoConversionUtilTest { 61 62 @Rule public final MockitoRule mocks = MockitoJUnit.rule(); 63 64 private SynchronousFileStorage fileStorage; 65 private Context context; 66 67 // The raw test data folder in google3. 68 private static final String TEST_DATA_DIR = 69 TestUtil.getRunfilesDir() 70 + "/third_party/java_src/android_libs/mobiledatadownload/javatests/com/google/android/libraries/mobiledatadownload/internal/util/testdata/"; 71 private static final File RAW_GROUP_WITH_EXTENSION = 72 new File(TEST_DATA_DIR, "raw_group_with_extension"); 73 74 @Before setUp()75 public void setUp() throws Exception { 76 context = ApplicationProvider.getApplicationContext(); 77 fileStorage = 78 new SynchronousFileStorage( 79 ImmutableList.of(AndroidFileBackend.builder(context).build(), new JavaFileBackend())); 80 } 81 82 @Test convert_fromDataFileGroup_toDataFileGroupInternal_noThrow()83 public void convert_fromDataFileGroup_toDataFileGroupInternal_noThrow() throws Exception { 84 DataFileGroup group = 85 DataFileGroup.newBuilder() 86 .setGroupName("test-group") 87 .setOwnerPackage("com.google.android.libraries.mobiledatadownload") 88 .setFileGroupVersionNumber(12) 89 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 90 .setDownloadConditions( 91 DownloadConditions.newBuilder() 92 .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK) 93 .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_LOWER_THRESHOLD) 94 .build()) 95 .setExpirationDate(1234567890) 96 .setStaleLifetimeSecs(123456) 97 .setTrafficTag(3) 98 .setPreserveFilenamesAndIsolateFiles(true) 99 .addAllFile( 100 ImmutableList.of( 101 DataFile.newBuilder() 102 .setFileId("one") 103 .setByteSize(200) 104 .setUrlToDownload("https://www.google.com/") 105 .setChecksum("checksum1") 106 .build(), 107 DataFile.newBuilder() 108 .setFileId("two") 109 .setByteSize(500) 110 .setUrlToDownload("https://www.instagram.com/") 111 .setChecksum("checksum2") 112 .build())) 113 .addAllGroupExtraHttpHeaders( 114 ImmutableList.of( 115 ExtraHttpHeader.newBuilder().setKey("k1").setValue("v1").build(), 116 ExtraHttpHeader.newBuilder().setKey("k2").setValue("v2").build())) 117 .build(); 118 DataFileGroupInternal unused = ProtoConversionUtil.convert(group); 119 } 120 121 @Test convert_fromDataFileGroup_toDataFileGroupInternal_parseFromTextProto()122 public void convert_fromDataFileGroup_toDataFileGroupInternal_parseFromTextProto() 123 throws Exception { 124 DataFileGroup dataFileGroup = getDataFileGroupFromTextProto(R.raw.group_data_pb); 125 DataFileGroupInternal dataFileGroupInternal = 126 getDataFileGroupInternalFromTextProto(R.raw.group_internal_data_pb); 127 assertThat(ProtoConversionUtil.convert(dataFileGroup)).isEqualTo(dataFileGroupInternal); 128 } 129 130 @Test convert_fromDataFileGroup_toDataFileGroupInternal_parseFromTextProto_optionalUnset()131 public void convert_fromDataFileGroup_toDataFileGroupInternal_parseFromTextProto_optionalUnset() 132 throws Exception { 133 DataFileGroup dataFileGroup = getDataFileGroupFromTextProto(R.raw.group_optional_unset_data_pb); 134 DataFileGroupInternal dataFileGroupInternal = 135 getDataFileGroupInternalFromTextProto(R.raw.group_internal_optional_unset_data_pb); 136 assertThat(ProtoConversionUtil.convert(dataFileGroup)).isEqualTo(dataFileGroupInternal); 137 } 138 139 /** 140 * The main purpose of this test is to make sure that after migration, we are still able to 141 * interpret and parse the group metadata correctly. The raw metadata was generated by the proto 142 * {@link DataFileGroup} with extensions before the migration. Since we reuse the same tag number, 143 * the extension should now be parsed into {@link DataFileGroupBookkeeping}. 144 */ 145 @Test convert_parseRawProtoWithExtensions()146 public void convert_parseRawProtoWithExtensions() throws Exception { 147 DataFileGroupInternal expected = 148 ProtoConversionUtil.convert( 149 MddTestUtil.createDataFileGroup(/* fileGroupName= */ "test-group", 2)) 150 .toBuilder() 151 .setBookkeeping( 152 DataFileGroupBookkeeping.newBuilder() 153 .setGroupNewFilesReceivedTimestamp(1000) 154 .build()) 155 .build(); 156 157 // Read the raw group with extension from file. 158 Uri uri = Uri.fromFile(RAW_GROUP_WITH_EXTENSION); 159 byte[] bytes = fileStorage.open(uri, ReadByteArrayOpener.create()); 160 161 // Make sure that the proto with extension is correcly parsed. 162 DataFileGroupInternal parsedFromRawProto = 163 DataFileGroupInternal.parseFrom(bytes, ExtensionRegistryLite.getEmptyRegistry()); 164 assertThat(parsedFromRawProto).isEqualTo(expected); 165 } 166 167 @Test convert_onDownloadConditions()168 public void convert_onDownloadConditions() throws Exception { 169 DownloadConditions downloadConditions = 170 DownloadConditions.newBuilder() 171 .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK) 172 .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_LOWER_THRESHOLD) 173 .build(); 174 MetadataProto.DownloadConditions convertedDownloadConditions = 175 ProtoConversionUtil.convert(downloadConditions); 176 assertThat(convertedDownloadConditions.hasDeviceStoragePolicy()).isTrue(); 177 assertThat(convertedDownloadConditions.hasDeviceNetworkPolicy()).isTrue(); 178 assertThat(convertedDownloadConditions.hasDownloadFirstOnWifiPeriodSecs()).isFalse(); 179 assertThat(convertedDownloadConditions.getDeviceNetworkPolicy()) 180 .isEqualTo(MetadataProto.DownloadConditions.DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK); 181 assertThat(convertedDownloadConditions.getDeviceStoragePolicy()) 182 .isEqualTo( 183 MetadataProto.DownloadConditions.DeviceStoragePolicy.BLOCK_DOWNLOAD_LOWER_THRESHOLD); 184 } 185 186 @Test convertDataFile_convertsFields()187 public void convertDataFile_convertsFields() { 188 Transforms downloadTransforms = 189 Transforms.newBuilder() 190 .addTransform( 191 Transform.newBuilder().setCompress(CompressTransform.getDefaultInstance()).build()) 192 .build(); 193 Transforms readTransforms = 194 Transforms.newBuilder() 195 .addTransform(Transform.newBuilder().setZip(ZipTransform.getDefaultInstance()).build()) 196 .build(); 197 198 DataFile dataFileExternal = 199 DataFile.newBuilder() 200 .setFileId("test-file") 201 .setUrlToDownload("https://url.to.download") 202 .setByteSize(10) 203 .setChecksumType(DataFile.ChecksumType.NONE) 204 .setChecksum("testchecksum") 205 .setDownloadTransforms(downloadTransforms) 206 .setDownloadedFileChecksum("testdownloadedchecksum") 207 .setDownloadedFileByteSize(100) 208 .setReadTransforms(readTransforms) 209 .setAndroidSharingType(DataFile.AndroidSharingType.ANDROID_BLOB_WHEN_AVAILABLE) 210 .setAndroidSharingChecksumType(DataFile.AndroidSharingChecksumType.SHA2_256) 211 .setAndroidSharingChecksum("testandroidsharingchecksum") 212 .setRelativeFilePath("relative/file/path") 213 .addDeltaFile(DeltaFile.newBuilder().setUrlToDownload("url1").build()) 214 .addDeltaFile(DeltaFile.newBuilder().setUrlToDownload("url2").build()) 215 .build(); 216 217 MetadataProto.DataFile dataFileInternal = ProtoConversionUtil.convertDataFile(dataFileExternal); 218 219 assertThat(dataFileInternal.getFileId()).isEqualTo("test-file"); 220 assertThat(dataFileInternal.getUrlToDownload()).isEqualTo("https://url.to.download"); 221 assertThat(dataFileInternal.getByteSize()).isEqualTo(10); 222 assertThat(dataFileInternal.getChecksumType()) 223 .isEqualTo(MetadataProto.DataFile.ChecksumType.NONE); 224 assertThat(dataFileInternal.getChecksum()).isEqualTo("testchecksum"); 225 assertThat(dataFileInternal.getDownloadTransforms()).isEqualTo(downloadTransforms); 226 assertThat(dataFileInternal.getDownloadedFileChecksum()).isEqualTo("testdownloadedchecksum"); 227 assertThat(dataFileInternal.getDownloadedFileByteSize()).isEqualTo(100); 228 assertThat(dataFileInternal.getReadTransforms()).isEqualTo(readTransforms); 229 assertThat(dataFileInternal.getAndroidSharingType()) 230 .isEqualTo(MetadataProto.DataFile.AndroidSharingType.ANDROID_BLOB_WHEN_AVAILABLE); 231 assertThat(dataFileInternal.getAndroidSharingChecksumType()) 232 .isEqualTo(MetadataProto.DataFile.AndroidSharingChecksumType.SHA2_256); 233 assertThat(dataFileInternal.getAndroidSharingChecksum()) 234 .isEqualTo("testandroidsharingchecksum"); 235 assertThat(dataFileInternal.getRelativeFilePath()).isEqualTo("relative/file/path"); 236 237 assertThat(dataFileInternal.getDeltaFileCount()).isEqualTo(2); 238 assertThat(dataFileInternal.getDeltaFileList()) 239 .comparingElementsUsing( 240 Correspondence.transforming(MetadataProto.DeltaFile::getUrlToDownload, "using url")) 241 .containsExactly("url1", "url2"); 242 } 243 244 @Test convertDeltaFile_convertsFields()245 public void convertDeltaFile_convertsFields() { 246 DeltaFile deltaFileExternal = 247 DeltaFile.newBuilder() 248 .setUrlToDownload("https://url.to.download") 249 .setByteSize(10) 250 .setChecksum("testchecksum") 251 .setDiffDecoder(DeltaFile.DiffDecoder.VC_DIFF) 252 .setBaseFile(BaseFile.newBuilder().setChecksum("testbasechecksum").build()) 253 .build(); 254 255 MetadataProto.DeltaFile deltaFileInternal = 256 ProtoConversionUtil.convertDeltaFile(deltaFileExternal); 257 258 assertThat(deltaFileInternal.getUrlToDownload()).isEqualTo("https://url.to.download"); 259 assertThat(deltaFileInternal.getByteSize()).isEqualTo(10); 260 assertThat(deltaFileInternal.getChecksum()).isEqualTo("testchecksum"); 261 assertThat(deltaFileInternal.getDiffDecoder()) 262 .isEqualTo(MetadataProto.DeltaFile.DiffDecoder.VC_DIFF); 263 assertThat(deltaFileInternal.getBaseFile().getChecksum()).isEqualTo("testbasechecksum"); 264 } 265 getDataFileGroupFromTextProto(int rawResId)266 private static DataFileGroup getDataFileGroupFromTextProto(int rawResId) { 267 return ProtoParsers.parseFromRawRes( 268 ApplicationProvider.getApplicationContext(), DataFileGroup.parser(), rawResId); 269 } 270 getDataFileGroupInternalFromTextProto(int rawResId)271 private static DataFileGroupInternal getDataFileGroupInternalFromTextProto(int rawResId) { 272 return ProtoParsers.parseFromRawRes( 273 ApplicationProvider.getApplicationContext(), DataFileGroupInternal.parser(), rawResId); 274 } 275 } 276