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.testing; 17 18 import android.content.Context; 19 20 import com.google.android.libraries.mobiledatadownload.TimeSource; 21 import com.google.android.libraries.mobiledatadownload.internal.logging.LoggingStateStore; 22 import com.google.android.libraries.mobiledatadownload.internal.logging.SharedPreferencesLoggingState; 23 import com.google.common.base.Optional; 24 import com.google.common.util.concurrent.ListeningExecutorService; 25 import com.google.common.util.concurrent.MoreExecutors; 26 import com.google.common.util.concurrent.ThreadFactoryBuilder; 27 28 import java.util.Random; 29 import java.util.concurrent.Executor; 30 import java.util.concurrent.Executors; 31 32 /** 33 * Utility class that provides support for building MDD with different types of dependencies for 34 * Testing. 35 * 36 * <p>If multiple type of dependencies need to be supported across tests, they can be defined here 37 * so all tests can rely on a single definition. This is useful for parameterizing tests, such as 38 * the case for ControlExecutor: 39 * 40 * <pre>{@code 41 * // In the test, define a parameter for ExecutorType 42 * @TestParameter ExecutorType controlExecutorType; 43 * 44 * // When building MDD in the test, rely on the shared provider: 45 * MobileDataDownloadBuilder.newBuilder() 46 * .setControlExecutor(controlExecutorType.executor()) 47 * // include other dependencies... 48 * .build(); 49 * 50 * }</pre> 51 */ 52 public final class MddTestDependencies { 53 54 private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz"; 55 private static final int INSTANCE_ID_CHAR_LIMIT = 10; 56 private static final Random random = new Random(); 57 MddTestDependencies()58 private MddTestDependencies() { 59 } 60 61 /** 62 * Generates a random instance id. 63 * 64 * <p>This prevents potential cross test conflicts from occurring since metadata will be siloed 65 * between tests. 66 */ randomInstanceId()67 public static String randomInstanceId() { 68 StringBuilder sb = new StringBuilder(); 69 for (int i = 0; i < INSTANCE_ID_CHAR_LIMIT; i++) { 70 sb.append(ALPHABET.charAt(random.nextInt(ALPHABET.length()))); 71 } 72 return sb.toString(); 73 } 74 75 /** 76 * Type of executor passed when building MDD. 77 * 78 * <p>Used for parameterizing tests. 79 */ 80 public enum ExecutorType { 81 SINGLE_THREADED, 82 MULTI_THREADED; 83 executor()84 public ListeningExecutorService executor() { 85 switch (this) { 86 case SINGLE_THREADED: 87 return MoreExecutors.listeningDecorator( 88 Executors.newSingleThreadExecutor( 89 new ThreadFactoryBuilder().setNameFormat( 90 "MddSingleThreaded-%d").build())); 91 case MULTI_THREADED: 92 return MoreExecutors.listeningDecorator( 93 Executors.newCachedThreadPool( 94 new ThreadFactoryBuilder().setNameFormat( 95 "MddMultiThreaded-%d").build())); 96 } 97 throw new AssertionError("ExecutorType unsupported"); 98 } 99 } 100 101 /** 102 * Differentiates between Downloader Configurations. 103 * 104 * <p>Used for parameterizing tests, as well as for making configuration-specific test 105 * assertions. 106 */ 107 // public enum DownloaderConfigurationType { 108 // V2_PLATFORM; 109 // 110 // public Supplier<FileDownloader> fileDownloaderSupplier( 111 // Context context, 112 // ListeningExecutorService controlExecutor, 113 // ListeningScheduledExecutorService downloadExecutor, 114 // SynchronousFileStorage fileStorage, 115 // Flags flags, 116 // Optional<DownloadProgressMonitor> downloadProgressMonitor, 117 // Optional<String> instanceId) { 118 // 119 // // Set up file downloader supplier based on the configuration given 120 // switch (this) { 121 // case V2_PLATFORM: 122 // return () -> { 123 // return BaseFileDownloaderModule.createOffroad2FileDownloader( 124 // context, 125 // downloadExecutor, 126 // controlExecutor, 127 // fileStorage, 128 // new SharedPreferencesDownloadMetadata( 129 // context.getSharedPreferences("downloadmetadata", 0), 130 // controlExecutor), 131 // /* downloadProgressMonitor= */ downloadProgressMonitor, 132 // /* urlEngineOptional= */ Optional.absent(), 133 // /* exceptionHandlerOptional= */ Optional.absent(), 134 // /* authTokenProviderOptional= */ Optional.absent(), 135 //// /* cookieJarSupplierOptional= */ Optional.absent(), 136 // /* trafficTag= */ Optional.absent(), 137 // flags); 138 // }; 139 // } 140 // throw new AssertionError("Invalid DownloaderConfigurationType"); 141 // } 142 // } 143 144 /** 145 * Differentiates between LoggingStateStore implementations. 146 * 147 * <p>Used for parameterizing tests, as well as for making configuration-specific test 148 * assertions. 149 */ 150 public enum LoggingStateStoreImpl { 151 SHARED_PREFERENCES; 152 loggingStateStore( Context context, Optional<String> instanceIdOptional, TimeSource timeSource, Executor backgroundExecutor, Random random)153 public LoggingStateStore loggingStateStore( 154 Context context, 155 Optional<String> instanceIdOptional, 156 TimeSource timeSource, 157 Executor backgroundExecutor, 158 Random random) { 159 160 // Set up file downloader supplier based on the configuration given 161 switch (this) { 162 case SHARED_PREFERENCES: 163 return SharedPreferencesLoggingState.createFromContext( 164 context, instanceIdOptional, timeSource, backgroundExecutor, random); 165 } 166 throw new AssertionError("Invalid LoggingStateStoreImpl"); 167 } 168 } 169 } 170