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.file.common.testing; 17 18 import static org.mockito.AdditionalAnswers.returnsSecondArg; 19 import static org.mockito.ArgumentMatchers.any; 20 import static org.mockito.Mockito.when; 21 22 import android.net.Uri; 23 import android.util.Pair; 24 import com.google.android.libraries.mobiledatadownload.file.spi.Backend; 25 import com.google.android.libraries.mobiledatadownload.file.spi.Monitor; 26 import com.google.android.libraries.mobiledatadownload.file.spi.Transform; 27 import java.io.ByteArrayInputStream; 28 import java.io.ByteArrayOutputStream; 29 import java.io.Closeable; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 import org.junit.Before; 34 import org.junit.Rule; 35 import org.mockito.Mock; 36 import org.mockito.junit.MockitoJUnit; 37 import org.mockito.junit.MockitoRule; 38 39 /** 40 * Base class with common code for file storage tests. 41 * 42 * <p>Mocks use "file", "cns", "compress" and "encrypt" to make the tests easier to follow, but they 43 * do not implement any of said behaviors. 44 */ 45 public abstract class FileStorageTestBase { 46 47 @Rule public final MockitoRule mockito = MockitoJUnit.rule(); 48 49 protected final Uri file1Uri; 50 protected final Uri file2Uri; 51 protected final Uri file3Uri; 52 protected final Uri dir1Uri; 53 protected final String file1Filename; 54 protected final String file2Filename; 55 protected final Uri cnsUri; 56 protected final Uri file1CompressUri; 57 protected final Uri file1CompressUriWithEncoded; 58 protected final Uri file2CompressEncryptUri; 59 protected final Uri file3IdentityUri; 60 protected final Uri dir2CompressUri; 61 protected final Uri uriWithCompressParam; 62 protected final Uri uriWithCompressParamWithEncoded; 63 protected final Uri uriWithEncryptParam; 64 protected final Uri uriWithIdentityParam; 65 66 private static final InputStream EMPTY_INPUT = new ByteArrayInputStream(new byte[] {}); 67 private static final OutputStream NULL_OUTPUT = 68 new OutputStream() { 69 @Override 70 public void write(int i) throws IOException { 71 // ignore 72 } 73 }; 74 75 @Mock protected Backend fileBackend; 76 @Mock protected Backend cnsBackend; 77 @Mock protected Transform compressTransform; 78 @Mock protected Transform encryptTransform; 79 @Mock protected Monitor countingMonitor; 80 81 protected InputStream compressInputStream = new ByteArrayInputStream(new byte[] {}); 82 protected InputStream encryptInputStream = new ByteArrayInputStream(new byte[] {}); 83 protected OutputStream compressOutputStream = new ByteArrayOutputStream(); 84 protected OutputStream encryptOutputStream = new ByteArrayOutputStream(); 85 protected Closeable closeable = () -> {}; 86 87 protected final Transform identityTransform = 88 new Transform() { 89 @Override 90 public String name() { 91 return "identity"; 92 } 93 }; 94 FileStorageTestBase()95 protected FileStorageTestBase() { 96 // Robolectric doesn't seem to work with static initializers. 97 file1Uri = Uri.parse("file:///file1"); 98 file2Uri = Uri.parse("file:///file2"); 99 file3Uri = Uri.parse("file:///file3"); 100 dir1Uri = Uri.parse("file:///dir1/"); 101 file1Filename = "file1"; 102 file2Filename = "file2"; 103 cnsUri = Uri.parse("cns:///1"); 104 file1CompressUri = Uri.parse("file:///file1#transform=compress"); 105 // Percent encoding of fragment params is not in our spec (<internal>), but 106 // implemented anyway. 107 file1CompressUriWithEncoded = Uri.parse("file:///file1#transform=compress(k%3D=%28v%29)"); 108 file2CompressEncryptUri = Uri.parse("file:///file2#transform=compress+encrypt"); 109 file3IdentityUri = Uri.parse("file:///file3#transform=identity"); 110 dir2CompressUri = Uri.parse("file:///dir2/#transform=compress"); 111 uriWithCompressParam = Uri.parse("#transform=compress"); 112 uriWithCompressParamWithEncoded = Uri.parse("#transform=compress(k%3D=%28v%29)"); 113 uriWithEncryptParam = Uri.parse("#transform=encrypt"); 114 uriWithIdentityParam = Uri.parse("#transform=identity"); 115 } 116 117 @Before initMocksAndCreateFileStorage()118 public void initMocksAndCreateFileStorage() throws Exception { 119 when(fileBackend.name()).thenReturn("file"); 120 when(fileBackend.openForRead(any())).thenReturn(EMPTY_INPUT); 121 when(fileBackend.openForWrite(any())).thenReturn(NULL_OUTPUT); 122 when(fileBackend.openForAppend(any())).thenReturn(NULL_OUTPUT); 123 when(cnsBackend.openForRead(any())).thenReturn(EMPTY_INPUT); 124 when(cnsBackend.openForWrite(any())).thenReturn(NULL_OUTPUT); 125 when(cnsBackend.openForAppend(any())).thenReturn(NULL_OUTPUT); 126 when(cnsBackend.name()).thenReturn("cns"); 127 when(compressTransform.name()).thenReturn("compress"); 128 when(compressTransform.encode(any(), any())).then(returnsSecondArg()); 129 when(compressTransform.decode(any(), any())).then(returnsSecondArg()); 130 when(encryptTransform.name()).thenReturn("encrypt"); 131 when(encryptTransform.encode(any(), any())).then(returnsSecondArg()); 132 when(encryptTransform.decode(any(), any())).then(returnsSecondArg()); 133 when(fileBackend.openForNativeRead(any())) 134 .thenReturn(Pair.create(Uri.parse("fd:123"), closeable)); 135 when(cnsBackend.openForNativeRead(any())) 136 .thenReturn(Pair.create(Uri.parse("fd:456"), closeable)); 137 initStorage(); 138 } 139 140 // Called after mocks are initialized to create the FileStorage instance. 141 // Required b/c order of @Befores is non-deterministic. initStorage()142 protected abstract void initStorage(); 143 } 144