diff --git a/build.gradle b/build.gradle index d6f355c021..af940a82bc 100644 --- a/build.gradle +++ b/build.gradle @@ -149,6 +149,7 @@ dependencies { testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1' testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1' + testImplementation 'androidx.test:core:1.1.1-alpha02' androidTestImplementation 'com.android.support:multidex:1.0.3' androidTestImplementation 'com.android.support:multidex-instrumentation:1.0.3' androidTestImplementation 'com.google.dexmaker:dexmaker:1.2' @@ -324,10 +325,13 @@ android { buildConfigField "boolean", "DEV_BUILD", "false" buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\"" buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"" + buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' ndk { abiFilters "armeabi", "armeabi-v7a", "x86" } + + resConfigs autoResConfig() } compileOptions { @@ -510,3 +514,16 @@ def getLastCommitTimestamp() { return os.toString() + "000" } } + +/** + * Discovers supported languages listed as under the res/values- directory. + */ +static def autoResConfig() { + def files = new ArrayList() + def root = new File('res') + root.eachFile { f -> files.add(f.name) } + ['en'] + files.collect { f -> f =~ /^values-([a-z]{2}(-r[A-Z]{2})?)$/ } + .findAll { matcher -> matcher.find() } + .collect { matcher -> matcher.group(1) } + .sort() +} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/l10n/LanguageResourcesTest.java b/test/unitTest/java/org/thoughtcrime/securesms/l10n/LanguageResourcesTest.java new file mode 100644 index 0000000000..fccc9b1c02 --- /dev/null +++ b/test/unitTest/java/org/thoughtcrime/securesms/l10n/LanguageResourcesTest.java @@ -0,0 +1,84 @@ +package org.thoughtcrime.securesms.l10n; + +import android.app.Application; +import android.content.res.Resources; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.thoughtcrime.securesms.BuildConfig; +import org.thoughtcrime.securesms.R; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import androidx.test.core.app.ApplicationProvider; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE, application = Application.class) +public final class LanguageResourcesTest { + + @Test + public void language_entries_match_language_values_in_length() { + Resources resources = ApplicationProvider.getApplicationContext().getResources(); + String[] values = resources.getStringArray(R.array.language_values); + String[] entries = resources.getStringArray(R.array.language_entries); + assertEquals(values.length, entries.length); + } + + @Test + public void language_options_matches_available_resources() { + Set languageEntries = languageEntries(); + Set foundResources = buildConfigResources(); + if (!languageEntries.equals(foundResources)) { + assertSubset(foundResources, languageEntries, "Missing language_entries for resources"); + assertSubset(languageEntries, foundResources, "Missing resources for language_entries"); + fail("Unexpected"); + } + } + + private static Set languageEntries() { + Resources resources = ApplicationProvider.getApplicationContext().getResources(); + String[] values = resources.getStringArray(R.array.language_values); + + List tail = Arrays.asList(values).subList(1, values.length); + Set set = new HashSet<>(tail); + + assertEquals("First is not the default", "zz", values[0]); + assertEquals("List contains duplicates", tail.size(), set.size()); + return set; + } + + private static Set buildConfigResources() { + Set set = new HashSet<>(); + Collections.addAll(set, BuildConfig.LANGUAGES); + assertEquals("List contains duplicates", BuildConfig.LANGUAGES.length, set.size()); + return set; + } + + /** + * Fails if "a" is not a subset of "b", lists the additional values found in "a" + */ + private static void assertSubset(Set a, Set b, String message) { + Set delta = subtract(a, b); + if (!delta.isEmpty()) { + fail(message + ": " + String.join(", ", delta)); + } + } + + /** + * Set a - Set b + */ + private static Set subtract(Set a, Set b) { + Set set = new HashSet<>(a); + set.removeAll(b); + return set; + } +}