/* * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.libraries.mobiledatadownload.internal.util; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.net.Uri; import androidx.test.core.app.ApplicationProvider; import com.google.mobiledatadownload.internal.MetadataProto; import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupBookkeeping; import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal; import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; import com.google.android.libraries.mobiledatadownload.file.backends.AndroidFileBackend; import com.google.android.libraries.mobiledatadownload.file.backends.JavaFileBackend; import com.google.android.libraries.mobiledatadownload.file.openers.ReadByteArrayOpener; import com.google.android.libraries.mobiledatadownload.internal.MddTestUtil; import com.google.common.collect.ImmutableList; import com.google.common.truth.Correspondence; import com.google.mobiledatadownload.DownloadConfigProto.BaseFile; import com.google.mobiledatadownload.DownloadConfigProto.DataFile; import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup; import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup.AllowedReaders; import com.google.mobiledatadownload.DownloadConfigProto.DeltaFile; import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions; import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions.DeviceNetworkPolicy; import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions.DeviceStoragePolicy; import com.google.mobiledatadownload.DownloadConfigProto.ExtraHttpHeader; import com.google.mobiledatadownload.TransformProto.CompressTransform; import com.google.mobiledatadownload.TransformProto.Transform; import com.google.mobiledatadownload.TransformProto.Transforms; import com.google.mobiledatadownload.TransformProto.ZipTransform; import com.google.protobuf.ExtensionRegistryLite; import com.google.protobuf.contrib.android.ProtoParsers; import com.google.testing.util.TestUtil; import java.io.File; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; /** Unit tests for {@link ProtoConversionUtil}. */ @RunWith(RobolectricTestRunner.class) public final class ProtoConversionUtilTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); private SynchronousFileStorage fileStorage; private Context context; // The raw test data folder in google3. private static final String TEST_DATA_DIR = TestUtil.getRunfilesDir() + "/third_party/java_src/android_libs/mobiledatadownload/javatests/com/google/android/libraries/mobiledatadownload/internal/util/testdata/"; private static final File RAW_GROUP_WITH_EXTENSION = new File(TEST_DATA_DIR, "raw_group_with_extension"); @Before public void setUp() throws Exception { context = ApplicationProvider.getApplicationContext(); fileStorage = new SynchronousFileStorage( ImmutableList.of(AndroidFileBackend.builder(context).build(), new JavaFileBackend())); } @Test public void convert_fromDataFileGroup_toDataFileGroupInternal_noThrow() throws Exception { DataFileGroup group = DataFileGroup.newBuilder() .setGroupName("test-group") .setOwnerPackage("com.google.android.libraries.mobiledatadownload") .setFileGroupVersionNumber(12) .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) .setDownloadConditions( DownloadConditions.newBuilder() .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK) .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_LOWER_THRESHOLD) .build()) .setExpirationDate(1234567890) .setStaleLifetimeSecs(123456) .setTrafficTag(3) .setPreserveFilenamesAndIsolateFiles(true) .addAllFile( ImmutableList.of( DataFile.newBuilder() .setFileId("one") .setByteSize(200) .setUrlToDownload("https://www.google.com/") .setChecksum("checksum1") .build(), DataFile.newBuilder() .setFileId("two") .setByteSize(500) .setUrlToDownload("https://www.instagram.com/") .setChecksum("checksum2") .build())) .addAllGroupExtraHttpHeaders( ImmutableList.of( ExtraHttpHeader.newBuilder().setKey("k1").setValue("v1").build(), ExtraHttpHeader.newBuilder().setKey("k2").setValue("v2").build())) .build(); DataFileGroupInternal unused = ProtoConversionUtil.convert(group); } @Test public void convert_fromDataFileGroup_toDataFileGroupInternal_parseFromTextProto() throws Exception { DataFileGroup dataFileGroup = getDataFileGroupFromTextProto(R.raw.group_data_pb); DataFileGroupInternal dataFileGroupInternal = getDataFileGroupInternalFromTextProto(R.raw.group_internal_data_pb); assertThat(ProtoConversionUtil.convert(dataFileGroup)).isEqualTo(dataFileGroupInternal); } @Test public void convert_fromDataFileGroup_toDataFileGroupInternal_parseFromTextProto_optionalUnset() throws Exception { DataFileGroup dataFileGroup = getDataFileGroupFromTextProto(R.raw.group_optional_unset_data_pb); DataFileGroupInternal dataFileGroupInternal = getDataFileGroupInternalFromTextProto(R.raw.group_internal_optional_unset_data_pb); assertThat(ProtoConversionUtil.convert(dataFileGroup)).isEqualTo(dataFileGroupInternal); } /** * The main purpose of this test is to make sure that after migration, we are still able to * interpret and parse the group metadata correctly. The raw metadata was generated by the proto * {@link DataFileGroup} with extensions before the migration. Since we reuse the same tag number, * the extension should now be parsed into {@link DataFileGroupBookkeeping}. */ @Test public void convert_parseRawProtoWithExtensions() throws Exception { DataFileGroupInternal expected = ProtoConversionUtil.convert( MddTestUtil.createDataFileGroup(/* fileGroupName= */ "test-group", 2)) .toBuilder() .setBookkeeping( DataFileGroupBookkeeping.newBuilder() .setGroupNewFilesReceivedTimestamp(1000) .build()) .build(); // Read the raw group with extension from file. Uri uri = Uri.fromFile(RAW_GROUP_WITH_EXTENSION); byte[] bytes = fileStorage.open(uri, ReadByteArrayOpener.create()); // Make sure that the proto with extension is correcly parsed. DataFileGroupInternal parsedFromRawProto = DataFileGroupInternal.parseFrom(bytes, ExtensionRegistryLite.getEmptyRegistry()); assertThat(parsedFromRawProto).isEqualTo(expected); } @Test public void convert_onDownloadConditions() throws Exception { DownloadConditions downloadConditions = DownloadConditions.newBuilder() .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK) .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_LOWER_THRESHOLD) .build(); MetadataProto.DownloadConditions convertedDownloadConditions = ProtoConversionUtil.convert(downloadConditions); assertThat(convertedDownloadConditions.hasDeviceStoragePolicy()).isTrue(); assertThat(convertedDownloadConditions.hasDeviceNetworkPolicy()).isTrue(); assertThat(convertedDownloadConditions.hasDownloadFirstOnWifiPeriodSecs()).isFalse(); assertThat(convertedDownloadConditions.getDeviceNetworkPolicy()) .isEqualTo(MetadataProto.DownloadConditions.DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK); assertThat(convertedDownloadConditions.getDeviceStoragePolicy()) .isEqualTo( MetadataProto.DownloadConditions.DeviceStoragePolicy.BLOCK_DOWNLOAD_LOWER_THRESHOLD); } @Test public void convertDataFile_convertsFields() { Transforms downloadTransforms = Transforms.newBuilder() .addTransform( Transform.newBuilder().setCompress(CompressTransform.getDefaultInstance()).build()) .build(); Transforms readTransforms = Transforms.newBuilder() .addTransform(Transform.newBuilder().setZip(ZipTransform.getDefaultInstance()).build()) .build(); DataFile dataFileExternal = DataFile.newBuilder() .setFileId("test-file") .setUrlToDownload("https://url.to.download") .setByteSize(10) .setChecksumType(DataFile.ChecksumType.NONE) .setChecksum("testchecksum") .setDownloadTransforms(downloadTransforms) .setDownloadedFileChecksum("testdownloadedchecksum") .setDownloadedFileByteSize(100) .setReadTransforms(readTransforms) .setAndroidSharingType(DataFile.AndroidSharingType.ANDROID_BLOB_WHEN_AVAILABLE) .setAndroidSharingChecksumType(DataFile.AndroidSharingChecksumType.SHA2_256) .setAndroidSharingChecksum("testandroidsharingchecksum") .setRelativeFilePath("relative/file/path") .addDeltaFile(DeltaFile.newBuilder().setUrlToDownload("url1").build()) .addDeltaFile(DeltaFile.newBuilder().setUrlToDownload("url2").build()) .build(); MetadataProto.DataFile dataFileInternal = ProtoConversionUtil.convertDataFile(dataFileExternal); assertThat(dataFileInternal.getFileId()).isEqualTo("test-file"); assertThat(dataFileInternal.getUrlToDownload()).isEqualTo("https://url.to.download"); assertThat(dataFileInternal.getByteSize()).isEqualTo(10); assertThat(dataFileInternal.getChecksumType()) .isEqualTo(MetadataProto.DataFile.ChecksumType.NONE); assertThat(dataFileInternal.getChecksum()).isEqualTo("testchecksum"); assertThat(dataFileInternal.getDownloadTransforms()).isEqualTo(downloadTransforms); assertThat(dataFileInternal.getDownloadedFileChecksum()).isEqualTo("testdownloadedchecksum"); assertThat(dataFileInternal.getDownloadedFileByteSize()).isEqualTo(100); assertThat(dataFileInternal.getReadTransforms()).isEqualTo(readTransforms); assertThat(dataFileInternal.getAndroidSharingType()) .isEqualTo(MetadataProto.DataFile.AndroidSharingType.ANDROID_BLOB_WHEN_AVAILABLE); assertThat(dataFileInternal.getAndroidSharingChecksumType()) .isEqualTo(MetadataProto.DataFile.AndroidSharingChecksumType.SHA2_256); assertThat(dataFileInternal.getAndroidSharingChecksum()) .isEqualTo("testandroidsharingchecksum"); assertThat(dataFileInternal.getRelativeFilePath()).isEqualTo("relative/file/path"); assertThat(dataFileInternal.getDeltaFileCount()).isEqualTo(2); assertThat(dataFileInternal.getDeltaFileList()) .comparingElementsUsing( Correspondence.transforming(MetadataProto.DeltaFile::getUrlToDownload, "using url")) .containsExactly("url1", "url2"); } @Test public void convertDeltaFile_convertsFields() { DeltaFile deltaFileExternal = DeltaFile.newBuilder() .setUrlToDownload("https://url.to.download") .setByteSize(10) .setChecksum("testchecksum") .setDiffDecoder(DeltaFile.DiffDecoder.VC_DIFF) .setBaseFile(BaseFile.newBuilder().setChecksum("testbasechecksum").build()) .build(); MetadataProto.DeltaFile deltaFileInternal = ProtoConversionUtil.convertDeltaFile(deltaFileExternal); assertThat(deltaFileInternal.getUrlToDownload()).isEqualTo("https://url.to.download"); assertThat(deltaFileInternal.getByteSize()).isEqualTo(10); assertThat(deltaFileInternal.getChecksum()).isEqualTo("testchecksum"); assertThat(deltaFileInternal.getDiffDecoder()) .isEqualTo(MetadataProto.DeltaFile.DiffDecoder.VC_DIFF); assertThat(deltaFileInternal.getBaseFile().getChecksum()).isEqualTo("testbasechecksum"); } private static DataFileGroup getDataFileGroupFromTextProto(int rawResId) { return ProtoParsers.parseFromRawRes( ApplicationProvider.getApplicationContext(), DataFileGroup.parser(), rawResId); } private static DataFileGroupInternal getDataFileGroupInternalFromTextProto(int rawResId) { return ProtoParsers.parseFromRawRes( ApplicationProvider.getApplicationContext(), DataFileGroupInternal.parser(), rawResId); } }