• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
17 
18 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 import static java.util.concurrent.TimeUnit.DAYS;
22 import static java.util.concurrent.TimeUnit.MILLISECONDS;
23 
24 import android.content.Context;
25 import android.os.Build.VERSION;
26 import android.support.test.uiautomator.UiDevice;
27 import android.util.Log;
28 import com.google.mobiledatadownload.internal.MetadataProto.BaseFile;
29 import com.google.mobiledatadownload.internal.MetadataProto.DataFile;
30 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal;
31 import com.google.mobiledatadownload.internal.MetadataProto.DeltaFile;
32 import com.google.mobiledatadownload.internal.MetadataProto.DeltaFile.DiffDecoder;
33 import com.google.mobiledatadownload.internal.MetadataProto.FileStatus;
34 import com.google.mobiledatadownload.internal.MetadataProto.NewFileKey;
35 import com.google.mobiledatadownload.internal.MetadataProto.SharedFile;
36 import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup;
37 import com.google.mobiledatadownload.TransformProto.Transform;
38 import com.google.mobiledatadownload.TransformProto.Transforms;
39 import com.google.mobiledatadownload.TransformProto.ZipTransform;
40 import com.google.protobuf.MessageLite;
41 import java.io.IOException;
42 import java.util.Collections;
43 import java.util.List;
44 
45 public class MddTestUtil {
46 
47   public static final String FILE_URI = "android://file";
48   private static final String TAG = "MddTestUtil";
49 
50   /**
51    * Creates a data file group with the given number of files. It only sets the field that are set
52    * in a data file group that we get from the server.
53    */
createDataFileGroup(String fileGroupName, int fileCount)54   public static DataFileGroup createDataFileGroup(String fileGroupName, int fileCount) {
55     DataFileGroup.Builder dataFileGroup = DataFileGroup.newBuilder().setGroupName(fileGroupName);
56     for (int i = 0; i < fileCount; ++i) {
57       com.google.mobiledatadownload.DownloadConfigProto.DataFile.Builder file =
58           com.google.mobiledatadownload.DownloadConfigProto.DataFile.newBuilder();
59       file.setFileId(String.format("%s_%s", fileGroupName, i));
60       file.setUrlToDownload(String.format("https://%s_%s", fileGroupName, i));
61       file.setByteSize(10 + i);
62       file.setChecksum("123" + i);
63       dataFileGroup.addFile(file.build());
64     }
65     return dataFileGroup.build();
66   }
67 
68   /**
69    * Creates an internal data file group with the given number of files. It only sets the field that
70    * are set in a data file group that we get from the server.
71    */
createDataFileGroupInternal( String fileGroupName, int fileCount)72   public static DataFileGroupInternal createDataFileGroupInternal(
73       String fileGroupName, int fileCount) {
74     DataFileGroupInternal.Builder dataFileGroupInternal =
75         DataFileGroupInternal.newBuilder().setGroupName(fileGroupName);
76     for (int i = 0; i < fileCount; ++i) {
77       dataFileGroupInternal.addFile(createDataFile(fileGroupName, i));
78     }
79     return dataFileGroupInternal.build();
80   }
81 
82   /**
83    * Creates an internal data file group with the given number of files. It only sets the field that
84    * are set in a data file group that we get from the server.
85    */
createDataFileGroupInternalWithDownloadId( String fileGroupName, int fileCount)86   public static DataFileGroupInternal createDataFileGroupInternalWithDownloadId(
87       String fileGroupName, int fileCount) {
88     DataFileGroupInternal.Builder dataFileGroupInternal =
89         DataFileGroupInternal.newBuilder().setGroupName(fileGroupName);
90     for (int i = 0; i < fileCount; ++i) {
91       dataFileGroupInternal.addFile(createDataFile(fileGroupName, i));
92     }
93     return dataFileGroupInternal.build();
94   }
95 
96   /**
97    * Creates an internal data file group with the given number of files, all configured to be
98    * shared. It only sets the field that are set in a data file group that we get from the server.
99    */
createSharedDataFileGroupInternal( String fileGroupName, int fileCount)100   public static DataFileGroupInternal createSharedDataFileGroupInternal(
101       String fileGroupName, int fileCount) {
102     DataFileGroupInternal.Builder dataFileGroupInternal =
103         DataFileGroupInternal.newBuilder().setGroupName(fileGroupName);
104     for (int i = 0; i < fileCount; ++i) {
105       dataFileGroupInternal.addFile(createSharedDataFile(fileGroupName, /* fileIndex= */ i));
106     }
107     return dataFileGroupInternal.build();
108   }
109 
110   /**
111    * Creates a data file group with the given number of files. It creates a downloaded file, so the
112    * file uri is also set.
113    */
createDownloadedDataFileGroupInternal( String fileGroupName, int fileCount)114   public static DataFileGroupInternal createDownloadedDataFileGroupInternal(
115       String fileGroupName, int fileCount) {
116     DataFileGroupInternal.Builder dataFileGroup =
117         DataFileGroupInternal.newBuilder().setGroupName(fileGroupName);
118     for (int i = 0; i < fileCount; ++i) {
119       dataFileGroup.addFile(createDownloadedDataFile(fileGroupName, i));
120     }
121     return dataFileGroup.build();
122   }
123 
createDownloadedDataFile(String fileId, int fileIndex)124   private static DataFile createDownloadedDataFile(String fileId, int fileIndex) {
125     DataFile file = createDataFile(fileId, fileIndex);
126     return file;
127   }
128 
createDataFile(String fileId, int fileIndex)129   public static DataFile createDataFile(String fileId, int fileIndex) {
130     DataFile.Builder file = DataFile.newBuilder();
131     file.setFileId(String.format("%s_%s", fileId, fileIndex));
132     file.setUrlToDownload(String.format("https://%s_%s", fileId, fileIndex));
133     file.setByteSize(10 + fileIndex);
134     file.setChecksum("123" + fileIndex);
135     return file.build();
136   }
137 
138   /**
139    * Creates a dataFile configured for sharing, i.e. with ChecksumType set to SHA256 and
140    * AndroidSharingType set to ANDROID_BLOB_WHEN_AVAILABLE.
141    */
createSharedDataFile(String fileId, int fileIndex)142   public static DataFile createSharedDataFile(String fileId, int fileIndex) {
143     DataFile.Builder file = DataFile.newBuilder();
144     file.setFileId(String.format("%s_%s", fileId, fileIndex));
145     file.setUrlToDownload(String.format("https://%s_%s", fileId, fileIndex));
146     file.setByteSize(10 + fileIndex);
147     file.setChecksum("123" + fileIndex);
148     file.setChecksumType(DataFile.ChecksumType.DEFAULT);
149     file.setAndroidSharingType(DataFile.AndroidSharingType.ANDROID_BLOB_WHEN_AVAILABLE);
150     file.setAndroidSharingChecksumType(DataFile.AndroidSharingChecksumType.SHA2_256);
151     file.setAndroidSharingChecksum("sha256_123" + fileIndex);
152     return file.build();
153   }
154 
155   /** Creates a dataFile with relative path. */
createRelativePathDataFile( String fileId, int fileIndex, String relativeFilePath)156   public static DataFile createRelativePathDataFile(
157       String fileId, int fileIndex, String relativeFilePath) {
158     return DataFile.newBuilder()
159         .setFileId(String.format("%s_%s", fileId, fileIndex))
160         .setUrlToDownload(String.format("https://%s_%s", fileId, fileIndex))
161         .setByteSize(10 + fileIndex)
162         .setChecksum("123" + fileIndex)
163         .setChecksumType(DataFile.ChecksumType.DEFAULT)
164         .setRelativeFilePath(relativeFilePath)
165         .build();
166   }
167 
createZipFolderDataFile(String fileId, int fileIndex)168   public static DataFile createZipFolderDataFile(String fileId, int fileIndex) {
169     DataFile.Builder file = DataFile.newBuilder();
170     file.setFileId(String.format("%s_%s", fileId, fileIndex));
171     file.setUrlToDownload(String.format("https://%s_%s", fileId, fileIndex));
172     file.setByteSize(10 + fileIndex);
173     file.setDownloadedFileChecksum("123" + fileIndex);
174     file.setDownloadTransforms(
175         Transforms.newBuilder()
176             .addTransform(
177                 Transform.newBuilder().setZip(ZipTransform.newBuilder().setTarget("*").build())));
178     return file.build();
179   }
180 
createFileGroupInternalWithDeltaFile(String fileGroupName)181   public static DataFileGroupInternal createFileGroupInternalWithDeltaFile(String fileGroupName) {
182     DataFileGroupInternal.Builder fileGroupBuilder =
183         MddTestUtil.createDataFileGroupInternal(fileGroupName, 1).toBuilder();
184     DataFileGroupInternal dataFileGroup =
185         fileGroupBuilder
186             .setFile(0, fileGroupBuilder.getFile(0).toBuilder().addDeltaFile(0, createDeltaFile()))
187             .build();
188     return dataFileGroup;
189   }
190 
createDeltaFile()191   public static DeltaFile createDeltaFile() {
192     return DeltaFile.newBuilder()
193         .setUrlToDownload("http://abc")
194         .setByteSize(10)
195         .setChecksum("ABC")
196         .setDiffDecoder(DiffDecoder.VC_DIFF)
197         .setBaseFile(createDeltaBaseFile("mychecksum"))
198         .build();
199   }
200 
createDeltaFile(String fileId, int fileIndex)201   public static DeltaFile createDeltaFile(String fileId, int fileIndex) {
202     return DeltaFile.newBuilder()
203         .setUrlToDownload(String.format("https://%s_%s", fileId, fileIndex))
204         .setByteSize(10 + fileIndex)
205         .setChecksum("123" + fileIndex)
206         .setDiffDecoder(DiffDecoder.VC_DIFF)
207         .setBaseFile(createDeltaBaseFile("mychecksum" + fileIndex))
208         .build();
209   }
210 
createDeltaBaseFile(String checksum)211   public static BaseFile createDeltaBaseFile(String checksum) {
212     return BaseFile.newBuilder().setChecksum(checksum).build();
213   }
214 
createFileKeysForDataFileGroupInternal(DataFileGroupInternal group)215   public static NewFileKey[] createFileKeysForDataFileGroupInternal(DataFileGroupInternal group) {
216     NewFileKey[] newFileKeys = new NewFileKey[group.getFileCount()];
217     for (int i = 0; i < group.getFileCount(); ++i) {
218       newFileKeys[i] =
219           SharedFilesMetadata.createKeyFromDataFile(
220               group.getFile(i), group.getAllowedReadersEnum());
221     }
222     return newFileKeys;
223   }
224 
assertMessageEquals(MessageLite expected, MessageLite actual)225   public static void assertMessageEquals(MessageLite expected, MessageLite actual) {
226     assertWithMessage(String.format("EXPECTED: %s\n ACTUAL: %s\n", expected, actual))
227         .that(expected.equals(actual))
228         .isTrue();
229   }
230 
createDataFileWithDeltaFile( String fileId, int fileIndex, int deltaFileCount)231   public static DataFile createDataFileWithDeltaFile(
232       String fileId, int fileIndex, int deltaFileCount) {
233     DataFile.Builder file =
234         DataFile.newBuilder()
235             .setFileId(String.format("%s_%s", fileId, fileIndex))
236             .setUrlToDownload(String.format("https://%s_%s", fileId, fileIndex))
237             .setByteSize(10 + fileIndex)
238             .setChecksum("123" + fileIndex);
239     for (int i = 0; i < deltaFileCount; i++) {
240       file.addDeltaFile(createDeltaFile(fileId + "_delta_" + i, i));
241     }
242     return file.build();
243   }
244 
245   /** Executes the shell command {@code cmd}. */
runShellCmd(String cmd)246   public static String runShellCmd(String cmd) throws IOException {
247     final UiDevice uiDevice = UiDevice.getInstance(getInstrumentation());
248     final String result = uiDevice.executeShellCommand(cmd).trim();
249     Log.i(TAG, "Output of '" + cmd + "': '" + result + "'");
250     return result;
251   }
252 
253   /** For API-level 19+, it moves the time forward by {@code timeInMillis} milliseconds. */
254 //  public static void timeTravel(Context context, long timeInMillis) {
255 //    if (VERSION.SDK_INT == 18) {
256 //      throw new UnsupportedOperationException(
257 //          "Time travel does not work on API-level 18 - b/31132161. "
258 //              + "You need to disable this test on API-level 18. Example: cl/131498720");
259 //    }
260 //
261 //    final long timestampBeforeTravel = System.currentTimeMillis();
262 //    if (!BackdoorTestUtil.advanceTime(context, timeInMillis)) {
263 //      // On some API levels (>23) the call returns false even if the time changed. Have a manual
264 //      // validation that the time changed instead.
265 //      if (VERSION.SDK_INT >= 23) {
266 //        assertThat(System.currentTimeMillis()).isAtLeast(timestampBeforeTravel + timeInMillis);
267 //      } else {
268 //        throw new IllegalStateException("Time Travel was not successful");
269 //      }
270 //    }
271 //  }
272 
273   /**
274    * @return the time (in seconds) that is n days from the current time
275    */
daysFromNow(int days)276   public static long daysFromNow(int days) {
277     long thenMillis = System.currentTimeMillis() + DAYS.toMillis(days);
278     return MILLISECONDS.toSeconds(thenMillis);
279   }
280 
281   /**
282    * Writes the SharedFile metadata for all the files stored in the {@code fileGroup}, setting for
283    * each of them the status and whether they are currently android-shared based on the array
284    * position.
285    */
writeSharedFiles( SharedFilesMetadata sharedFilesMetadata, DataFileGroupInternal fileGroup, List<FileStatus> statuses, List<Boolean> androidShared)286   public static void writeSharedFiles(
287       SharedFilesMetadata sharedFilesMetadata,
288       DataFileGroupInternal fileGroup,
289       List<FileStatus> statuses,
290       List<Boolean> androidShared)
291       throws Exception {
292     int size = fileGroup.getFileCount();
293     NewFileKey[] keys = createFileKeysForDataFileGroupInternal(fileGroup);
294     assertWithMessage("Created file keys must match the given DataFileGroup's file count")
295         .that(keys.length)
296         .isEqualTo(size);
297     assertWithMessage("Given FileStatus list must match the given DataFileGroup's file count")
298         .that(statuses.size())
299         .isEqualTo(size);
300     assertWithMessage("Given androidShared list must match the given DataFileGroup's file count")
301         .that(androidShared.size())
302         .isEqualTo(size);
303     for (int i = 0; i < fileGroup.getFileCount(); i++) {
304       DataFile file = fileGroup.getFile(i);
305       SharedFile.Builder sharedFileBuilder =
306           SharedFile.newBuilder().setFileName(file.getFileId()).setFileStatus(statuses.get(i));
307       if (androidShared.get(i)) {
308         sharedFileBuilder.setAndroidShared(true).setAndroidSharingChecksum("sha256_123" + i);
309       }
310       sharedFilesMetadata.write(keys[i], sharedFileBuilder.build()).get();
311     }
312   }
313 
314   /**
315    * Convenience method for {@link MddTestUtil#writeSharedFiles(SharedFilesMetadata,
316    * DataFileGroupInternal, List<FileStatus>, List<Boolean>)} when android shared status is
317    * unnecessary.
318    */
writeSharedFiles( SharedFilesMetadata sharedFilesMetadata, DataFileGroupInternal fileGroup, List<FileStatus> statuses)319   public static void writeSharedFiles(
320       SharedFilesMetadata sharedFilesMetadata,
321       DataFileGroupInternal fileGroup,
322       List<FileStatus> statuses)
323       throws Exception {
324     int size = fileGroup.getFileCount();
325     List<Boolean> androidShared = Collections.nCopies(size, false);
326     writeSharedFiles(sharedFilesMetadata, fileGroup, statuses, androidShared);
327   }
328 }
329