Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit adb59a01 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove duplicate code from InputMethodUtils"

parents 82e9122c 08a3ecea
Loading
Loading
Loading
Loading
+1 −71
Original line number Original line Diff line number Diff line
@@ -34,10 +34,6 @@ import android.os.LocaleList;
import android.os.RemoteException;
import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Pair;
import android.util.Printer;
import android.util.Printer;
import android.util.Slog;
import android.util.Slog;
@@ -782,58 +778,6 @@ public class InputMethodUtils {
        }
        }
    }
    }


    /**
     * Parses the setting stored input methods and subtypes string value.
     *
     * @param inputMethodsAndSubtypesString The input method subtypes value stored in settings.
     * @return Map from input method ID to set of input method subtypes IDs.
     */
    @VisibleForTesting
    public static ArrayMap<String, ArraySet<String>> parseInputMethodsAndSubtypesString(
            @Nullable final String inputMethodsAndSubtypesString) {

        final ArrayMap<String, ArraySet<String>> imeMap = new ArrayMap<>();
        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
            return imeMap;
        }

        final SimpleStringSplitter typeSplitter =
                new SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
        final SimpleStringSplitter subtypeSplitter =
                new SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);

        List<Pair<String, ArrayList<String>>> allImeSettings =
                InputMethodSettings.buildInputMethodsAndSubtypeList(inputMethodsAndSubtypesString,
                        typeSplitter,
                        subtypeSplitter);
        for (Pair<String, ArrayList<String>> ime : allImeSettings) {
            ArraySet<String> subtypes = new ArraySet<>();
            if (ime.second != null) {
                subtypes.addAll(ime.second);
            }
            imeMap.put(ime.first, subtypes);
        }
        return imeMap;
    }

    @NonNull
    public static String buildInputMethodsAndSubtypesString(
            @NonNull final ArrayMap<String, ArraySet<String>> map) {
        // we want to use the canonical InputMethodSettings implementation,
        // so we convert data structures first.
        List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
        for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
            final String imeName = entry.getKey();
            final ArraySet<String> subtypeSet = entry.getValue();
            final ArrayList<String> subtypes = new ArrayList<>(2);
            if (subtypeSet != null) {
                subtypes.addAll(subtypeSet);
            }
            imeMap.add(new Pair<>(imeName, subtypes));
        }
        return InputMethodSettings.buildInputMethodsSettingString(imeMap);
    }

    /**
    /**
     * Utility class for putting and getting settings for InputMethod
     * Utility class for putting and getting settings for InputMethod
     * TODO: Move all putters and getters of settings to this class.
     * TODO: Move all putters and getters of settings to this class.
@@ -871,21 +815,7 @@ public class InputMethodUtils {
            }
            }
        }
        }


        public static String buildInputMethodsSettingString(
        private static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
                List<Pair<String, ArrayList<String>>> allImeSettingsMap) {
            final StringBuilder b = new StringBuilder();
            boolean needsSeparator = false;
            for (Pair<String, ArrayList<String>> ime : allImeSettingsMap) {
                if (needsSeparator) {
                    b.append(INPUT_METHOD_SEPARATOR);
                }
                buildEnabledInputMethodsSettingString(b, ime);
                needsSeparator = true;
            }
            return b.toString();
        }

        public static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
                String enabledInputMethodsStr,
                String enabledInputMethodsStr,
                TextUtils.SimpleStringSplitter inputMethodSplitter,
                TextUtils.SimpleStringSplitter inputMethodSplitter,
                TextUtils.SimpleStringSplitter subtypeSplitter) {
                TextUtils.SimpleStringSplitter subtypeSplitter) {
+0 −244
Original line number Original line Diff line number Diff line
@@ -40,8 +40,6 @@ import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.runner.AndroidJUnit4;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -1191,248 +1189,6 @@ public class InputMethodUtilsTest {
        }
        }
    }
    }


    @Test
    public void testParseInputMethodsAndSubtypesString() {
        // Trivial cases.
        {
            assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString("").isEmpty());
            assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString(null).isEmpty());
        }

        // No subtype cases.
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0");
            assertEquals(1, r.size());
            assertTrue(r.containsKey("ime0"));
            assertTrue(r.get("ime0").isEmpty());
        }
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0:ime1");
            assertEquals(2, r.size());
            assertTrue(r.containsKey("ime0"));
            assertTrue(r.get("ime0").isEmpty());
            assertTrue(r.containsKey("ime1"));
            assertTrue(r.get("ime1").isEmpty());
        }

        // Input metho IDs and their subtypes.
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0");
            assertEquals(1, r.size());
            assertTrue(r.containsKey("ime0"));
            ArraySet<String> subtypes = r.get("ime0");
            assertEquals(1, subtypes.size());
            assertTrue(subtypes.contains("subtype0"));
        }
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype0");
            assertEquals(1, r.size());
            assertTrue(r.containsKey("ime0"));
            ArraySet<String> subtypes = r.get("ime0");
            assertEquals(1, subtypes.size());
            assertTrue(subtypes.contains("subtype0"));
        }
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype1");
            assertEquals(1, r.size());
            assertTrue(r.containsKey("ime0"));
            ArraySet<String> subtypes = r.get("ime0");
            assertEquals(2, subtypes.size());
            assertTrue(subtypes.contains("subtype0"));
            assertTrue(subtypes.contains("subtype1"));
        }
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString(
                            "ime0;subtype0:ime1;subtype1");
            assertEquals(2, r.size());
            assertTrue(r.containsKey("ime0"));
            assertTrue(r.containsKey("ime1"));
            ArraySet<String> subtypes0 = r.get("ime0");
            assertEquals(1, subtypes0.size());
            assertTrue(subtypes0.contains("subtype0"));

            ArraySet<String> subtypes1 = r.get("ime1");
            assertEquals(1, subtypes1.size());
            assertTrue(subtypes1.contains("subtype1"));
        }
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString(
                            "ime0;subtype0;subtype1:ime1;subtype2");
            assertEquals(2, r.size());
            assertTrue(r.containsKey("ime0"));
            assertTrue(r.containsKey("ime1"));
            ArraySet<String> subtypes0 = r.get("ime0");
            assertEquals(2, subtypes0.size());
            assertTrue(subtypes0.contains("subtype0"));
            assertTrue(subtypes0.contains("subtype1"));

            ArraySet<String> subtypes1 = r.get("ime1");
            assertEquals(1, subtypes1.size());
            assertTrue(subtypes1.contains("subtype2"));
        }
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString(
                            "ime0;subtype0;subtype1:ime1;subtype1;subtype2");
            assertEquals(2, r.size());
            assertTrue(r.containsKey("ime0"));
            assertTrue(r.containsKey("ime1"));
            ArraySet<String> subtypes0 = r.get("ime0");
            assertEquals(2, subtypes0.size());
            assertTrue(subtypes0.contains("subtype0"));
            assertTrue(subtypes0.contains("subtype1"));

            ArraySet<String> subtypes1 = r.get("ime1");
            assertEquals(2, subtypes1.size());
            assertTrue(subtypes0.contains("subtype1"));
            assertTrue(subtypes1.contains("subtype2"));
        }
        {
            ArrayMap<String, ArraySet<String>> r =
                    InputMethodUtils.parseInputMethodsAndSubtypesString(
                            "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
            assertEquals(3, r.size());
            assertTrue(r.containsKey("ime0"));
            assertTrue(r.containsKey("ime1"));
            assertTrue(r.containsKey("ime2"));
            ArraySet<String> subtypes0 = r.get("ime0");
            assertEquals(2, subtypes0.size());
            assertTrue(subtypes0.contains("subtype0"));
            assertTrue(subtypes0.contains("subtype1"));

            ArraySet<String> subtypes1 = r.get("ime1");
            assertEquals(2, subtypes1.size());
            assertTrue(subtypes0.contains("subtype1"));
            assertTrue(subtypes1.contains("subtype2"));

            ArraySet<String> subtypes2 = r.get("ime2");
            assertTrue(subtypes2.isEmpty());
        }
    }

    @Test
    public void testbuildInputMethodsAndSubtypesString() {
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            assertEquals("", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            map.put("ime0", new ArraySet<>());
            assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            ArraySet<String> subtypes1 = new ArraySet<>();
            subtypes1.add("subtype0");
            map.put("ime0", subtypes1);
            assertEquals("ime0;subtype0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            ArraySet<String> subtypes1 = new ArraySet<>();
            subtypes1.add("subtype0");
            subtypes1.add("subtype1");
            map.put("ime0", subtypes1);

            // We do not expect what order will be used to concatenate items in
            // InputMethodUtils.buildInputMethodsAndSubtypesString() hence enumerate all possible
            // permutations here.
            ArraySet<String> validSequences = new ArraySet<>();
            validSequences.add("ime0;subtype0;subtype1");
            validSequences.add("ime0;subtype1;subtype0");
            assertTrue(validSequences.contains(
                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            map.put("ime0", new ArraySet<>());
            map.put("ime1", new ArraySet<>());

            ArraySet<String> validSequences = new ArraySet<>();
            validSequences.add("ime0:ime1");
            validSequences.add("ime1:ime0");
            assertTrue(validSequences.contains(
                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            ArraySet<String> subtypes1 = new ArraySet<>();
            subtypes1.add("subtype0");
            map.put("ime0", subtypes1);
            map.put("ime1", new ArraySet<>());

            ArraySet<String> validSequences = new ArraySet<>();
            validSequences.add("ime0;subtype0:ime1");
            validSequences.add("ime1;ime0;subtype0");
            assertTrue(validSequences.contains(
                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            ArraySet<String> subtypes1 = new ArraySet<>();
            subtypes1.add("subtype0");
            subtypes1.add("subtype1");
            map.put("ime0", subtypes1);
            map.put("ime1", new ArraySet<>());

            ArraySet<String> validSequences = new ArraySet<>();
            validSequences.add("ime0;subtype0;subtype1:ime1");
            validSequences.add("ime0;subtype1;subtype0:ime1");
            validSequences.add("ime1:ime0;subtype0;subtype1");
            validSequences.add("ime1:ime0;subtype1;subtype0");
            assertTrue(validSequences.contains(
                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            ArraySet<String> subtypes1 = new ArraySet<>();
            subtypes1.add("subtype0");
            map.put("ime0", subtypes1);

            ArraySet<String> subtypes2 = new ArraySet<>();
            subtypes2.add("subtype1");
            map.put("ime1", subtypes2);

            ArraySet<String> validSequences = new ArraySet<>();
            validSequences.add("ime0;subtype0:ime1;subtype1");
            validSequences.add("ime1;subtype1:ime0;subtype0");
            assertTrue(validSequences.contains(
                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
        }
        {
            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
            ArraySet<String> subtypes1 = new ArraySet<>();
            subtypes1.add("subtype0");
            subtypes1.add("subtype1");
            map.put("ime0", subtypes1);

            ArraySet<String> subtypes2 = new ArraySet<>();
            subtypes2.add("subtype2");
            subtypes2.add("subtype3");
            map.put("ime1", subtypes2);

            ArraySet<String> validSequences = new ArraySet<>();
            validSequences.add("ime0;subtype0;subtype1:ime1;subtype2;subtype3");
            validSequences.add("ime0;subtype1;subtype0:ime1;subtype2;subtype3");
            validSequences.add("ime0;subtype0;subtype1:ime1;subtype3;subtype2");
            validSequences.add("ime0;subtype1;subtype0:ime1;subtype3;subtype2");
            validSequences.add("ime1;subtype2;subtype3:ime0;subtype0;subtype1");
            validSequences.add("ime2;subtype3;subtype2:ime0;subtype0;subtype1");
            validSequences.add("ime3;subtype2;subtype3:ime0;subtype1;subtype0");
            validSequences.add("ime4;subtype3;subtype2:ime0;subtype1;subtype0");
            assertTrue(validSequences.contains(
                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
        }
    }

    @Test
    @Test
    public void testConstructLocaleFromString() throws Exception {
    public void testConstructLocaleFromString() throws Exception {
        assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en"));
        assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en"));
+2 −2
Original line number Original line Diff line number Diff line
@@ -60,7 +60,7 @@ public class InputMethodAndSubtypeUtil {


    // InputMethods and subtypes are saved in the settings as follows:
    // InputMethods and subtypes are saved in the settings as follows:
    // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
    // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
    private static String buildInputMethodsAndSubtypesString(
    public static String buildInputMethodsAndSubtypesString(
            final HashMap<String, HashSet<String>> imeToSubtypesMap) {
            final HashMap<String, HashSet<String>> imeToSubtypesMap) {
        final StringBuilder builder = new StringBuilder();
        final StringBuilder builder = new StringBuilder();
        for (final String imi : imeToSubtypesMap.keySet()) {
        for (final String imi : imeToSubtypesMap.keySet()) {
@@ -115,7 +115,7 @@ public class InputMethodAndSubtypeUtil {
        return parseInputMethodsAndSubtypesString(enabledInputMethodsStr);
        return parseInputMethodsAndSubtypesString(enabledInputMethodsStr);
    }
    }


    private static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
    public static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
            final String inputMethodsAndSubtypesString) {
            final String inputMethodsAndSubtypesString) {
        final HashMap<String, HashSet<String>> subtypesMap = new HashMap<>();
        final HashMap<String, HashSet<String>> subtypesMap = new HashMap<>();
        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
+190 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.inputmethod;

import static com.google.common.truth.Truth.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

import java.util.HashMap;
import java.util.HashSet;

@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilTest {

    private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();

    private static HashSet<String> asHashSet(String... strings) {
        HashSet<String> hashSet = new HashSet<>();
        for (String s : strings) {
            hashSet.add(s);
        }
        return hashSet;
    }

    @Test
    public void testParseInputMethodsAndSubtypesString_EmptyString() {
        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("")).isEmpty();
        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(null)).isEmpty();
    }

    @Test
    public void testParseInputMethodsAndSubtypesString_SingleImeNoSubtype() {
        HashMap<String, HashSet<String>> r =
                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0");
        assertThat(r).containsExactly("ime0", EMPTY_STRING_SET);
    }

    @Test
    public void testParseInputMethodsAndSubtypesString_MultipleImesNoSubtype() {
        HashMap<String, HashSet<String>> r =
                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0:ime1");
        assertThat(r).containsExactly("ime0", EMPTY_STRING_SET, "ime1", EMPTY_STRING_SET);
    }

    @Test
    public void testParseInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
        HashMap<String, HashSet<String>> r =
                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0;subtype0");
        assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
    }

    @Test
    public void testParseInputMethodsAndSubtypesString_SingleImeDuplicateSameSubtypes() {
        HashMap<String, HashSet<String>> r =
                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
                        "ime0;subtype0;subtype0");
        assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
    }

    @Test
    public void testParseInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
        HashMap<String, HashSet<String>> r =
                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
                        "ime0;subtype0;subtype1");
        assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"));
    }

    @Test
    public void testParseInputMethodsAndSubtypesString_MultiplePairsOfImeSubtype() {
        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
                "ime0;subtype0:ime1;subtype1"))
                .containsExactly("ime0", asHashSet("subtype0"), "ime1", asHashSet("subtype1"));
        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
                "ime0;subtype0;subtype1:ime1;subtype2"))
                .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
                        "ime1", asHashSet("subtype2"));
        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
                "ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
                .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
                        "ime1", asHashSet("subtype1", "subtype2"));

    }

    @Test
    public void testParseInputMethodsAndSubtypesString_MixedImeSubtypePairsAndImeNoSubtype() {
        HashMap<String, HashSet<String>> r =
                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
                        "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
        assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"),
                "ime1", asHashSet("subtype1", "subtype2"),
                "ime2", EMPTY_STRING_SET);
    }

    @Test
    public void testBuildInputMethodsAndSubtypesString_EmptyInput() {
        HashMap<String, HashSet<String>> map = new HashMap<>();
        assertThat(map).isEmpty();
    }

    @Test
    public void testBuildInputMethodsAndSubtypesString_SingleIme() {
        HashMap<String, HashSet<String>> map = new HashMap<>();
        map.put("ime0", new HashSet<>());
        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
        assertThat(result).isEqualTo("ime0");
    }

    @Test
    public void testBuildInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
        HashMap<String, HashSet<String>> map = new HashMap<>();
        map.put("ime0", asHashSet("subtype0"));
        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
        assertThat(result).isEqualTo("ime0;subtype0");
    }

    @Test
    public void testBuildInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
        HashMap<String, HashSet<String>> map = new HashMap<>();
        map.put("ime0", asHashSet("subtype0", "subtype1"));
        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);

        // We do not expect what order will be used to concatenate items in
        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
        // permutations here.
        assertThat(result).matches("(ime0;subtype0;subtype1)|(ime0;subtype1;subtype0)");
    }

    @Test
    public void testBuildInputMethodsAndSubtypesString_MultipleImesNoSubtypes() {
        HashMap<String, HashSet<String>> map = new HashMap<>();
        map.put("ime0", EMPTY_STRING_SET);
        map.put("ime1", EMPTY_STRING_SET);
        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);

        // We do not expect what order will be used to concatenate items in
        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
        // permutations here.
        assertThat(result).matches("(ime0:ime1)|(ime1:ime0)");
    }

    @Test
    public void testBuildInputMethodsAndSubtypesString_MultipleImesWithAndWithoutSubtypes() {
        HashMap<String, HashSet<String>> map = new HashMap<>();
        map.put("ime0", asHashSet("subtype0", "subtype1"));
        map.put("ime1", EMPTY_STRING_SET);
        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);

        // We do not expect what order will be used to concatenate items in
        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
        // permutations here.
        assertThat(result).matches("(ime0;subtype0;subtype1:ime1)|(ime0;subtype1;subtype0:ime1)"
                + "|(ime1:ime0;subtype0;subtype1)|(ime1:ime0;subtype1;subtype0)");
    }

    @Test
    public void testBuildInputMethodsAndSubtypesString_MultipleImesWithSubtypes() {
        HashMap<String, HashSet<String>> map = new HashMap<>();
        map.put("ime0", asHashSet("subtype0", "subtype1"));
        map.put("ime1", asHashSet("subtype2", "subtype3"));
        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);

        // We do not expect what order will be used to concatenate items in
        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
        // permutations here.
        assertThat(result).matches("|(ime0;subtype0;subtype1:ime1;subtype2;subtype3)|"
                + "|(ime0;subtype1;subtype0:ime1;subtype2;subtype3)|"
                + "|(ime0;subtype0;subtype1:ime1;subtype3;subtype2)|"
                + "|(ime0;subtype1;subtype0:ime1;subtype3;subtype2)|"
                + "|(ime1;subtype2;subtype3:ime0;subtype0;subtype1)|"
                + "|(ime2;subtype3;subtype2:ime0;subtype0;subtype1)|"
                + "|(ime3;subtype2;subtype3:ime0;subtype1;subtype0)|"
                + "|(ime4;subtype3;subtype2:ime0;subtype1;subtype0)");
    }
}