1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.shared_preferences; 6 7 import com.google.common.collect.Sets; 8 9 import org.chromium.base.ResettersForTesting; 10 import org.chromium.build.BuildConfig; 11 import org.chromium.build.annotations.CheckDiscard; 12 13 import java.util.ArrayList; 14 import java.util.HashSet; 15 import java.util.List; 16 import java.util.Set; 17 18 /** 19 * Ensures that all {@link PreferenceKeyRegistry}s used are known. 20 * 21 * A complement to ChromePreferenceKeysTest, which ensures that preference keys across all known 22 * registries are unique. 23 * 24 * This checking is done in tests in which |initializeKnownRegistries()| is called, which happens 25 * during browser process initialization. 26 */ 27 @CheckDiscard("Preference key checking should only happen on build with asserts") 28 public class KnownPreferenceKeyRegistries { 29 private static Set<PreferenceKeyRegistry> sKnownRegistries; 30 private static Set<PreferenceKeyRegistry> sRegistriesUsedBeforeInitialization = new HashSet<>(); 31 onRegistryUsed(PreferenceKeyRegistry registry)32 public static void onRegistryUsed(PreferenceKeyRegistry registry) { 33 if (!BuildConfig.ENABLE_ASSERTS) { 34 return; 35 } 36 37 if (sKnownRegistries == null) { 38 // Before initialization, keep track of registries used. 39 sRegistriesUsedBeforeInitialization.add(registry); 40 } else { 41 // After initialization, check if registry is known. 42 if (!sKnownRegistries.contains(registry)) { 43 String message = 44 "An unknown registry was used, PreferenceKeyRegistries must be declared as " 45 + "known in AllPreferenceKeyRegistries: " 46 + String.join(",", registry.toDebugString()); 47 assert false : message; 48 } 49 } 50 } 51 initializeKnownRegistries(Set<PreferenceKeyRegistry> knownRegistries)52 public static void initializeKnownRegistries(Set<PreferenceKeyRegistry> knownRegistries) { 53 if (!BuildConfig.ENABLE_ASSERTS) { 54 return; 55 } 56 57 if (sKnownRegistries != null) { 58 // Double initialization; make sure new known registries are the same. 59 assert sKnownRegistries.equals(knownRegistries); 60 return; 61 } 62 63 // Check that each registry already used is known; assert otherwise. 64 Set<PreferenceKeyRegistry> unknownRegistries = 65 Sets.difference(sRegistriesUsedBeforeInitialization, knownRegistries); 66 if (!unknownRegistries.isEmpty()) { 67 List<String> unknownRegistryNames = new ArrayList<>(); 68 for (PreferenceKeyRegistry unknownRegistry : unknownRegistries) { 69 unknownRegistryNames.add(unknownRegistry.toDebugString()); 70 } 71 String message = 72 "Unknown registries were used, PreferenceKeyRegistries must be declared as " 73 + "known in AllPreferenceKeyRegistries: " 74 + String.join(",", unknownRegistryNames); 75 assert false : message; 76 } 77 78 sKnownRegistries = knownRegistries; 79 sRegistriesUsedBeforeInitialization = null; 80 } 81 clearForTesting()82 static void clearForTesting() { 83 Set<PreferenceKeyRegistry> previousKnownRegistries = sKnownRegistries; 84 Set<PreferenceKeyRegistry> registriesUsedBeforeInitialization = 85 sRegistriesUsedBeforeInitialization; 86 87 ResettersForTesting.register( 88 () -> { 89 sKnownRegistries = previousKnownRegistries; 90 sRegistriesUsedBeforeInitialization = registriesUsedBeforeInitialization; 91 }); 92 sKnownRegistries = null; 93 sRegistriesUsedBeforeInitialization = new HashSet<>(); 94 } 95 } 96