package com.android.ide.common.resources;

import com.android.ide.common.rendering.api.ArrayResourceValue;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.Density;
import com.android.resources.ResourceType;
import com.google.common.collect.Lists;

import junit.framework.TestCase;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

public class ResourceResolverTest extends TestCase {
    public void test() throws Exception {
        TestResourceRepository frameworkRepository = TestResourceRepository.create(true,
                new Object[]{
                        "values/strings.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <string name=\"ok\">Ok</string>\n"
                        + "</resources>\n",

                        "values/themes.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <style name=\"Theme\">\n"
                        + "        <item name=\"colorForeground\">@android:color/bright_foreground_dark</item>\n"
                        + "        <item name=\"colorBackground\">@android:color/background_dark</item>\n"
                        + "    </style>\n"
                        + "    <style name=\"Theme.Light\">\n"
                        + "        <item name=\"colorBackground\">@android:color/background_light</item>\n"
                        + "        <item name=\"colorForeground\">@color/bright_foreground_light</item>\n"
                        + "    </style>\n"
                        + "</resources>\n",

                        "values/colors.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <color name=\"background_dark\">#ff000000</color>\n"
                        + "    <color name=\"background_light\">#ffffffff</color>\n"
                        + "    <color name=\"bright_foreground_dark\">@android:color/background_light</color>\n"
                        + "    <color name=\"bright_foreground_light\">@android:color/background_dark</color>\n"
                        + "</resources>\n",

                        "values/ids.xml",  ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <item name=\"some_framework_id\" type=\"id\" />\n"
                        + "</resources>\n",
                });

        TestResourceRepository projectRepository = TestResourceRepository.create(false,
                new Object[]{
                        "layout/layout1.xml", "<!--contents doesn't matter-->",

                        "layout/layout2.xml", "<!--contents doesn't matter-->",

                        "layout-land/layout1.xml", "<!--contents doesn't matter-->",

                        "layout-land/onlyLand.xml", "<!--contents doesn't matter-->",

                        "drawable/graphic.9.png", new byte[0],

                        "mipmap-xhdpi/ic_launcher.png", new byte[0],

                        "values/styles.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <style name=\"MyTheme\" parent=\"android:Theme.Light\">\n"
                        + "        <item name=\"android:textColor\">#999999</item>\n"
                        + "        <item name=\"foo\">?android:colorForeground</item>\n"
                        + "    </style>\n"
                        + "    <style name=\"MyTheme.Dotted1\" parent=\"\">\n"
                        + "    </style>"
                        + "    <style name=\"MyTheme.Dotted2\">\n"
                        + "    </style>"
                        + "    <style name=\"RandomStyle\">\n"
                        + "        <item name=\"android:text\">&#169; Copyright</item>\n"
                        + "    </style>"
                        + "    <style name=\"RandomStyle2\" parent=\"RandomStyle\">\n"
                        + "    </style>"
                        + "    <style name=\"Theme.FakeTheme\" parent=\"\">\n"
                        + "    </style>"
                        + "    <style name=\"Theme\" parent=\"RandomStyle\">\n"
                        + "    </style>"
                        + "</resources>\n",

                        "values/strings.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <item type=\"id\" name=\"action_bar_refresh\" />\n"
                        + "    <item type=\"dimen\" name=\"dialog_min_width_major\">45%</item>\n"
                        + "    <string name=\"home_title\">Home Sample</string>\n"
                        + "    <string name=\"show_all_apps\">All</string>\n"
                        + "    <string name=\"menu_wallpaper\">Wallpaper</string>\n"
                        + "    <string name=\"menu_search\">Search</string>\n"
                        + "    <string name=\"menu_settings\">Settings</string>\n"
                        + "    <string name=\"dummy\" translatable=\"false\">Ignore Me</string>\n"
                        + "    <string name=\"wallpaper_instructions\">Tap picture to set portrait wallpaper</string>\n"
                        + "</resources>\n",

                        "values-es/strings.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <string name=\"show_all_apps\">Todo</string>\n"
                        + "</resources>\n",

                        "values/arrays.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <string name=\"first\">Item1</string>\n"
                        + "    <string-array name=\"my_array\">\n"
                        + "        <item>@string/first</item>\n"
                        + "        <item>Item2</item>\n"
                        + "        <item>Item3</item>\n"
                        + "    </string-array>\n"
                        + "</resources>\n",
                });

        assertFalse(projectRepository.isFrameworkRepository());
        FolderConfiguration config = FolderConfiguration.getConfigForFolder("values-es-land");
        assertNotNull(config);
        Map<ResourceType, Map<String, ResourceValue>> projectResources =
                projectRepository.getConfiguredResources(config);
        Map<ResourceType, Map<String, ResourceValue>> frameworkResources =
                frameworkRepository.getConfiguredResources(config);
        assertNotNull(projectResources);
        ResourceResolver resolver = ResourceResolver.create(projectResources, frameworkResources,
                "MyTheme", true);
        assertNotNull(resolver);

        LayoutLog logger = new LayoutLog() {
            @Override
            public void warning(String tag, String message, Object data) {
                fail(message);
            }

            @Override
            public void fidelityWarning(String tag, String message, Throwable throwable,
                    Object data) {
                fail(message);
            }

            @Override
            public void error(String tag, String message, Object data) {
                fail(message);
            }

            @Override
            public void error(String tag, String message, Throwable throwable, Object data) {
                fail(message);
            }
        };
        resolver.setLogger(logger);

        assertEquals("MyTheme", resolver.getThemeName());
        assertTrue(resolver.isProjectTheme());

        // findResValue
        assertNotNull(resolver.findResValue("@string/show_all_apps", false));
        assertNotNull(resolver.findResValue("@android:string/ok", false));
        assertNotNull(resolver.findResValue("@android:string/ok", true));
        assertEquals("Todo", resolver.findResValue("@string/show_all_apps", false).getValue());
        assertEquals("Home Sample", resolver.findResValue("@string/home_title", false).getValue());
        assertEquals("45%", resolver.findResValue("@dimen/dialog_min_width_major",
                false).getValue());
        assertNotNull(resolver.findResValue("@android:color/bright_foreground_dark", true));
        assertEquals("@android:color/background_light",
                resolver.findResValue("@android:color/bright_foreground_dark", true).getValue());
        assertEquals("#ffffffff",
                resolver.findResValue("@android:color/background_light", true).getValue());
        assertNull(resolver.findResValue("?attr/non_existent_style", false)); // shouldn't log an error.
        assertEquals(Density.XHIGH,
                ((DensityBasedResourceValue) resolver.findResValue("@mipmap/ic_launcher", false))
                        .getResourceDensity());  // also ensures that returned value is instance of DensityBasedResourceValue

        // getTheme
        StyleResourceValue myTheme = resolver.getTheme("MyTheme", false);
        assertNotNull(myTheme);
        assertSame(resolver.findResValue("@style/MyTheme", false), myTheme);
        assertNull(resolver.getTheme("MyTheme", true));
        assertNull(resolver.getTheme("MyNonexistentTheme", true));
        StyleResourceValue themeLight = resolver.getTheme("Theme.Light", true);
        assertNotNull(themeLight);
        StyleResourceValue theme = resolver.getTheme("Theme", true);
        assertNotNull(theme);

        // getParent
        StyleResourceValue parent = resolver.getParent(myTheme);
        assertNotNull(parent);
        assertEquals("Theme.Light", parent.getName());

        // themeIsParentOf
        assertTrue(resolver.themeIsParentOf(themeLight, myTheme));
        assertFalse(resolver.themeIsParentOf(myTheme, themeLight));
        assertTrue(resolver.themeIsParentOf(theme, themeLight));
        assertFalse(resolver.themeIsParentOf(themeLight, theme));
        assertTrue(resolver.themeIsParentOf(theme, myTheme));
        assertFalse(resolver.themeIsParentOf(myTheme, theme));
        StyleResourceValue dotted1 = resolver.getTheme("MyTheme.Dotted1", false);
        assertNotNull(dotted1);
        StyleResourceValue dotted2 = resolver.getTheme("MyTheme.Dotted2", false);
        assertNotNull(dotted2);
        assertTrue(resolver.themeIsParentOf(myTheme, dotted2));
        assertFalse(resolver.themeIsParentOf(myTheme, dotted1)); // because parent=""

        // isTheme
        assertFalse(resolver.isTheme(resolver.findResValue("@style/RandomStyle", false), null));
        assertFalse(resolver.isTheme(resolver.findResValue("@style/RandomStyle2", false), null));
        assertFalse(resolver.isTheme(resolver.findResValue("@style/Theme.FakeTheme", false), null));
        assertFalse(resolver.isTheme(resolver.findResValue("@style/Theme", false), null));
        //    check XML escaping in value resources
        StyleResourceValue randomStyle = (StyleResourceValue) resolver.findResValue(
                "@style/RandomStyle", false);
        assertEquals("\u00a9 Copyright", randomStyle.getItem("text", true).getValue());
        assertTrue(resolver.isTheme(resolver.findResValue("@style/MyTheme.Dotted2", false), null));
        assertFalse(resolver.isTheme(resolver.findResValue("@style/MyTheme.Dotted1", false),
                null));
        assertTrue(resolver.isTheme(resolver.findResValue("@style/MyTheme", false), null));
        assertTrue(resolver.isTheme(resolver.findResValue("@android:style/Theme.Light", false),
                null));
        assertTrue(resolver.isTheme(resolver.findResValue("@android:style/Theme", false), null));

        // findItemInStyle
        assertNotNull(resolver.findItemInStyle(myTheme, "colorForeground", true));
        assertEquals("@color/bright_foreground_light",
                resolver.findItemInStyle(myTheme, "colorForeground", true).getValue());
        assertNotNull(resolver.findItemInStyle(dotted2, "colorForeground", true));
        assertNull(resolver.findItemInStyle(dotted1, "colorForeground", true));

        // findItemInTheme
        assertNotNull(resolver.findItemInTheme("colorForeground", true));
        assertEquals("@color/bright_foreground_light",
                resolver.findItemInTheme("colorForeground", true).getValue());
        assertEquals("@color/bright_foreground_light",
                resolver.findResValue("?colorForeground", true).getValue());
        ResourceValue target = new ResourceValue(ResourceType.STRING, "dummy", false);
        target.setValue("?foo");
        assertEquals("#ff000000", resolver.resolveResValue(target).getValue());

        // getFrameworkResource
        assertNull(resolver.getFrameworkResource(ResourceType.STRING, "show_all_apps"));
        assertNotNull(resolver.getFrameworkResource(ResourceType.STRING, "ok"));
        assertEquals("Ok", resolver.getFrameworkResource(ResourceType.STRING, "ok").getValue());

        // getProjectResource
        assertNull(resolver.getProjectResource(ResourceType.STRING, "ok"));
        assertNotNull(resolver.getProjectResource(ResourceType.STRING, "show_all_apps"));
        assertEquals("Todo", resolver.getProjectResource(ResourceType.STRING,
                "show_all_apps").getValue());


        // resolveResValue
        //    android:color/bright_foreground_dark ⇒ @android:color/background_light ⇒ white
        assertEquals("Todo", resolver.resolveResValue(
                resolver.findResValue("@string/show_all_apps", false)).getValue());
        assertEquals("#ffffffff", resolver.resolveResValue(
                resolver.findResValue("@android:color/bright_foreground_dark", false)).getValue());

        // resolveValue
        assertEquals("#ffffffff",
                resolver.resolveValue(ResourceType.STRING, "bright_foreground_dark",
                        "@android:color/background_light", true).getValue());
        assertFalse(resolver.resolveValue(null, "id", "@+id/some_framework_id", false)
                .isFramework());
        // error expected.
        boolean failed = false;
        ResourceValue val = null;
        try {
            val = resolver.resolveValue(ResourceType.STRING, "bright_foreground_dark",
                    "@color/background_light", false);
        } catch (AssertionError expected) {
            failed = true;
        }
        assertTrue("incorrect resource returned: " + val, failed);
        ResourceValue array = resolver
                .resolveResValue(resolver.getProjectResource(ResourceType.ARRAY, "my_array"));
        assertTrue("array" + "my_array" + "resolved incorrectly as " + array.getResourceType()
                .getName(), array instanceof ArrayResourceValue);

        // themeExtends
        assertTrue(resolver.themeExtends("@android:style/Theme", "@android:style/Theme"));
        assertTrue(resolver.themeExtends("@android:style/Theme", "@android:style/Theme.Light"));
        assertFalse(resolver.themeExtends("@android:style/Theme.Light", "@android:style/Theme"));
        assertTrue(resolver.themeExtends("@style/MyTheme.Dotted2", "@style/MyTheme.Dotted2"));
        assertTrue(resolver.themeExtends("@style/MyTheme", "@style/MyTheme.Dotted2"));
        assertTrue(resolver.themeExtends("@android:style/Theme.Light", "@style/MyTheme.Dotted2"));
        assertTrue(resolver.themeExtends("@android:style/Theme", "@style/MyTheme.Dotted2"));
        assertFalse(resolver.themeExtends("@style/MyTheme.Dotted1", "@style/MyTheme.Dotted2"));

        // Switch to MyTheme.Dotted1 (to make sure the parent="" inheritance works properly.)
        // To do that we need to create a new resource resolver.
        resolver = ResourceResolver.create(projectResources, frameworkResources,
                "MyTheme.Dotted1", true);
        resolver.setLogger(logger);
        assertNotNull(resolver);
        assertEquals("MyTheme.Dotted1", resolver.getThemeName());
        assertTrue(resolver.isProjectTheme());
        assertNull(resolver.findItemInTheme("colorForeground", true));

        resolver = ResourceResolver.create(projectResources, frameworkResources,
                "MyTheme.Dotted2", true);
        resolver.setLogger(logger);
        assertNotNull(resolver);
        assertEquals("MyTheme.Dotted2", resolver.getThemeName());
        assertTrue(resolver.isProjectTheme());
        assertNotNull(resolver.findItemInTheme("colorForeground", true));

        // Test recording resolver
        List<ResourceValue> chain = Lists.newArrayList();
        resolver = ResourceResolver.create(projectResources, frameworkResources, "MyTheme", true);
        resolver = resolver.createRecorder(chain);
        assertNotNull(resolver.findResValue("@android:color/bright_foreground_dark", true));
        ResourceValue v = resolver.findResValue("@android:color/bright_foreground_dark", false);
        chain.clear();
        assertEquals("#ffffffff", resolver.resolveResValue(v).getValue());
        assertEquals("@android:color/bright_foreground_dark => "
                + "@android:color/background_light => #ffffffff",
                ResourceItemResolver.getDisplayString("@android:color/bright_foreground_dark",
                        chain));

        frameworkRepository.dispose();
        projectRepository.dispose();
    }

