1 /*
2  * Copyright (C) 2023 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 package com.android.adservices.shared.testing;
17 
18 import static com.android.adservices.shared.testing.concurrency.SyncCallback.LOG_TAG;
19 
20 import com.android.adservices.shared.testing.DeviceConfigHelper.SyncDisabledModeForTest;
21 import com.android.adservices.shared.testing.Logger.LogLevel;
22 import com.android.adservices.shared.testing.Logger.RealLogger;
23 import com.android.adservices.shared.testing.NameValuePair.Matcher;
24 import com.android.adservices.shared.testing.annotations.DisableDebugFlag;
25 import com.android.adservices.shared.testing.annotations.DisableDebugFlags;
26 import com.android.adservices.shared.testing.annotations.EnableDebugFlag;
27 import com.android.adservices.shared.testing.annotations.EnableDebugFlags;
28 import com.android.adservices.shared.testing.annotations.SetDoubleFlag;
29 import com.android.adservices.shared.testing.annotations.SetDoubleFlags;
30 import com.android.adservices.shared.testing.annotations.SetFlagDisabled;
31 import com.android.adservices.shared.testing.annotations.SetFlagEnabled;
32 import com.android.adservices.shared.testing.annotations.SetFlagFalse;
33 import com.android.adservices.shared.testing.annotations.SetFlagTrue;
34 import com.android.adservices.shared.testing.annotations.SetFlagsDisabled;
35 import com.android.adservices.shared.testing.annotations.SetFlagsEnabled;
36 import com.android.adservices.shared.testing.annotations.SetFlagsFalse;
37 import com.android.adservices.shared.testing.annotations.SetFlagsTrue;
38 import com.android.adservices.shared.testing.annotations.SetFloatFlag;
39 import com.android.adservices.shared.testing.annotations.SetFloatFlags;
40 import com.android.adservices.shared.testing.annotations.SetIntegerFlag;
41 import com.android.adservices.shared.testing.annotations.SetIntegerFlags;
42 import com.android.adservices.shared.testing.annotations.SetLogcatTag;
43 import com.android.adservices.shared.testing.annotations.SetLogcatTags;
44 import com.android.adservices.shared.testing.annotations.SetLongDebugFlag;
45 import com.android.adservices.shared.testing.annotations.SetLongDebugFlags;
46 import com.android.adservices.shared.testing.annotations.SetLongFlag;
47 import com.android.adservices.shared.testing.annotations.SetLongFlags;
48 import com.android.adservices.shared.testing.annotations.SetStringArrayFlag;
49 import com.android.adservices.shared.testing.annotations.SetStringArrayFlags;
50 import com.android.adservices.shared.testing.annotations.SetStringFlag;
51 import com.android.adservices.shared.testing.annotations.SetStringFlags;
52 
53 import com.google.errorprone.annotations.FormatMethod;
54 import com.google.errorprone.annotations.FormatString;
55 
56 import org.junit.runner.Description;
57 import org.junit.runners.model.Statement;
58 
59 import java.lang.annotation.Annotation;
60 import java.util.ArrayList;
61 import java.util.LinkedHashSet;
62 import java.util.List;
63 import java.util.Objects;
64 import java.util.Set;
65 
66 // TODO(b/294423183): add unit tests for the most relevant / less repetitive stuff (don't need to
67 // test all setters / getters, for example)
68 /**
69  * Rule used to properly set "Android flags"- it will take care of permissions, restoring values at
70  * the end, setting {@link android.provider.DeviceConfig} or {@link android.os.SystemProperties},
71  * etc...
72  */
73 public abstract class AbstractFlagsSetterRule<T extends AbstractFlagsSetterRule<T>>
74         extends AbstractRethrowerRule {
75 
76     protected static final String SYSTEM_PROPERTY_FOR_LOGCAT_TAGS_PREFIX = "log.tag.";
77 
78     private final String mDeviceConfigNamespace;
79     private final DeviceConfigHelper mDeviceConfig;
80     // TODO(b/338067482): move system properties to its own rule?
81     // Prefix used on SystemProperties used for DebugFlags
82     private final String mDebugFlagPrefix;
83     private final SystemPropertiesHelper mSystemProperties;
84 
85     // Cache methods that were called before the test started, so the rule can be
86     // instantiated using a builder-like approach.
87     private final List<Command> mInitialCommands = new ArrayList<>();
88 
89     // Name of flags that were changed by the test
90     private final Set<String> mChangedFlags = new LinkedHashSet<>();
91     // Name of system properties that were changed by the test
92     private final Set<String> mChangedSystemProperties = new LinkedHashSet<>();
93     private final Matcher mSystemPropertiesMatcher;
94 
95     private final List<NameValuePair> mPreTestFlags = new ArrayList<>();
96     private final List<NameValuePair> mPreTestSystemProperties = new ArrayList<>();
97     private final List<NameValuePair> mOnTestFailureFlags = new ArrayList<>();
98     private final List<NameValuePair> mOnTestFailureSystemProperties = new ArrayList<>();
99 
100     private final SyncDisabledModeForTest mPreviousSyncDisabledModeForTest;
101 
102     private boolean mIsRunning;
103     private boolean mFlagsClearedByTest;
104 
AbstractFlagsSetterRule( RealLogger logger, String deviceConfigNamespace, String debugFlagPrefix, DeviceConfigHelper.InterfaceFactory deviceConfigInterfaceFactory, SystemPropertiesHelper.Interface systemPropertiesInterface)105     protected AbstractFlagsSetterRule(
106             RealLogger logger,
107             String deviceConfigNamespace,
108             String debugFlagPrefix,
109             DeviceConfigHelper.InterfaceFactory deviceConfigInterfaceFactory,
110             SystemPropertiesHelper.Interface systemPropertiesInterface) {
111         this(
112                 logger,
113                 deviceConfigNamespace,
114                 debugFlagPrefix,
115                 // TODO(b/294423183, 328682831): should not be necessary, but integrated with
116                 // setLogcatTag()
117                 (prop) ->
118                         prop.name.startsWith(debugFlagPrefix)
119                                 || prop.name.startsWith(SYSTEM_PROPERTY_FOR_LOGCAT_TAGS_PREFIX),
120                 deviceConfigInterfaceFactory,
121                 systemPropertiesInterface);
122     }
123 
AbstractFlagsSetterRule( RealLogger logger, String deviceConfigNamespace, String debugFlagPrefix, Matcher systemPropertiesMatcher, DeviceConfigHelper.InterfaceFactory deviceConfigInterfaceFactory, SystemPropertiesHelper.Interface systemPropertiesInterface)124     protected AbstractFlagsSetterRule(
125             RealLogger logger,
126             String deviceConfigNamespace,
127             String debugFlagPrefix,
128             Matcher systemPropertiesMatcher,
129             DeviceConfigHelper.InterfaceFactory deviceConfigInterfaceFactory,
130             SystemPropertiesHelper.Interface systemPropertiesInterface) {
131 
132         super(logger);
133 
134         mDeviceConfigNamespace = Objects.requireNonNull(deviceConfigNamespace);
135         mDebugFlagPrefix = Objects.requireNonNull(debugFlagPrefix);
136         mSystemPropertiesMatcher = Objects.requireNonNull(systemPropertiesMatcher);
137         mDeviceConfig =
138                 new DeviceConfigHelper(deviceConfigInterfaceFactory, deviceConfigNamespace, logger);
139         mSystemProperties = new SystemPropertiesHelper(systemPropertiesInterface, logger);
140         // TODO(b/294423183): ideally the current mode should be returned by
141         // setSyncDisabledMode(),
142         // but unfortunately getting the current mode is not straightforward due to
143         // different
144         // behaviors:
145         // - T+ provides get_sync_disabled_for_tests
146         // - S provides is_sync_disabled_for_tests
147         // - R doesn't provide anything
148         mPreviousSyncDisabledModeForTest = SyncDisabledModeForTest.NONE;
149         // Must set right away to avoid race conditions (for example, backend setting flags before
150         // apply() is called)
151         setSyncDisabledMode(SyncDisabledModeForTest.PERSISTENT);
152 
153         mLog.v(
154                 "Constructor: mDeviceConfigNamespace=%s,"
155                         + " mDebugFlagPrefix=%s,mDeviceConfig=%s, mSystemProperties=%s,"
156                         + " mPreviousSyncDisabledModeForTest=%s",
157                 mDeviceConfigNamespace,
158                 mDebugFlagPrefix,
159                 mDeviceConfig,
160                 mSystemProperties,
161                 mPreviousSyncDisabledModeForTest);
162     }
163 
164     @Override
preTest(Statement base, Description description, List<Throwable> cleanUpErrors)165     protected void preTest(Statement base, Description description, List<Throwable> cleanUpErrors) {
166         String testName = TestHelper.getTestName(description);
167         mIsRunning = true;
168 
169         // TODO(b/294423183): ideally should be "setupErrors", but it's not used yet (other
170         // than logging), so it doesn't matter
171         runSafely(cleanUpErrors, () -> mPreTestFlags.addAll(mDeviceConfig.getAll()));
172         runSafely(
173                 cleanUpErrors,
174                 () ->
175                         mPreTestSystemProperties.addAll(
176                                 mSystemProperties.getAll(mSystemPropertiesMatcher)));
177 
178         setAnnotatedFlags(description);
179         runInitialCommands(testName);
180     }
181 
182     @Override
onTestFailure( Statement base, Description description, List<Throwable> cleanUpErrors, Throwable testFailure)183     protected void onTestFailure(
184             Statement base,
185             Description description,
186             List<Throwable> cleanUpErrors,
187             Throwable testFailure) {
188         runSafely(cleanUpErrors, () -> mOnTestFailureFlags.addAll(mDeviceConfig.getAll()));
189         runSafely(
190                 cleanUpErrors,
191                 () ->
192                         mOnTestFailureSystemProperties.addAll(
193                                 mSystemProperties.getAll(mSystemPropertiesMatcher)));
194     }
195 
196     @Override
postTest( Statement base, Description description, List<Throwable> cleanUpErrors)197     protected void postTest(
198             Statement base, Description description, List<Throwable> cleanUpErrors) {
199         String testName = TestHelper.getTestName(description);
200         runSafely(cleanUpErrors, () -> resetFlags(testName));
201         runSafely(cleanUpErrors, () -> resetSystemProperties(testName));
202         runSafely(cleanUpErrors, () -> setSyncDisabledMode(mPreviousSyncDisabledModeForTest));
203         mIsRunning = false;
204     }
205 
setSyncDisabledMode(SyncDisabledModeForTest mode)206     private void setSyncDisabledMode(SyncDisabledModeForTest mode) {
207         runOrCache(
208                 "setSyncDisabledMode(" + mode + ")", () -> mDeviceConfig.setSyncDisabledMode(mode));
209     }
210 
211     @Override
decorateTestFailureMessage(StringBuilder dump, List<Throwable> cleanUpErrors)212     protected String decorateTestFailureMessage(StringBuilder dump, List<Throwable> cleanUpErrors) {
213         if (mFlagsClearedByTest) {
214             dump.append("NOTE: test explicitly cleared all flags.\n");
215         }
216 
217         logAllAndDumpDiff(
218                 "flags", dump, mChangedFlags, mPreTestFlags, mOnTestFailureSystemProperties);
219         logAllAndDumpDiff(
220                 "system properties",
221                 dump,
222                 mChangedSystemProperties,
223                 mPreTestSystemProperties,
224                 mOnTestFailureSystemProperties);
225         return "flags / system properties state";
226     }
227 
logAllAndDumpDiff( String what, StringBuilder dump, Set<String> changedNames, List<NameValuePair> preTest, List<NameValuePair> postTest)228     private void logAllAndDumpDiff(
229             String what,
230             StringBuilder dump,
231             Set<String> changedNames,
232             List<NameValuePair> preTest,
233             List<NameValuePair> postTest) {
234         // Log all values
235         log(preTest, "%s before the test", what);
236         log(postTest, "%s after the test", what);
237 
238         // Dump only what was change
239         appendChanges(dump, what, changedNames, preTest, postTest);
240     }
241 
appendChanges( StringBuilder dump, String what, Set<String> changedNames, List<NameValuePair> preTest, List<NameValuePair> postTest)242     private void appendChanges(
243             StringBuilder dump,
244             String what,
245             Set<String> changedNames,
246             List<NameValuePair> preTest,
247             List<NameValuePair> postTest) {
248         if (changedNames.isEmpty()) {
249             dump.append("Tested didn't change any ").append(what).append('\n');
250             return;
251         }
252         dump.append("Test changed ")
253                 .append(changedNames.size())
254                 .append(' ')
255                 .append(what)
256                 .append(" (see log for all changes): \n");
257 
258         for (String name : changedNames) {
259             String before = getValue("before", preTest, name);
260             String after = getValue("after", postTest, name);
261             dump.append('\t')
262                     .append(name)
263                     .append(": ")
264                     .append(before)
265                     .append(", ")
266                     .append(after)
267                     .append('\n');
268         }
269     }
270 
getValue(String when, List<NameValuePair> list, String name)271     private String getValue(String when, List<NameValuePair> list, String name) {
272         for (NameValuePair candidate : list) {
273             if (candidate.name.equals(name)) {
274                 return when + "=" + candidate.value;
275             }
276         }
277         return "(not set " + when + ")";
278     }
279 
280     /**
281      * Dumps all flags using the {@value #TAG} tag.
282      *
283      * <p>Typically use for temporary debugging purposes like {@code dumpFlags("getFoo(%s)", bar)}.
284      */
285     @FormatMethod
dumpFlags(@ormatString String reasonFmt, @Nullable Object... reasonArgs)286     public final void dumpFlags(@FormatString String reasonFmt, @Nullable Object... reasonArgs) {
287         log(mDeviceConfig.getAll(), "flags (Reason: %s)", String.format(reasonFmt, reasonArgs));
288     }
289 
290     /**
291      * Dumps all system properties using the {@value #TAG} tag.
292      *
293      * <p>Typically use for temporary debugging purposes like {@code
294      * dumpSystemProperties("getFoo(%s)", bar)}.
295      */
296     @FormatMethod
dumpSystemProperties( @ormatString String reasonFmt, @Nullable Object... reasonArgs)297     public final void dumpSystemProperties(
298             @FormatString String reasonFmt, @Nullable Object... reasonArgs) {
299         log(
300                 mSystemProperties.getAll(mSystemPropertiesMatcher),
301                 "system properties (Reason: %s)",
302                 String.format(reasonFmt, reasonArgs));
303     }
304 
305     @FormatMethod
log( List<NameValuePair> values, @FormatString String whatFmt, @Nullable Object... whatArgs)306     private void log(
307             List<NameValuePair> values,
308             @FormatString String whatFmt,
309             @Nullable Object... whatArgs) {
310         String what = String.format(whatFmt, whatArgs);
311         if (values.isEmpty()) {
312             mLog.e("%s: empty", what);
313             return;
314         }
315         mLog.i("Logging name/value of %d %s:", values.size(), what);
316         values.forEach(value -> mLog.i("\t%s", value));
317     }
318 
319     /** Clears all flags from the namespace */
clearFlags()320     public final T clearFlags() {
321         return runOrCache(
322                 "clearFlags()",
323                 () -> {
324                     mLog.i("Clearing all flags. mIsRunning=%b", mIsRunning);
325                     mDeviceConfig.clearFlags();
326                     // TODO(b/294423183): ideally we should save the flags and restore - possibly
327                     // using DeviceConfig properties - but for now let's just clear it.
328                     mFlagsClearedByTest = true;
329                 });
330     }
331 
332     /** Sets the flag with the given value. */
setFlag(String name, boolean value)333     public final T setFlag(String name, boolean value) {
334         return setOrCacheFlag(name, Boolean.toString(value));
335     }
336 
337     /** Sets the flag with the given value. */
setFlag(String name, int value)338     public final T setFlag(String name, int value) {
339         return setOrCacheFlag(name, Integer.toString(value));
340     }
341 
342     /** Sets the flag with the given value. */
setFlag(String name, long value)343     public final T setFlag(String name, long value) {
344         return setOrCacheFlag(name, Long.toString(value));
345     }
346 
347     /** Sets the flag with the given value. */
setFlag(String name, float value)348     public final T setFlag(String name, float value) {
349         return setOrCacheFlag(name, Float.toString(value));
350     }
351 
352     /** Sets the flag with the given value. */
setFlag(String name, double value)353     public final T setFlag(String name, double value) {
354         return setOrCacheFlag(name, Double.toString(value));
355     }
356 
357     /** Sets the flag with the given value. */
setFlag(String name, String value)358     public final T setFlag(String name, String value) {
359         return setOrCacheFlag(name, value);
360     }
361 
362     // TODO(b/303901926): add unit test
363     /**
364      * Sets the string array flag with the given value , using the {@code separator} to flatten it.
365      *
366      * <p><b>Note:</b> in most cases, it's clearer to use the {@link SetLogcatTag} annotation
367      * instead.
368      */
setFlag(String name, String[] value, String separator)369     public final T setFlag(String name, String[] value, String separator) {
370         if (value == null || value.length == 0) {
371             throw new IllegalArgumentException("no values (name=" + name + ")");
372         }
373         if (value.length == 1) {
374             return setFlag(name, value[0]);
375         }
376 
377         // TODO(b/303901926): use some existing helper / utility to flatten it - or a stream like
378         // list.stream().map(Object::toString).collect(Collectors.joining(delimiter) - once it's
379         // unit tested
380         StringBuilder flattenedValue = new StringBuilder().append(value[0]);
381         for (int i = 1; i < value.length; i++) {
382             String nextValue = value[i];
383             if (i < value.length) {
384                 flattenedValue.append(separator);
385             }
386             flattenedValue.append(nextValue);
387         }
388         return setFlag(new NameValuePair(name, flattenedValue.toString(), separator));
389     }
390 
391     /**
392      * Sets a {@code logcat} tag.
393      *
394      * <p><b>Note: </b> it's clearer to use the {@link SetLogcatTag} annotation instead.
395      */
setLogcatTag(String tag, LogLevel level)396     public final T setLogcatTag(String tag, LogLevel level) {
397         setOrCacheLogtagSystemProperty(tag, level.name());
398         return getThis();
399     }
400 
401     // TODO(b/331781012): add all of them
402     // TODO(b/331781012): create @SetInfraLogcatTags as well
403     /** Sets the {@code logcat} tags for the (shared) infra classes. */
setInfraLogcatTags()404     public final T setInfraLogcatTags() {
405         // TODO(b/331781012): create a String[] constants somewhere and iterate over it
406         setLogcatTag(LOG_TAG, LogLevel.VERBOSE);
407         return getThis();
408     }
409 
410     /** Gets the value of the given flag. */
411     @Nullable
getFlag(String flag)412     public final String getFlag(String flag) {
413         return mDeviceConfig.get(flag);
414     }
415 
416     // TODO(295007931): abstract SDK-related methods in a new SdkLevelHelper and reuse them on
417     // SdkLevelSupportRule
418     /** Gets the device's SDK level. */
getDeviceSdk()419     protected abstract int getDeviceSdk();
420 
isAtLeastR()421     protected boolean isAtLeastR() {
422         return getDeviceSdk() >= 30;
423     }
424 
isAtLeastS()425     protected boolean isAtLeastS() {
426         return getDeviceSdk() >= 31;
427     }
428 
isAtLeastT()429     protected boolean isAtLeastT() {
430         return getDeviceSdk() > 32;
431     }
432 
433     // Helper to get a reference to this object, taking care of the generic casting.
434     @SuppressWarnings("unchecked")
getThis()435     protected final T getThis() {
436         return (T) this;
437     }
438 
439     // Set the annotated flags with the specified value for a particular test method.
440     // NOTE: when adding an annotation here, you also need to add it on isFlagAnnotationPresent()
setAnnotatedFlags(Description description)441     private void setAnnotatedFlags(Description description) {
442         List<Annotation> annotations = getAllFlagAnnotations(description);
443 
444         // Apply the annotations in the reverse order. First apply from the super classes, test
445         // class and then test method. If same annotated flag is present in class and test
446         // method, test method takes higher priority.
447         // NOTE: add annotations sorted by "most likely usage" and "groups"
448         for (int i = annotations.size() - 1; i >= 0; i--) {
449             Annotation annotation = annotations.get(i);
450 
451             // Boolean
452             if (annotation instanceof SetFlagEnabled) {
453                 setAnnotatedFlag((SetFlagEnabled) annotation);
454             } else if (annotation instanceof SetFlagsEnabled) {
455                 setAnnotatedFlag((SetFlagsEnabled) annotation);
456             } else if (annotation instanceof SetFlagDisabled) {
457                 setAnnotatedFlag((SetFlagDisabled) annotation);
458             } else if (annotation instanceof SetFlagsDisabled) {
459                 setAnnotatedFlag((SetFlagsDisabled) annotation);
460             } else if (annotation instanceof SetFlagTrue) {
461                 setAnnotatedFlag((SetFlagTrue) annotation);
462             } else if (annotation instanceof SetFlagsTrue) {
463                 setAnnotatedFlag((SetFlagsTrue) annotation);
464             } else if (annotation instanceof SetFlagFalse) {
465                 setAnnotatedFlag((SetFlagFalse) annotation);
466             } else if (annotation instanceof SetFlagsFalse) {
467                 setAnnotatedFlag((SetFlagsFalse) annotation);
468 
469                 // Numbers
470             } else if (annotation instanceof SetIntegerFlag) {
471                 setAnnotatedFlag((SetIntegerFlag) annotation);
472             } else if (annotation instanceof SetIntegerFlags) {
473                 setAnnotatedFlag((SetIntegerFlags) annotation);
474             } else if (annotation instanceof SetLongFlag) {
475                 setAnnotatedFlag((SetLongFlag) annotation);
476             } else if (annotation instanceof SetLongFlags) {
477                 setAnnotatedFlag((SetLongFlags) annotation);
478             } else if (annotation instanceof SetFloatFlag) {
479                 setAnnotatedFlag((SetFloatFlag) annotation);
480             } else if (annotation instanceof SetFloatFlags) {
481                 setAnnotatedFlag((SetFloatFlags) annotation);
482             } else if (annotation instanceof SetDoubleFlag) {
483                 setAnnotatedFlag((SetDoubleFlag) annotation);
484             } else if (annotation instanceof SetDoubleFlags) {
485                 setAnnotatedFlag((SetDoubleFlags) annotation);
486 
487                 // String
488             } else if (annotation instanceof SetStringFlag) {
489                 setAnnotatedFlag((SetStringFlag) annotation);
490             } else if (annotation instanceof SetStringFlags) {
491                 setAnnotatedFlag((SetStringFlags) annotation);
492             } else if (annotation instanceof SetStringArrayFlag) {
493                 setAnnotatedFlag((SetStringArrayFlag) annotation);
494             } else if (annotation instanceof SetStringArrayFlags) {
495                 setAnnotatedFlag((SetStringArrayFlags) annotation);
496 
497                 // Debug flags
498             } else if (annotation instanceof EnableDebugFlag) {
499                 setAnnotatedFlag((EnableDebugFlag) annotation);
500             } else if (annotation instanceof EnableDebugFlags) {
501                 setAnnotatedFlag((EnableDebugFlags) annotation);
502             } else if (annotation instanceof DisableDebugFlag) {
503                 setAnnotatedFlag((DisableDebugFlag) annotation);
504             } else if (annotation instanceof DisableDebugFlags) {
505                 setAnnotatedFlag((DisableDebugFlags) annotation);
506             } else if (annotation instanceof SetLongDebugFlag) {
507                 setAnnotatedFlag((SetLongDebugFlag) annotation);
508             } else if (annotation instanceof SetLongDebugFlags) {
509                 setAnnotatedFlag((SetLongDebugFlags) annotation);
510 
511                 // Logcat flags
512             } else if (annotation instanceof SetLogcatTag) {
513                 setAnnotatedFlag((SetLogcatTag) annotation);
514             } else if (annotation instanceof SetLogcatTags) {
515                 setAnnotatedFlag((SetLogcatTags) annotation);
516             } else {
517                 processAnnotation(description, annotation);
518             }
519         }
520     }
521 
setOrCacheFlag(String name, String value)522     private T setOrCacheFlag(String name, String value) {
523         return setOrCacheFlag(name, value, /* separator= */ null);
524     }
525 
526     // TODO(b/294423183): need to add unit test for setters that call this
setOrCacheFlag(String name, String value, @Nullable String separator)527     protected final T setOrCacheFlag(String name, String value, @Nullable String separator) {
528         NameValuePair flag = new NameValuePair(name, value, separator);
529         if (!mIsRunning) {
530             if (isFlagManagedByRunner(name)) {
531                 return getThis();
532             }
533             cacheCommand(new SetFlagCommand(flag));
534             return getThis();
535         }
536         return setFlag(flag);
537     }
538 
539     // TODO(b/295321663): need to provide a more elegant way to integrate it with the custom runners
isFlagManagedByRunner(String flag)540     protected boolean isFlagManagedByRunner(String flag) {
541         return false;
542     }
543 
setFlag(NameValuePair flag)544     private T setFlag(NameValuePair flag) {
545         mLog.d("Setting flag: %s", flag);
546         if (flag.separator == null) {
547             mDeviceConfig.set(flag.name, flag.value);
548         } else {
549             mDeviceConfig.setWithSeparator(flag.name, flag.value, flag.separator);
550         }
551         mChangedFlags.add(flag.name);
552         return getThis();
553     }
554 
resetFlags(String testName)555     private void resetFlags(String testName) {
556         mLog.d("Resetting flags after %s", testName);
557         mDeviceConfig.reset();
558     }
559 
560     /** Sets the value of the given {@link com.android.adservices.service.DebugFlag}. */
setDebugFlag(String name, boolean value)561     public final T setDebugFlag(String name, boolean value) {
562         return setDebugFlag(name, Boolean.toString(value));
563     }
564 
setOrCacheLogtagSystemProperty(String name, String value)565     private T setOrCacheLogtagSystemProperty(String name, String value) {
566         return setOrCacheSystemProperty(SYSTEM_PROPERTY_FOR_LOGCAT_TAGS_PREFIX + name, value);
567     }
568 
setDebugFlag(String name, String value)569     private T setDebugFlag(String name, String value) {
570         return setOrCacheSystemProperty(mDebugFlagPrefix + name, value);
571     }
572 
setOrCacheSystemProperty(String name, String value)573     private T setOrCacheSystemProperty(String name, String value) {
574         NameValuePair systemProperty = new NameValuePair(name, value);
575         if (!mIsRunning) {
576             cacheCommand(new SetSystemPropertyCommand(systemProperty));
577             return getThis();
578         }
579         return setSystemProperty(systemProperty);
580     }
581 
setSystemProperty(NameValuePair systemProperty)582     private T setSystemProperty(NameValuePair systemProperty) {
583         mLog.d("Setting system property: %s", systemProperty);
584         mSystemProperties.set(systemProperty.name, systemProperty.value);
585         mChangedSystemProperties.add(systemProperty.name);
586         return getThis();
587     }
588 
resetSystemProperties(String testName)589     private void resetSystemProperties(String testName) {
590         mLog.d("Resetting SystemProperties after %s", testName);
591         mSystemProperties.reset();
592     }
593 
runOrCache(String description, Runnable r)594     protected T runOrCache(String description, Runnable r) {
595         RunnableCommand command = new RunnableCommand(description, r);
596         if (!mIsRunning) {
597             cacheCommand(command);
598             return getThis();
599         }
600         command.execute();
601         return getThis();
602     }
603 
cacheCommand(Command command)604     private void cacheCommand(Command command) {
605         if (mIsRunning) {
606             throw new IllegalStateException(
607                     "Cannot cache " + command + " as test is already running");
608         }
609         mLog.v("Caching %s as test is not running yet", command);
610         mInitialCommands.add(command);
611     }
612 
runCommand(String description, Runnable runnable)613     private void runCommand(String description, Runnable runnable) {
614         mLog.v("Running runnable for %s", description);
615         runnable.run();
616     }
617 
618     // TODO(b/294423183): make private once not used by subclass for legacy methods
runInitialCommands(String testName)619     protected final void runInitialCommands(String testName) {
620         if (mInitialCommands.isEmpty()) {
621             mLog.d("Not executing any command before %s", testName);
622         } else {
623             int size = mInitialCommands.size();
624             mLog.d("Executing %d commands before %s", size, testName);
625             for (int i = 0; i < mInitialCommands.size(); i++) {
626                 Command command = mInitialCommands.get(i);
627                 mLog.v("\t%d: %s", i, command);
628                 command.execute();
629             }
630         }
631     }
632 
633     // TODO(b/294423183): improve logic used here and on setAnnotatedFlags()
634     // NOTE: when adding an annotation here, you also need to add it on setAnnotatedFlags()
isFlagAnnotationPresent(Annotation annotation)635     private boolean isFlagAnnotationPresent(Annotation annotation) {
636         // NOTE: add annotations sorted by "most likely usage" and "groups"
637         boolean processedHere =
638                 // Boolean
639                 (annotation instanceof SetFlagEnabled)
640                         || (annotation instanceof SetFlagsEnabled)
641                         || (annotation instanceof SetFlagDisabled)
642                         || (annotation instanceof SetFlagsDisabled)
643                         || (annotation instanceof SetFlagTrue)
644                         || (annotation instanceof SetFlagsTrue)
645                         || (annotation instanceof SetFlagFalse)
646                         || (annotation instanceof SetFlagsFalse)
647                         // Numbers
648                         || (annotation instanceof SetIntegerFlag)
649                         || (annotation instanceof SetIntegerFlags)
650                         || (annotation instanceof SetLongFlag)
651                         || (annotation instanceof SetLongFlags)
652                         || (annotation instanceof SetFloatFlag)
653                         || (annotation instanceof SetFloatFlags)
654                         || (annotation instanceof SetDoubleFlag)
655                         || (annotation instanceof SetDoubleFlags)
656                         // Strings
657                         || (annotation instanceof SetStringFlag)
658                         || (annotation instanceof SetStringFlags)
659                         || (annotation instanceof SetStringArrayFlag)
660                         || (annotation instanceof SetStringArrayFlags)
661                         // Debug flags
662                         || (annotation instanceof DisableDebugFlag)
663                         || (annotation instanceof DisableDebugFlags)
664                         || (annotation instanceof EnableDebugFlag)
665                         || (annotation instanceof EnableDebugFlags)
666                         || (annotation instanceof SetLongDebugFlag)
667                         || (annotation instanceof SetLongDebugFlags)
668                         // Logcat flags
669                         || (annotation instanceof SetLogcatTag)
670                         || (annotation instanceof SetLogcatTags);
671         return processedHere || isAnnotationSupported(annotation);
672     }
673 
674     /**
675      * By default returns {@code false}, but subclasses can override to support custom annotations.
676      *
677      * <p>Note: when overridden, {@link #processAnnotation(Description, Annotation)} should be
678      * overridden as well.
679      */
isAnnotationSupported(Annotation annotation)680     protected boolean isAnnotationSupported(Annotation annotation) {
681         return false;
682     }
683 
684     /**
685      * Called to process custom annotations present in the test (when {@link
686      * #isAnnotationSupported(Annotation)} returns {@code true} for that annotation type).
687      */
processAnnotation(Description description, Annotation annotation)688     protected void processAnnotation(Description description, Annotation annotation) {
689         throw new IllegalStateException(
690                 "Rule subclass ("
691                         + this.getClass().getName()
692                         + ") supports annotation "
693                         + annotation.annotationType().getName()
694                         + ", but doesn't override processAnnotation(), which was called with "
695                         + annotation);
696     }
697 
getAllFlagAnnotations(Description description)698     private List<Annotation> getAllFlagAnnotations(Description description) {
699         // TODO(b/318893752): Move this to a helper function to scan test method, class and
700         //  superclasses for annotations.
701         List<Annotation> result = new ArrayList<>();
702         for (Annotation testMethodAnnotation : description.getAnnotations()) {
703             if (isFlagAnnotationPresent(testMethodAnnotation)) {
704                 result.add(testMethodAnnotation);
705             } else {
706                 mLog.v("Ignoring annotation %s", testMethodAnnotation);
707             }
708         }
709 
710         // Get all the flag based annotations from test class and super classes
711         Class<?> clazz = description.getTestClass();
712         do {
713             Annotation[] classAnnotations = clazz.getAnnotations();
714             if (classAnnotations != null) {
715                 for (Annotation annotation : classAnnotations) {
716                     if (isFlagAnnotationPresent(annotation)) {
717                         result.add(annotation);
718                     }
719                 }
720             }
721             clazz = clazz.getSuperclass();
722         } while (clazz != null);
723 
724         return result;
725     }
726 
727     // Single SetFlagEnabled annotations present
setAnnotatedFlag(SetFlagEnabled annotation)728     private void setAnnotatedFlag(SetFlagEnabled annotation) {
729         setFlag(annotation.value(), true);
730     }
731 
732     // Multiple SetFlagEnabled annotations present
setAnnotatedFlag(SetFlagsEnabled repeatedAnnotation)733     private void setAnnotatedFlag(SetFlagsEnabled repeatedAnnotation) {
734         for (SetFlagEnabled annotation : repeatedAnnotation.value()) {
735             setAnnotatedFlag(annotation);
736         }
737     }
738 
739     // Single SetFlagDisabled annotations present
setAnnotatedFlag(SetFlagDisabled annotation)740     private void setAnnotatedFlag(SetFlagDisabled annotation) {
741         setFlag(annotation.value(), false);
742     }
743 
744     // Multiple SetFlagDisabled annotations present
setAnnotatedFlag(SetFlagsDisabled repeatedAnnotation)745     private void setAnnotatedFlag(SetFlagsDisabled repeatedAnnotation) {
746         for (SetFlagDisabled annotation : repeatedAnnotation.value()) {
747             setAnnotatedFlag(annotation);
748         }
749     }
750 
751     // Single SetFlagTrue annotations present
setAnnotatedFlag(SetFlagTrue annotation)752     private void setAnnotatedFlag(SetFlagTrue annotation) {
753         setFlag(annotation.value(), true);
754     }
755 
756     // Multiple SetFlagTrue annotations present
setAnnotatedFlag(SetFlagsTrue repeatedAnnotation)757     private void setAnnotatedFlag(SetFlagsTrue repeatedAnnotation) {
758         for (SetFlagTrue annotation : repeatedAnnotation.value()) {
759             setAnnotatedFlag(annotation);
760         }
761     }
762 
763     // Single SetFlagFalse annotations present
setAnnotatedFlag(SetFlagFalse annotation)764     private void setAnnotatedFlag(SetFlagFalse annotation) {
765         setFlag(annotation.value(), false);
766     }
767 
768     // Multiple SetFlagFalse annotations present
setAnnotatedFlag(SetFlagsFalse repeatedAnnotation)769     private void setAnnotatedFlag(SetFlagsFalse repeatedAnnotation) {
770         for (SetFlagFalse annotation : repeatedAnnotation.value()) {
771             setAnnotatedFlag(annotation);
772         }
773     }
774 
775     // Single SetIntegerFlag annotations present
setAnnotatedFlag(SetIntegerFlag annotation)776     private void setAnnotatedFlag(SetIntegerFlag annotation) {
777         setFlag(annotation.name(), annotation.value());
778     }
779 
780     // Multiple SetIntegerFlag annotations present
setAnnotatedFlag(SetIntegerFlags repeatedAnnotation)781     private void setAnnotatedFlag(SetIntegerFlags repeatedAnnotation) {
782         for (SetIntegerFlag annotation : repeatedAnnotation.value()) {
783             setAnnotatedFlag(annotation);
784         }
785     }
786 
787     // Single SetLongFlag annotations present
setAnnotatedFlag(SetLongFlag annotation)788     private void setAnnotatedFlag(SetLongFlag annotation) {
789         setFlag(annotation.name(), annotation.value());
790     }
791 
792     // Multiple SetLongFlag annotations present
setAnnotatedFlag(SetLongFlags repeatedAnnotation)793     private void setAnnotatedFlag(SetLongFlags repeatedAnnotation) {
794         for (SetLongFlag annotation : repeatedAnnotation.value()) {
795             setAnnotatedFlag(annotation);
796         }
797     }
798 
799     // Single SetLongFlag annotations present
setAnnotatedFlag(SetFloatFlag annotation)800     private void setAnnotatedFlag(SetFloatFlag annotation) {
801         setFlag(annotation.name(), annotation.value());
802     }
803 
804     // Multiple SetLongFlag annotations present
setAnnotatedFlag(SetFloatFlags repeatedAnnotation)805     private void setAnnotatedFlag(SetFloatFlags repeatedAnnotation) {
806         for (SetFloatFlag annotation : repeatedAnnotation.value()) {
807             setAnnotatedFlag(annotation);
808         }
809     }
810 
811     // Single SetDoubleFlag annotations present
setAnnotatedFlag(SetDoubleFlag annotation)812     private void setAnnotatedFlag(SetDoubleFlag annotation) {
813         setFlag(annotation.name(), annotation.value());
814     }
815 
816     // Multiple SetDoubleFlag annotations present
setAnnotatedFlag(SetDoubleFlags repeatedAnnotation)817     private void setAnnotatedFlag(SetDoubleFlags repeatedAnnotation) {
818         for (SetDoubleFlag annotation : repeatedAnnotation.value()) {
819             setAnnotatedFlag(annotation);
820         }
821     }
822 
823     // Single SetStringFlag annotations present
setAnnotatedFlag(SetStringFlag annotation)824     private void setAnnotatedFlag(SetStringFlag annotation) {
825         setFlag(annotation.name(), annotation.value());
826     }
827 
828     // Multiple SetStringFlag annotations present
setAnnotatedFlag(SetStringFlags repeatedAnnotation)829     private void setAnnotatedFlag(SetStringFlags repeatedAnnotation) {
830         for (SetStringFlag annotation : repeatedAnnotation.value()) {
831             setAnnotatedFlag(annotation);
832         }
833     }
834 
835     // Single SetStringArrayFlag annotations present
setAnnotatedFlag(SetStringArrayFlag annotation)836     private void setAnnotatedFlag(SetStringArrayFlag annotation) {
837         setFlag(annotation.name(), annotation.value(), annotation.separator());
838     }
839 
840     // Multiple SetStringArrayFlag annotations present
setAnnotatedFlag(SetStringArrayFlags repeatedAnnotation)841     private void setAnnotatedFlag(SetStringArrayFlags repeatedAnnotation) {
842         for (SetStringArrayFlag annotation : repeatedAnnotation.value()) {
843             setAnnotatedFlag(annotation);
844         }
845     }
846 
847     // Single EnableDebugFlag annotations present
setAnnotatedFlag(EnableDebugFlag annotation)848     private void setAnnotatedFlag(EnableDebugFlag annotation) {
849         setDebugFlag(annotation.value(), true);
850     }
851 
852     // Multiple EnableDebugFlag annotations present
setAnnotatedFlag(EnableDebugFlags repeatedAnnotation)853     private void setAnnotatedFlag(EnableDebugFlags repeatedAnnotation) {
854         for (EnableDebugFlag annotation : repeatedAnnotation.value()) {
855             setAnnotatedFlag(annotation);
856         }
857     }
858 
859     // Single DisableDebugFlag annotations present
setAnnotatedFlag(DisableDebugFlag annotation)860     private void setAnnotatedFlag(DisableDebugFlag annotation) {
861         setDebugFlag(annotation.value(), false);
862     }
863 
864     // Multiple DisableDebugFlag annotations present
setAnnotatedFlag(DisableDebugFlags repeatedAnnotation)865     private void setAnnotatedFlag(DisableDebugFlags repeatedAnnotation) {
866         for (DisableDebugFlag annotation : repeatedAnnotation.value()) {
867             setAnnotatedFlag(annotation);
868         }
869     }
870 
871     // Single SetLongDebugFlag annotations present
setAnnotatedFlag(SetLongDebugFlag annotation)872     private void setAnnotatedFlag(SetLongDebugFlag annotation) {
873         setDebugFlag(annotation.name(), Long.toString(annotation.value()));
874     }
875 
876     // Multiple SetLongDebugFlag annotations present
setAnnotatedFlag(SetLongDebugFlags repeatedAnnotation)877     private void setAnnotatedFlag(SetLongDebugFlags repeatedAnnotation) {
878         for (SetLongDebugFlag annotation : repeatedAnnotation.value()) {
879             setAnnotatedFlag(annotation);
880         }
881     }
882 
883     // Single SetLogcatTag annotations present
setAnnotatedFlag(SetLogcatTag annotation)884     private void setAnnotatedFlag(SetLogcatTag annotation) {
885         setLogcatTag(annotation.tag(), annotation.level());
886     }
887 
888     // Multiple SetLogcatTag annotations present
setAnnotatedFlag(SetLogcatTags repeatedAnnotation)889     private void setAnnotatedFlag(SetLogcatTags repeatedAnnotation) {
890         for (SetLogcatTag annotation : repeatedAnnotation.value()) {
891             setAnnotatedFlag(annotation);
892         }
893     }
894 
895     @SuppressWarnings("ClassCanBeStatic") // Subclasses reference enclosing class
896     private abstract class Command {
897         protected final String mDescription;
898 
Command(String description)899         Command(String description) {
900             mDescription = description;
901         }
902 
execute()903         abstract void execute();
904 
905         @Override
toString()906         public final String toString() {
907             return mDescription;
908         }
909     }
910 
911     private final class RunnableCommand extends Command {
912         private final Runnable mRunnable;
913 
RunnableCommand(String description, Runnable runnable)914         RunnableCommand(String description, Runnable runnable) {
915             super(description);
916             mRunnable = runnable;
917         }
918 
919         @Override
execute()920         void execute() {
921             runCommand(mDescription, mRunnable);
922         }
923     }
924 
925     private abstract class SetFlagOrSystemPropertyCommand extends Command {
926         protected final NameValuePair mFlagOrSystemProperty;
927 
SetFlagOrSystemPropertyCommand(String description, NameValuePair flagOrSystemProperty)928         SetFlagOrSystemPropertyCommand(String description, NameValuePair flagOrSystemProperty) {
929             super(description + "(" + flagOrSystemProperty + ")");
930             mFlagOrSystemProperty = flagOrSystemProperty;
931         }
932     }
933 
934     private final class SetFlagCommand extends SetFlagOrSystemPropertyCommand {
SetFlagCommand(NameValuePair flag)935         SetFlagCommand(NameValuePair flag) {
936             super("SetFlag", flag);
937         }
938 
939         @Override
execute()940         void execute() {
941             setFlag(mFlagOrSystemProperty);
942         }
943     }
944 
945     private final class SetSystemPropertyCommand extends SetFlagOrSystemPropertyCommand {
SetSystemPropertyCommand(NameValuePair flag)946         SetSystemPropertyCommand(NameValuePair flag) {
947             super("SetSystemProperty", flag);
948         }
949 
950         @Override
execute()951         void execute() {
952             setSystemProperty(mFlagOrSystemProperty);
953         }
954     }
955 }
956