// // C4MutableFleeceTest.java // // Copyright (c) 2017 Couchbase, Inc All rights reserved. // // 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.couchbase.lite.internal.core; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.couchbase.lite.CouchbaseLiteException; import com.couchbase.lite.LiteCoreException; import com.couchbase.lite.internal.fleece.AllocSlice; import com.couchbase.lite.internal.fleece.FLEncoder; import com.couchbase.lite.internal.fleece.FLValue; import com.couchbase.lite.internal.fleece.FleeceDict; import com.couchbase.lite.internal.fleece.FleeceDocument; import com.couchbase.lite.internal.fleece.MRoot; import com.couchbase.lite.internal.fleece.MValue; import com.couchbase.lite.internal.fleece.MValueDelegate; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class C4MutableFleeceTest extends C4BaseTest { private MValue.Delegate delegate; @Before @Override public void setUp() throws CouchbaseLiteException { super.setUp(); delegate = MValue.getRegisteredDelegate(); MValue.registerDelegate(new MValueDelegate()); } @After @Override public void tearDown() { try { if (delegate != null) { MValue.registerDelegate(delegate); } } finally { super.tearDown(); } } static AllocSlice encode(Object obj) throws LiteCoreException { FLEncoder enc = new FLEncoder(); try { enc.writeValue(obj); return enc.finish2(); } finally { enc.free(); } } static AllocSlice encode(MRoot root) throws LiteCoreException { FLEncoder enc = new FLEncoder(); try { root.encodeTo(enc); return enc.finish2(); } finally { enc.free(); } } static List<String> sortedKeys(Map<String, Object> dict) { Set<String> keys = dict.keySet(); ArrayList<String> list = new ArrayList<>(keys); Collections.sort(list); return list; } static void verifyDictIterator(Map<String, Object> dict) { int count = 0; Set<String> keys = new HashSet<>(); for (String key : dict.keySet()) { count++; assertNotNull(key); keys.add(key); } assertEquals(dict.size(), keys.size()); assertEquals(dict.size(), count); } static String fleece2JSON(AllocSlice fleece) { try { FLValue v = FLValue.fromData(fleece); if (v == null) { return "INVALID_FLEECE"; } return v.toJSON5(); } finally { if (fleece != null) { fleece.free(); } } } // TEST_CASE("MValue", "[Mutable]") @Test public void testMValue() { MValue val = new MValue("hi"); assertEquals("hi", val.asNative(null)); assertNull(val.getValue()); } // TEST_CASE("MDict", "[Mutable]") @Test public void testMDict() throws LiteCoreException { Map<String, Object> map = new HashMap<>(); map.put("greeting", "hi"); map.put("array", Arrays.asList("boo", false)); Map<String, Object> subMap = new HashMap<>(); subMap.put("melt", 32); subMap.put("boil", 212); map.put("dict", subMap); AllocSlice data = encode(map); try { MRoot root = new MRoot(data); assertFalse(root.isMutated()); Object obj = root.asNative(); assertNotNull(obj); assertTrue(obj instanceof Map); Map<String, Object> dict = (Map<String, Object>) obj; assertNotNull(dict); assertEquals(3, dict.size()); assertTrue(dict.containsKey("greeting")); assertFalse(dict.containsKey("x")); assertEquals(Arrays.asList("array", "dict", "greeting"), sortedKeys(dict)); assertEquals("hi", dict.get("greeting")); assertNull(dict.get("x")); obj = dict.get("dict"); assertNotNull(obj); assertTrue(obj instanceof Map); Map<String, Object> nested = (Map<String, Object>) obj; assertEquals(sortedKeys(nested), Arrays.asList("boil", "melt")); Map<String, Object> expected = new HashMap<>(); expected.put("melt", 32L); expected.put("boil", 212L); assertEquals(expected, nested); assertEquals(32L, nested.get("melt")); assertEquals(212L, nested.get("boil")); assertNull(nested.get("freeze")); assertFalse(root.isMutated()); verifyDictIterator(dict); nested.put("freeze", Arrays.asList(32L, "Fahrenheit")); assertTrue(root.isMutated()); assertEquals(32L, nested.remove("melt")); expected.clear(); expected.put("freeze", Arrays.asList(32L, "Fahrenheit")); expected.put("boil", 212L); assertEquals(expected, nested); verifyDictIterator(dict); assertEquals( "{array:[\"boo\",false],dict:{boil:212,freeze:[32,\"Fahrenheit\"]},greeting:\"hi\"}", fleece2JSON(encode(dict))); assertEquals( "{array:[\"boo\",false],dict:{boil:212,freeze:[32,\"Fahrenheit\"]},greeting:\"hi\"}", fleece2JSON(encode(root))); } finally { if (data != null) { data.free(); } } } // TEST_CASE("MArray", "[Mutable]") @Test public void testMArray() throws LiteCoreException { List<Object> list = Arrays.asList("hi", Arrays.asList("boo", false), 42); AllocSlice data = encode(list); try { MRoot root = new MRoot(data); assertFalse(root.isMutated()); Object obj = root.asNative(); assertNotNull(obj); assertTrue(obj instanceof List); List<Object> array = (List<Object>) obj; assertNotNull(array); assertEquals(3, array.size()); assertEquals("hi", array.get(0)); assertEquals(42L, array.get(2)); assertNotNull(array.get(1)); obj = array.get(1); assertTrue(obj instanceof List); assertEquals(Arrays.asList("boo", false), obj); array.set(0, Arrays.asList(3.14, 2.17)); array.add(2, "NEW"); assertEquals(Arrays.asList(3.14, 2.17), array.get(0)); assertEquals(Arrays.asList("boo", false), array.get(1)); assertEquals("NEW", array.get(2)); assertEquals(42L, array.get(3)); assertEquals(4, array.size()); List<Object> expected = new ArrayList<>(); expected.add(Arrays.asList(3.14, 2.17)); expected.add(Arrays.asList("boo", false)); expected.add("NEW"); expected.add(42L); assertEquals(expected, array); obj = array.get(1); assertNotNull(obj); List<Object> nested = (List<Object>) obj; nested.set(1, true); assertEquals( "[[3.14,2.17],[\"boo\",true],\"NEW\",42]", fleece2JSON(encode(array))); assertEquals( "[[3.14,2.17],[\"boo\",true],\"NEW\",42]", fleece2JSON(encode(root))); } finally { if (data != null) { data.free(); } } } // TEST_CASE("MArray iteration", "[Mutable]") @Test public void testMArrayIteration() throws LiteCoreException { List<Object> orig = new ArrayList<>(); for (int i = 0; i < 100; i++) { orig.add(String.format(Locale.ENGLISH, "This is item number %d", i)); } AllocSlice data = encode(orig); try { MRoot root = new MRoot(data); List<Object> array = (List<Object>) root.asNative(); int i = 0; for (Object o : array) { assertEquals(orig.get(i), o); i++; } } finally { if (data != null) { data.free(); } } } // TEST_CASE("MDict no root", "[Mutable]") @Test public void testMDictNoRoot() throws LiteCoreException { Map<String, Object> subMap = new HashMap<>(); subMap.put("melt", 32); subMap.put("boil", 212); Map<String, Object> map = new HashMap<>(); map.put("greeting", "hi"); map.put("array", Arrays.asList("boo", false)); map.put("dict", subMap); AllocSlice data = encode(map); try { Object obj = FleeceDocument.getObject(data, true); assertNotNull(obj); assertTrue(obj instanceof Map); Map<String, Object> dict = (Map<String, Object>) obj; assertFalse(((FleeceDict) dict).isMutated()); assertEquals(Arrays.asList("array", "dict", "greeting"), sortedKeys(dict)); assertEquals("hi", dict.get("greeting")); assertNull(dict.get("x")); verifyDictIterator(dict); obj = dict.get("dict"); assertNotNull(obj); assertTrue(obj instanceof Map); Map<String, Object> nested = (Map<String, Object>) obj; assertEquals(sortedKeys(nested), Arrays.asList("boil", "melt")); Map<String, Object> expected = new HashMap<>(); expected.put("melt", 32L); expected.put("boil", 212L); assertEquals(expected, nested); assertEquals(32L, nested.get("melt")); assertEquals(212L, nested.get("boil")); assertNull(nested.get("freeze")); verifyDictIterator(nested); assertFalse(((FleeceDict) nested).isMutated()); assertFalse(((FleeceDict) dict).isMutated()); nested.put("freeze", Arrays.asList(32L, "Fahrenheit")); assertTrue(((FleeceDict) nested).isMutated()); assertTrue(((FleeceDict) dict).isMutated()); assertEquals(32L, nested.remove("melt")); expected.clear(); expected.put("freeze", Arrays.asList(32L, "Fahrenheit")); expected.put("boil", 212L); assertEquals(expected, nested); verifyDictIterator(nested); verifyDictIterator(dict); assertEquals( "{array:[\"boo\",false],dict:{boil:212,freeze:[32,\"Fahrenheit\"]},greeting:\"hi\"}", fleece2JSON(encode(dict))); } finally { if (data != null) { data.free(); } } } // TEST_CASE("Adding mutable collections", "[Mutable]") @Test public void testAddingMutableCollections() throws LiteCoreException { Map<String, Object> subMap = new HashMap<>(); subMap.put("melt", 32); subMap.put("boil", 212); Map<String, Object> map = new HashMap<>(); map.put("greeting", "hi"); map.put("array", Arrays.asList("boo", false)); map.put("dict", subMap); AllocSlice data = encode(map); try { MRoot root = new MRoot(data); assertFalse(root.isMutated()); Object obj = root.asNative(); assertNotNull(obj); assertTrue(obj instanceof Map); Map<String, Object> dict = (Map<String, Object>) obj; obj = dict.get("array"); assertTrue(obj instanceof List); List<Object> array = (List<Object>) obj; dict.put("new", array); array.add(true); assertEquals( "{array:[\"boo\",false,true],dict:{boil:212,melt:32},greeting:\"hi\",new:[\"boo\",false,true]}", fleece2JSON(encode(root))); assertEquals( "{array:[\"boo\",false,true],dict:{boil:212,melt:32},greeting:\"hi\",new:[\"boo\",false,true]}", fleece2JSON(root.encode())); } finally { if (data != null) { data.free(); } } } @Test public void testMRoot() throws LiteCoreException { Map<String, Object> subMap = new HashMap<>(); subMap.put("melt", 32L); subMap.put("boil", 212L); Map<String, Object> map = new HashMap<>(); map.put("greeting", "hi"); map.put("array", Arrays.asList("boo", false)); map.put("dict", subMap); AllocSlice data = encode(map); try { MRoot root = new MRoot(data); assertFalse(root.isMutated()); Object obj = root.asNative(); assertNotNull(obj); assertTrue(obj instanceof Map); Map<String, Object> dict = (Map<String, Object>) obj; assertEquals("hi", dict.get("greeting")); assertEquals(Arrays.asList("boo", false), dict.get("array")); assertEquals(subMap, dict.get("dict")); assertEquals("{array:[\"boo\",false],dict:{boil:212,melt:32},greeting:\"hi\"}", fleece2JSON(root.encode())); List<Object> array = (List<Object>) dict.get("array"); dict.put("new", array); array.add(true); assertEquals( "{array:[\"boo\",false,true],dict:{boil:212,melt:32},greeting:\"hi\",new:[\"boo\",false,true]}", fleece2JSON(root.encode())); } finally { if (data != null) { data.free(); } } } @Test public void testMRoot2() throws LiteCoreException { Map<String, Object> map = new HashMap<>(); map.put("greeting", "hi"); AllocSlice data = encode(map); try { MRoot root = new MRoot(data); assertFalse(root.isMutated()); Object obj = root.asNative(); assertNotNull(obj); assertTrue(obj instanceof Map); Map<String, Object> dict = (Map<String, Object>) obj; assertEquals("hi", dict.get("greeting")); assertEquals("{greeting:\"hi\"}", fleece2JSON(root.encode())); assertEquals("{greeting:\"hi\"}", fleece2JSON(encode(root))); dict.put("hello", "world"); assertEquals("hi", dict.get("greeting")); assertEquals("world", dict.get("hello")); assertEquals("{greeting:\"hi\",hello:\"world\"}", fleece2JSON(encode(dict))); assertEquals("{greeting:\"hi\",hello:\"world\"}", fleece2JSON(encode(root.asNative()))); assertEquals("{greeting:\"hi\",hello:\"world\"}", fleece2JSON(root.encode())); assertEquals("{greeting:\"hi\",hello:\"world\"}", fleece2JSON(encode(root))); } finally { if (data != null) { data.free(); } } } }