    public void testMissingMessage() throws Exception {
        TestResourceRepository projectRepository = TestResourceRepository.create(false,
                new Object[]{
                        "values/colors.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <color name=\"loop1\">@color/loop1</color>\n"
                        + "    <color name=\"loop2a\">@color/loop2b</color>\n"
                        + "    <color name=\"loop2b\">@color/loop2a</color>\n"
                        + "</resources>\n",

                });

        assertFalse(projectRepository.isFrameworkRepository());
        FolderConfiguration config = FolderConfiguration.getConfigForFolder("values-es-land");
        assertNotNull(config);
        Map<ResourceType, Map<String, ResourceValue>> projectResources =
                projectRepository.getConfiguredResources(config);
        assertNotNull(projectResources);
        ResourceResolver resolver = ResourceResolver.create(projectResources, projectResources,
                "MyTheme", true);
        final AtomicBoolean wasWarned = new AtomicBoolean(false);
        LayoutLog logger = new LayoutLog() {
            @Override
            public void warning(String tag, String message, Object data) {
                if ("Couldn't resolve resource @android:string/show_all_apps".equals(message)) {
                    wasWarned.set(true);
                } else {
                    fail(message);
                }
            }
        };
        resolver.setLogger(logger);
        assertNull(resolver.findResValue("@string/show_all_apps", true));
        assertTrue(wasWarned.get());
        projectRepository.dispose();
    }

