1 /*
2  * Copyright (C) 2020 The Android Open Source Project
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 
17 package com.android.csuite.core;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.testng.Assert.assertThrows;
22 
23 import com.android.tradefed.build.BuildInfo;
24 import com.android.tradefed.config.Configuration;
25 import com.android.tradefed.config.IConfiguration;
26 import com.android.tradefed.config.OptionSetter;
27 import com.android.tradefed.device.ITestDevice;
28 import com.android.tradefed.invoker.IInvocationContext;
29 import com.android.tradefed.invoker.InvocationContext;
30 import com.android.tradefed.invoker.TestInformation;
31 
32 import com.google.common.collect.ImmutableList;
33 import com.google.common.jimfs.Jimfs;
34 import com.google.common.truth.IterableSubject;
35 import com.google.common.truth.StringSubject;
36 
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.JUnit4;
40 import org.mockito.Mockito;
41 
42 import java.io.IOException;
43 import java.io.UncheckedIOException;
44 import java.nio.file.FileSystem;
45 import java.nio.file.Files;
46 import java.nio.file.Path;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.List;
50 import java.util.concurrent.atomic.AtomicBoolean;
51 import java.util.stream.Stream;
52 
53 @RunWith(JUnit4.class)
54 public final class ModuleGeneratorTest {
55     private static final String TEST_PACKAGE_NAME1 = "test.package.name1";
56     private static final String TEST_PACKAGE_NAME2 = "test.package.name2";
57     private static final String TEST_PACKAGE_NAME3 = "test.package.name3";
58     private static final Exception NO_EXCEPTION = null;
59 
60     private final FileSystem mFileSystem =
61             Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix());
62 
63     @Test
tearDown_nonModuleFilesExist_doesNotDelete()64     public void tearDown_nonModuleFilesExist_doesNotDelete() throws Exception {
65         Path testsDir = createTestsDir();
66         Path nonModule = Files.createFile(testsDir.resolve("a"));
67         ModuleGenerator generator = new GeneratorBuilder().setTestsDir(testsDir).build();
68 
69         generator.tearDown(createTestInfo(), NO_EXCEPTION);
70 
71         assertThatListDirectory(testsDir).containsExactly(nonModule);
72     }
73 
74     @Test
tearDown_preserveNonGeneratedModules_doesNotDelete()75     public void tearDown_preserveNonGeneratedModules_doesNotDelete() throws Exception {
76         Path testsDir = createTestsDir();
77         Path nonGeneratedModule =
78                 Files.createFile(
79                         testsDir.resolve("b" + ModuleGenerator.MODULE_FILE_NAME_EXTENSION));
80         ModuleGenerator sut = new GeneratorBuilder().setTestsDir(testsDir).build();
81         OptionSetter option = new OptionSetter(sut);
82         option.setOptionValue(ModuleGenerator.OPTION_PRESERVE_EXISTING_MODULES, "true");
83 
84         sut.tearDown(createTestInfo(), NO_EXCEPTION);
85 
86         assertThatListDirectory(testsDir).containsExactly(nonGeneratedModule);
87     }
88 
89     @Test
tearDown_doesNotPreserveNonGeneratedModules_delete()90     public void tearDown_doesNotPreserveNonGeneratedModules_delete() throws Exception {
91         Path testsDir = createTestsDir();
92         Path nonGeneratedModule =
93                 Files.createFile(
94                         testsDir.resolve("b" + ModuleGenerator.MODULE_FILE_NAME_EXTENSION));
95         ModuleGenerator sut = new GeneratorBuilder().setTestsDir(testsDir).build();
96         OptionSetter option = new OptionSetter(sut);
97         option.setOptionValue(ModuleGenerator.OPTION_PRESERVE_EXISTING_MODULES, "false");
98 
99         sut.tearDown(createTestInfo(), NO_EXCEPTION);
100 
101         assertThatListDirectory(testsDir).doesNotContain(nonGeneratedModule);
102     }
103 
104     @Test
tearDown_packageNamesProvided_deletesGeneratedModules()105     public void tearDown_packageNamesProvided_deletesGeneratedModules() throws Exception {
106         Path testsDir = createTestsDir();
107         ModuleGenerator generator1 =
108                 new GeneratorBuilder()
109                         .setTestsDir(testsDir)
110                         .addModuleInfoProvider(
111                                 config ->
112                                         Stream.of(
113                                                 new ModuleInfoProvider.ModuleInfo(
114                                                         TEST_PACKAGE_NAME1, "")))
115                         .addModuleInfoProvider(
116                                 config ->
117                                         Stream.of(
118                                                 new ModuleInfoProvider.ModuleInfo(
119                                                         TEST_PACKAGE_NAME2, "")))
120                         .build();
121         generator1.split();
122         ModuleGenerator generator2 = new GeneratorBuilder().setTestsDir(testsDir).build();
123 
124         generator2.tearDown(createTestInfo(), NO_EXCEPTION);
125 
126         assertThatListDirectory(testsDir).isEmpty();
127     }
128 
129     @Test
tearDown_moduleInfoNotProvided_doesNotThrowError()130     public void tearDown_moduleInfoNotProvided_doesNotThrowError() throws Exception {
131         ModuleGenerator generator = new GeneratorBuilder().setTestsDir(createTestsDir()).build();
132         generator.split();
133 
134         generator.tearDown(createTestInfo(), NO_EXCEPTION);
135     }
136 
137     @Test
split_moduleInfoStreamProvided_streamIsClosed()138     public void split_moduleInfoStreamProvided_streamIsClosed() throws Exception {
139         AtomicBoolean wasClosed = new AtomicBoolean(false);
140         ModuleGenerator generator =
141                 new GeneratorBuilder()
142                         .setTestsDir(createTestsDir())
143                         .addModuleInfoProvider(
144                                 config ->
145                                         Stream.of(
146                                                         new ModuleInfoProvider.ModuleInfo(
147                                                                 TEST_PACKAGE_NAME1, ""))
148                                                 .onClose(() -> wasClosed.set(true)))
149                         .build();
150 
151         generator.split();
152 
153         assertThat(wasClosed.get()).isTrue();
154     }
155 
156     @Test
split_moduleInfoProvidersSpecified_contentIsWritten()157     public void split_moduleInfoProvidersSpecified_contentIsWritten() throws Exception {
158         Path testsDir = createTestsDir();
159         String content1 = "a";
160         String content2 = "b";
161         ModuleGenerator generator =
162                 new GeneratorBuilder()
163                         .setTestsDir(testsDir)
164                         .addModuleInfoProvider(
165                                 config ->
166                                         Stream.of(
167                                                 new ModuleInfoProvider.ModuleInfo(
168                                                         TEST_PACKAGE_NAME1, content1)))
169                         .addModuleInfoProvider(
170                                 config ->
171                                         Stream.of(
172                                                 new ModuleInfoProvider.ModuleInfo(
173                                                         TEST_PACKAGE_NAME2, content2)))
174                         .build();
175 
176         generator.split();
177 
178         assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME1).contains(content1);
179         assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME2).contains(content2);
180     }
181 
182     @Test
split_emptyModuleNameProvided_throwsException()183     public void split_emptyModuleNameProvided_throwsException() throws Exception {
184         Path testsDir = createTestsDir();
185         ModuleGenerator generator =
186                 new GeneratorBuilder()
187                         .setTestsDir(testsDir)
188                         .addModuleInfoProvider(
189                                 config -> Stream.of(new ModuleInfoProvider.ModuleInfo(" ", "a")))
190                         .build();
191 
192         assertThrows(IllegalArgumentException.class, () -> generator.split());
193     }
194 
195     @Test
split_duplicatedModuleNamesProvided_throwsException()196     public void split_duplicatedModuleNamesProvided_throwsException() throws Exception {
197         Path testsDir = createTestsDir();
198         ModuleGenerator generator =
199                 new GeneratorBuilder()
200                         .setTestsDir(testsDir)
201                         .addModuleInfoProvider(
202                                 config ->
203                                         Stream.of(
204                                                 new ModuleInfoProvider.ModuleInfo(
205                                                         TEST_PACKAGE_NAME1, "a")))
206                         .addModuleInfoProvider(
207                                 config ->
208                                         Stream.of(
209                                                 new ModuleInfoProvider.ModuleInfo(
210                                                         TEST_PACKAGE_NAME1, "b")))
211                         .build();
212 
213         assertThrows(IllegalArgumentException.class, () -> generator.split());
214     }
215 
216     @Test
split_moduleInfoProvidersSpecified_generateModulesForAll()217     public void split_moduleInfoProvidersSpecified_generateModulesForAll() throws Exception {
218         Path testsDir = createTestsDir();
219         ModuleGenerator generator =
220                 new GeneratorBuilder()
221                         .setTestsDir(testsDir)
222                         .addModuleInfoProvider(
223                                 config ->
224                                         Arrays.asList(
225                                                 new ModuleInfoProvider.ModuleInfo(
226                                                         TEST_PACKAGE_NAME1, ""),
227                                                 new ModuleInfoProvider.ModuleInfo(
228                                                         TEST_PACKAGE_NAME2, ""))
229                                                 .stream())
230                         .addModuleInfoProvider(
231                                 config ->
232                                         Stream.of(
233                                                 new ModuleInfoProvider.ModuleInfo(
234                                                         TEST_PACKAGE_NAME3, "")))
235                         .build();
236 
237         generator.split();
238 
239         assertThatListDirectory(testsDir)
240                 .containsExactly(
241                         getModuleConfigFile(testsDir, TEST_PACKAGE_NAME1),
242                         getModuleConfigFile(testsDir, TEST_PACKAGE_NAME2),
243                         getModuleConfigFile(testsDir, TEST_PACKAGE_NAME3));
244     }
245 
246     @Test
split_streamThrowsException_throwsException()247     public void split_streamThrowsException_throwsException() throws Exception {
248         ModuleGenerator generator =
249                 new GeneratorBuilder()
250                         .setTestsDir(createTestsDir())
251                         .addModuleInfoProvider(
252                                 config ->
253                                         Arrays.stream(new String[] {"a"})
254                                                 .map(
255                                                         i -> {
256                                                             throw new UncheckedIOException(
257                                                                     new IOException());
258                                                         }))
259                         .build();
260 
261         assertThrows(UncheckedIOException.class, () -> generator.split());
262     }
263 
264     @Test
split_providerThrowsException_throwsException()265     public void split_providerThrowsException_throwsException() throws Exception {
266         ModuleGenerator generator =
267                 new GeneratorBuilder()
268                         .setTestsDir(createTestsDir())
269                         .addModuleInfoProvider(
270                                 config -> {
271                                     throw new UncheckedIOException(new IOException());
272                                 })
273                         .build();
274 
275         assertThrows(UncheckedIOException.class, () -> generator.split());
276     }
277 
278     @Test
split_noProviders_doesNotGenerate()279     public void split_noProviders_doesNotGenerate() throws Exception {
280         Path testsDir = createTestsDir();
281         ModuleGenerator generator = new GeneratorBuilder().setTestsDir(testsDir).build();
282 
283         generator.split();
284 
285         assertThatListDirectory(testsDir).isEmpty();
286     }
287 
assertThatModuleConfigFileContent( Path testsDir, String packageName)288     private static StringSubject assertThatModuleConfigFileContent(
289             Path testsDir, String packageName) throws IOException {
290         return assertThat(
291                 new String(Files.readAllBytes(getModuleConfigFile(testsDir, packageName))));
292     }
293 
assertThatListDirectory(Path dir)294     private static IterableSubject assertThatListDirectory(Path dir) throws IOException {
295         // Convert stream to list because com.google.common.truth.Truth8 is not available.
296         return assertThat(
297                 Files.walk(dir)
298                         .filter(p -> !p.equals(dir))
299                         .collect(ImmutableList.toImmutableList()));
300     }
301 
getModuleConfigFile(Path baseDir, String packageName)302     private static Path getModuleConfigFile(Path baseDir, String packageName) {
303         return baseDir.resolve(packageName + ".config");
304     }
305 
createTestsDir()306     private Path createTestsDir() throws IOException {
307         Path rootPath = mFileSystem.getPath("csuite");
308         Files.createDirectories(rootPath);
309         return Files.createTempDirectory(rootPath, "testDir");
310     }
311 
312     private static final class GeneratorBuilder {
313         private final List<ModuleInfoProvider> mModuleInfoProviders = new ArrayList<>();
314         private Path mTestsDir;
315 
addModuleInfoProvider(ModuleInfoProvider moduleInfoProviders)316         GeneratorBuilder addModuleInfoProvider(ModuleInfoProvider moduleInfoProviders) {
317             mModuleInfoProviders.add(moduleInfoProviders);
318             return this;
319         }
320 
setTestsDir(Path testsDir)321         GeneratorBuilder setTestsDir(Path testsDir) {
322             mTestsDir = testsDir;
323             return this;
324         }
325 
build()326         ModuleGenerator build() throws Exception {
327             ModuleGenerator generator = new ModuleGenerator(buildInfo -> mTestsDir);
328 
329             IConfiguration configuration = new Configuration("name", "description");
330             configuration.setConfigurationObjectList(
331                     ModuleInfoProvider.MODULE_INFO_PROVIDER_OBJECT_TYPE, mModuleInfoProviders);
332             generator.setConfiguration(configuration);
333 
334             return generator;
335         }
336     }
337 
createTestInfo()338     private static TestInformation createTestInfo() {
339         IInvocationContext context = new InvocationContext();
340         context.addAllocatedDevice("device1", Mockito.mock(ITestDevice.class));
341         context.addDeviceBuildInfo("device1", new BuildInfo());
342         return TestInformation.newBuilder().setInvocationContext(context).build();
343     }
344 }
345