1/*
2 * Copyright 2017 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
17package com.android.server.display;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertNull;
22import static org.junit.Assert.assertTrue;
23
24import android.hardware.display.BrightnessConfiguration;
25import android.support.test.filters.SmallTest;
26import android.support.test.runner.AndroidJUnit4;
27import android.util.AtomicFile;
28import android.util.Pair;
29
30import org.junit.Before;
31import org.junit.Test;
32import org.junit.runner.RunWith;
33
34import java.io.FileInputStream;
35import java.io.ByteArrayInputStream;
36import java.io.ByteArrayOutputStream;
37import java.io.FileNotFoundException;
38import java.io.InputStream;
39import java.io.IOException;
40import java.io.OutputStream;
41import java.io.PrintWriter;
42import java.nio.charset.StandardCharsets;
43
44@SmallTest
45@RunWith(AndroidJUnit4.class)
46public class PersistentDataStoreTest {
47    private PersistentDataStore mDataStore;
48    private TestInjector mInjector;
49
50    @Before
51    public void setUp() {
52        mInjector = new TestInjector();
53        mDataStore = new PersistentDataStore(mInjector);
54    }
55
56    @Test
57    public void testLoadingBrightnessConfigurations() {
58        String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
59                + "<display-manager-state>\n"
60                + "  <brightness-configurations>\n"
61                + "    <brightness-configuration"
62                + "         user-serial=\"1\""
63                + "         package-name=\"example.com\""
64                + "         timestamp=\"123456\">\n"
65                + "      <brightness-curve description=\"something\">\n"
66                + "        <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
67                + "        <brightness-point lux=\"25\" nits=\"35.94\"/>\n"
68                + "      </brightness-curve>\n"
69                + "    </brightness-configuration>\n"
70                + "    <brightness-configuration user-serial=\"3\">\n"
71                + "      <brightness-curve>\n"
72                + "        <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
73                + "        <brightness-point lux=\"10.2\" nits=\"15\"/>\n"
74                + "      </brightness-curve>\n"
75                + "    </brightness-configuration>\n"
76                + "  </brightness-configurations>\n"
77                + "</display-manager-state>\n";
78        InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
79        mInjector.setReadStream(is);
80        mDataStore.loadIfNeeded();
81        BrightnessConfiguration config = mDataStore.getBrightnessConfiguration(1 /*userSerial*/);
82        Pair<float[], float[]> curve = config.getCurve();
83        float[] expectedLux = { 0f, 25f };
84        float[] expectedNits = { 13.25f, 35.94f };
85        assertArrayEquals(expectedLux, curve.first, "lux");
86        assertArrayEquals(expectedNits, curve.second, "nits");
87        assertEquals("something", config.getDescription());
88
89        config = mDataStore.getBrightnessConfiguration(3 /*userSerial*/);
90        curve = config.getCurve();
91        expectedLux = new float[] { 0f, 10.2f };
92        expectedNits = new float[] { 13.25f, 15f };
93        assertArrayEquals(expectedLux, curve.first, "lux");
94        assertArrayEquals(expectedNits, curve.second, "nits");
95        assertNull(config.getDescription());
96    }
97
98    @Test
99    public void testBrightnessConfigWithInvalidCurveIsIgnored() {
100        String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
101                + "<display-manager-state>\n"
102                + "  <brightness-configurations>\n"
103                + "    <brightness-configuration user-serial=\"0\">\n"
104                + "      <brightness-curve>\n"
105                + "        <brightness-point lux=\"1\" nits=\"13.25\"/>\n"
106                + "        <brightness-point lux=\"25\" nits=\"35.94\"/>\n"
107                + "      </brightness-curve>\n"
108                + "    </brightness-configuration>\n"
109                + "  </brightness-configurations>\n"
110                + "</display-manager-state>\n";
111        InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
112        mInjector.setReadStream(is);
113        mDataStore.loadIfNeeded();
114        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
115    }
116
117    @Test
118    public void testBrightnessConfigWithInvalidFloatsIsIgnored() {
119        String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
120                + "<display-manager-state>\n"
121                + "  <brightness-configurations>\n"
122                + "    <brightness-configuration user-serial=\"0\">\n"
123                + "      <brightness-curve>\n"
124                + "        <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
125                + "        <brightness-point lux=\"0xFF\" nits=\"foo\"/>\n"
126                + "      </brightness-curve>\n"
127                + "    </brightness-configuration>\n"
128                + "  </brightness-configurations>\n"
129                + "</display-manager-state>\n";
130        InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
131        mInjector.setReadStream(is);
132        mDataStore.loadIfNeeded();
133        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
134    }
135
136    @Test
137    public void testEmptyBrightnessConfigurationsDoesntCrash() {
138        String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
139                + "<display-manager-state>\n"
140                + "  <brightness-configurations />\n"
141                + "</display-manager-state>\n";
142        InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
143        mInjector.setReadStream(is);
144        mDataStore.loadIfNeeded();
145        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
146    }
147
148    @Test
149    public void testStoreAndReloadOfBrightnessConfigurations() {
150        final float[] lux = { 0f, 10f };
151        final float[] nits = {1f, 100f };
152        final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
153                .setDescription("a description")
154                .build();
155        mDataStore.loadIfNeeded();
156        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
157        mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename");
158
159        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
160        mInjector.setWriteStream(baos);
161        mDataStore.saveIfNeeded();
162        assertTrue(mInjector.wasWriteSuccessful());
163
164        TestInjector newInjector = new TestInjector();
165        PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
166        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
167        newInjector.setReadStream(bais);
168        newDataStore.loadIfNeeded();
169        assertNotNull(newDataStore.getBrightnessConfiguration(0 /*userSerial*/));
170        assertEquals(mDataStore.getBrightnessConfiguration(0 /*userSerial*/),
171                newDataStore.getBrightnessConfiguration(0 /*userSerial*/));
172    }
173
174    @Test
175    public void testNullBrightnessConfiguration() {
176        final float[] lux = { 0f, 10f };
177        final float[] nits = {1f, 100f };
178        final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
179                .setDescription("a description")
180                .build();
181        mDataStore.loadIfNeeded();
182        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
183
184        mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename");
185        assertNotNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
186
187        mDataStore.setBrightnessConfigurationForUser(null, 0, "packagename");
188        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
189    }
190
191    public class TestInjector extends PersistentDataStore.Injector {
192        private InputStream mReadStream;
193        private OutputStream mWriteStream;
194
195        private boolean mWasSuccessful;
196
197        @Override
198        public InputStream openRead() throws FileNotFoundException {
199            if (mReadStream != null) {
200                return mReadStream;
201            } else {
202                throw new FileNotFoundException();
203            }
204        }
205
206        @Override
207        public OutputStream startWrite() {
208            return mWriteStream;
209        }
210
211        @Override
212        public void finishWrite(OutputStream os, boolean success) {
213            mWasSuccessful = success;
214            try {
215                os.close();
216            } catch (IOException e) {
217                // This method can't throw IOException since the super implementation doesn't, so
218                // we just wrap it in a RuntimeException so we end up crashing the test all the
219                // same.
220                throw new RuntimeException(e);
221            }
222        }
223
224        public void setReadStream(InputStream is) {
225            mReadStream = is;
226        }
227
228        public void setWriteStream(OutputStream os) {
229            mWriteStream = os;
230        }
231
232        public boolean wasWriteSuccessful() {
233            return mWasSuccessful;
234        }
235    }
236
237    private static void assertArrayEquals(float[] expected, float[] actual, String name) {
238        assertEquals("Expected " + name + " arrays to be the same length!",
239                expected.length, actual.length);
240        for (int i = 0; i < expected.length; i++) {
241            assertEquals("Expected " + name + " arrays to be equivalent when value " + i
242                    + "differs", expected[i], actual[i], 0.01 /*tolerance*/);
243        }
244    }
245}
246