    public void testLoop() throws Exception {
        TestResourceRepository projectRepository = TestResourceRepository.create(false,
                new Object[]{
                        "values/colors.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <color name=\"loop1\">@color/loop1</color>\n"
                        + "    <color name=\"loop2a\">@color/loop2b</color>\n"
                        + "    <color name=\"loop2b\">@color/loop2a</color>\n"
                        + "</resources>\n",

                });

        assertFalse(projectRepository.isFrameworkRepository());
        FolderConfiguration config = FolderConfiguration.getConfigForFolder("values-es-land");
        assertNotNull(config);
        Map<ResourceType, Map<String, ResourceValue>> projectResources =
                projectRepository.getConfiguredResources(config);
        assertNotNull(projectResources);
        ResourceResolver resolver = ResourceResolver.create(projectResources, projectResources,
                "MyTheme", true);
        assertNotNull(resolver);

        final AtomicBoolean wasWarned = new AtomicBoolean(false);
        LayoutLog logger = new LayoutLog() {
            @Override
            public void error(String tag, String message, Object data) {
                if (("Potential stack overflow trying to resolve "
                        + "'@color/loop1': cyclic resource definitions?"
                        + " Render may not be accurate.").equals(message)) {
                    wasWarned.set(true);
                } else if (("Potential stack overflow trying to resolve "
                        + "'@color/loop2b': cyclic resource definitions? "
                        + "Render may not be accurate.").equals(message)) {
                    wasWarned.set(true);
                } else {
                    fail(message);
                }
            }
        };
        resolver.setLogger(logger);

        assertNotNull(resolver.findResValue("@color/loop1", false));
        resolver.resolveResValue(resolver.findResValue("@color/loop1", false));
        assertTrue(wasWarned.get());

        wasWarned.set(false);
        assertNotNull(resolver.findResValue("@color/loop2a", false));
        resolver.resolveResValue(resolver.findResValue("@color/loop2a", false));
        assertTrue(wasWarned.get());

        projectRepository.dispose();
    }

    public void testParentCycle() throws IOException {
        TestResourceRepository projectRepository = TestResourceRepository.create(false,
                new Object[]{
                        "values/styles.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <style name=\"ButtonStyle.Base\">\n"
                        + "        <item name=\"android:textColor\">#ff0000</item>\n"
                        + "    </style>\n"
                        + "    <style name=\"ButtonStyle\" parent=\"ButtonStyle.Base\">\n"
                        + "        <item name=\"android:layout_height\">40dp</item>\n"
                        + "    </style>\n"
                        + "</resources>\n",

                        "layouts/layout.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
                        + "    android:layout_width=\"match_parent\"\n"
                        + "    android:layout_height=\"match_parent\">\n"
                        + "\n"
                        + "    <TextView\n"
                        + "        style=\"@style/ButtonStyle\"\n"
                        + "        android:layout_width=\"wrap_content\"\n"
                        + "        android:layout_height=\"wrap_content\" />\n"
                        + "\n"
                        + "</RelativeLayout>\n",

                });
        assertFalse(projectRepository.isFrameworkRepository());
        FolderConfiguration config = FolderConfiguration.getConfigForFolder("values-es-land");
        assertNotNull(config);
        Map<ResourceType, Map<String, ResourceValue>> projectResources =
                projectRepository.getConfiguredResources(config);
        assertNotNull(projectResources);
        ResourceResolver resolver = ResourceResolver.create(projectResources, projectResources,
                "ButtonStyle", true);
        assertNotNull(resolver);

        final AtomicBoolean wasWarned = new AtomicBoolean(false);
        LayoutLog logger = new LayoutLog() {
            @Override
            public void error(String tag, String message, Object data) {
                assertEquals("Cyclic style parent definitions: \"ButtonStyle\" specifies "
                        + "parent \"ButtonStyle.Base\" implies parent \"ButtonStyle\"", message);
                assertEquals(LayoutLog.TAG_BROKEN, tag);
                wasWarned.set(true);
            }
        };
        resolver.setLogger(logger);

        StyleResourceValue buttonStyle = (StyleResourceValue) resolver.findResValue(
                "@style/ButtonStyle", false);
        ResourceValue textColor = resolver.findItemInStyle(buttonStyle, "textColor", true);
        assertNotNull(textColor);
        assertEquals("#ff0000", textColor.getValue());
        assertFalse(wasWarned.get());
        ResourceValue missing = resolver.findItemInStyle(buttonStyle, "missing", true);
        assertNull(missing);
        assertTrue(wasWarned.get());

        projectRepository.dispose();
    }

    public void testSetDeviceDefaults() throws Exception {
        TestResourceRepository frameworkRepository = TestResourceRepository.create(true,
            new Object[] {
                "values/themes.xml", ""
                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                + "<resources>\n"
                + "    <style name=\"Theme.Light\" parent=\"\">\n"
                + "         <item name=\"android:textColor\">#ff0000</item>\n"
                + "    </style>\n"
                + "    <style name=\"Theme.Holo.Light\" parent=\"Theme.Light\">\n"
                + "         <item name=\"android:textColor\">#00ff00</item>\n"
                + "    </style>\n"
                + "    <style name=\"Theme.DeviceDefault.Light\" parent=\"Theme.Holo.Light\"/>\n"
                + "    <style name=\"Theme\" parent=\"\">\n"
                + "         <item name=\"android:textColor\">#000000</item>\n"
                + "    </style>\n"
                + "    <style name=\"Theme.Holo\" parent=\"Theme\">\n"
                + "         <item name=\"android:textColor\">#0000ff</item>\n"
                + "    </style>\n"
                + "    <style name=\"Theme.DeviceDefault\" parent=\"Theme.Holo\"/>\n"
                + "</resources>\n",

                "values/styles.xml", ""
                + "<resources>\n"
                + "    <style name=\"Widget.Button.Small\">\n"
                + "         <item name=\"android:textColor\">#000000</item>\n"
                + "    </style>\n"
                + "    <style name=\"Widget.Holo.Button.Small\">\n"
                + "         <item name=\"android:textColor\">#ffffff</item>\n"
                + "    </style>\n"
                + "    <style name=\"Widget.DeviceDefault.Button.Small\" parent=\"Widget.Holo.Button.Small\" />\n"
                + "    <style name=\"ButtonBar\">\n"
                + "         <item name=\"android:textColor\">#000000</item>\n"
                + "    </style>\n"
                + "    <style name=\"Holo.ButtonBar\">\n"
                + "         <item name=\"android:textColor\">#ffffff</item>\n"
                + "    </style>\n"
                + "    <style name=\"DeviceDefault.ButtonBar\" parent=\"Holo.ButtonBar\" />\n"
                + "</resources>\n",
        });

        TestResourceRepository projectRepository = TestResourceRepository.create(false,
            new Object[] {
                "values/themes.xml", ""
                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                + "<resources>\n"
                + "    <style name=\"AppTheme\" parent=\"android:Theme.DeviceDefault.Light\"/>\n"
                + "    <style name=\"AppTheme.Dark\" parent=\"android:Theme.DeviceDefault\"/>\n"
                + "</resources>\n"
        });

        assertFalse(projectRepository.isFrameworkRepository());
        FolderConfiguration config = FolderConfiguration.getConfigForFolder("values-es-land");
        assertNotNull(config);
        Map<ResourceType, Map<String, ResourceValue>> projectResources = projectRepository
                .getConfiguredResources(config);
        Map<ResourceType, Map<String, ResourceValue>> frameworkResources = frameworkRepository
                .getConfiguredResources(config);
        assertNotNull(projectResources);
        ResourceResolver lightResolver = ResourceResolver.create(projectResources,
                frameworkResources, "AppTheme", true);
        assertNotNull(lightResolver);
        ResourceValue textColor = lightResolver.findItemInTheme("textColor", true);
        assertNotNull(textColor);
        assertEquals("#00ff00", textColor.getValue());

        lightResolver.setDeviceDefaults(ResourceResolver.LEGACY_THEME);
        textColor = lightResolver.findItemInTheme("textColor", true);
        assertNotNull(textColor);
        assertEquals("#ff0000", textColor.getValue());

        ResourceResolver darkResolver = ResourceResolver.create(projectResources,
                frameworkResources, "AppTheme.Dark", true);
        assertNotNull(darkResolver);
        textColor = darkResolver.findItemInTheme("textColor", true);
        assertNotNull(textColor);
        assertEquals("#0000ff", textColor.getValue());

        darkResolver.setDeviceDefaults(ResourceResolver.LEGACY_THEME);
        textColor = darkResolver.findItemInTheme("textColor", true);
        assertNotNull(textColor);
        assertEquals("#000000", textColor.getValue());

        // Check styles are correctly patched. We could use either resolver for that
        textColor = darkResolver
                .findItemInStyle(lightResolver.getStyle("Widget.DeviceDefault.Button.Small", true),
                        "textColor", true);
        assertEquals("#000000", textColor.getValue());
        textColor = darkResolver
                .findItemInStyle(lightResolver.getStyle("DeviceDefault.ButtonBar", true),
                        "textColor", true);
        assertEquals("#000000", textColor.getValue());

        darkResolver.setDeviceDefaults("Holo");
        textColor = darkResolver
                .findItemInStyle(lightResolver.getStyle("Widget.DeviceDefault.Button.Small", true),
                        "textColor", true);
        assertEquals("#ffffff", textColor.getValue());
        textColor = darkResolver
                .findItemInStyle(lightResolver.getStyle("DeviceDefault.ButtonBar", true),
                        "textColor", true);
        assertEquals("#ffffff", textColor.getValue());
    }

    public void testCycle() throws Exception {
        TestResourceRepository frameworkRepository = TestResourceRepository.create(true,
                new Object[] {
                        "values/themes.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <style name=\"Theme.DeviceDefault.Light\"/>\n"
                        + "</resources>\n",
                });

        TestResourceRepository projectRepository = TestResourceRepository.create(false,
                new Object[] {
                        "values/themes.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources>\n"
                        + "    <style name=\"AppTheme\" parent=\"android:Theme.DeviceDefault.Light\"/>\n"
                        + "    <style name=\"AppTheme.Dark\" parent=\"android:Theme.DeviceDefault\"/>\n"
                        + "    <style name=\"foo\" parent=\"bar\"/>\n"
                        + "    <style name=\"bar\" parent=\"foo\"/>\n"
                        + "</resources>\n"
                });

        assertFalse(projectRepository.isFrameworkRepository());
        FolderConfiguration config = FolderConfiguration.getConfigForFolder("values");
        assertNotNull(config);
        Map<ResourceType, Map<String, ResourceValue>> projectResources = projectRepository
                .getConfiguredResources(config);
        Map<ResourceType, Map<String, ResourceValue>> frameworkResources = frameworkRepository
                .getConfiguredResources(config);
        assertNotNull(projectResources);
        ResourceResolver resolver = ResourceResolver.create(projectResources,
                frameworkResources, "AppTheme", true);

        final AtomicBoolean wasWarned = new AtomicBoolean(false);
        LayoutLog logger = new LayoutLog() {
            @Override
            public void error(String tag, String message, Object data) {
                if ("Cyclic style parent definitions: \"foo\" specifies parent \"bar\" specifies parent \"foo\"".equals(message)) {
                    wasWarned.set(true);
                } else {
                    fail(message);
                }
            }
        };
        resolver.setLogger(logger);
        assertFalse(resolver.isTheme(resolver.findResValue("@style/foo", false), null));
        assertTrue(wasWarned.get());

        projectRepository.dispose();

    }
}
