bobv@google.com | a29cb15 | 2011-04-08 14:17:10 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2011 Google Inc. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 5 | * use this file except in compliance with the License. You may obtain a copy of |
| 6 | * 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, WITHOUT |
| 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | * License for the specific language governing permissions and limitations under |
| 14 | * the License. |
| 15 | */ |
bobv@google.com | b0068d7 | 2011-04-18 09:42:06 +0000 | [diff] [blame^] | 16 | package com.google.web.bindery.autobean.shared; |
bobv@google.com | a29cb15 | 2011-04-08 14:17:10 +0000 | [diff] [blame] | 17 | |
bobv@google.com | b0068d7 | 2011-04-18 09:42:06 +0000 | [diff] [blame^] | 18 | import com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable; |
| 19 | import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl; |
| 20 | import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder; |
| 21 | import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; |
| 22 | import com.google.web.bindery.autobean.shared.impl.SplittableList; |
| 23 | import com.google.web.bindery.autobean.shared.impl.SplittableSimpleMap; |
| 24 | import com.google.web.bindery.autobean.shared.impl.StringQuoter; |
bobv@google.com | a29cb15 | 2011-04-08 14:17:10 +0000 | [diff] [blame] | 25 | import com.google.gwt.core.client.GWT; |
| 26 | import com.google.gwt.junit.client.GWTTestCase; |
| 27 | |
| 28 | import java.util.Arrays; |
| 29 | import java.util.HashSet; |
| 30 | import java.util.List; |
| 31 | import java.util.Map; |
| 32 | |
| 33 | /** |
| 34 | * Tests for the underlying Splittable implementation. This test class is not |
| 35 | * indicative of code that users would write, it's simply doing spot-checks of |
| 36 | * functionality that AbstractAutoBean depends on. |
| 37 | */ |
| 38 | public class SplittableTest extends GWTTestCase { |
| 39 | |
| 40 | /** |
| 41 | * |
| 42 | */ |
| 43 | private static final EncodeState testState = EncodeState.forTesting(); |
| 44 | |
| 45 | @Override |
| 46 | public String getModuleName() { |
bobv@google.com | b0068d7 | 2011-04-18 09:42:06 +0000 | [diff] [blame^] | 47 | return "com.google.web.bindery.autobean.AutoBean"; |
bobv@google.com | a29cb15 | 2011-04-08 14:17:10 +0000 | [diff] [blame] | 48 | } |
| 49 | |
| 50 | public void testBasicProperties() { |
| 51 | Splittable data = StringQuoter.split("{\"a\":true, \"b\":3, \"c\":\"string\", \"d\":null}"); |
| 52 | assertTrue("isBoolean", data.get("a").isBoolean()); |
| 53 | assertTrue("asBoolean", data.get("a").asBoolean()); |
| 54 | assertTrue("isNumber", data.get("b").isNumber()); |
| 55 | assertEquals(3.0, data.get("b").asNumber()); |
| 56 | assertTrue("isString", data.get("c").isString()); |
| 57 | assertEquals("string", data.get("c").asString()); |
| 58 | assertTrue("isNull", data.isNull("d")); |
| 59 | assertNull("should be null", data.get("d")); |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Ensure that hashcodes don't leak into the payload. |
| 64 | */ |
| 65 | public void testHashCode() { |
| 66 | Splittable data = StringQuoter.split("{\"a\":\"b\"}"); |
| 67 | int hash = data.hashCode(); |
| 68 | String payload = data.getPayload(); |
| 69 | assertFalse(payload, payload.contains("$H")); |
| 70 | assertFalse(payload, payload.contains(String.valueOf(hash))); |
| 71 | assertEquals(hash, data.hashCode()); |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Splittables are implemented by a couple of different concrete types. We'll |
| 76 | * use this method to make sure that the correct implementation type is being |
| 77 | * used in various circumstances. |
| 78 | */ |
| 79 | public void testImplementationChoice() { |
| 80 | Splittable s = StringQuoter.split("[1,false,\"true\"]"); |
| 81 | if (GWT.isScript()) { |
| 82 | assertTrue("s should be JsoSplittable", s instanceof JsoSplittable); |
| 83 | assertTrue("s[0] should be JsoSplittable", s.get(0) instanceof JsoSplittable); |
| 84 | assertTrue("s[1] should be JsoSplittable", s.get(1) instanceof JsoSplittable); |
| 85 | assertTrue("s[2] should be JsoSplittable", s.get(2) instanceof JsoSplittable); |
| 86 | } else { |
| 87 | // Using the same types in both pure-JRE and DevMode to avoid JSNI |
| 88 | // overhead |
| 89 | assertTrue("s should be JsonSplittable", s.getClass().getName().endsWith("JsonSplittable")); |
| 90 | assertTrue("s[0] should be JsonSplittable", s.get(0).getClass().getName().endsWith( |
| 91 | "JsonSplittable")); |
| 92 | assertTrue("s[1] should be JsonSplittable", s.get(1).getClass().getName().endsWith( |
| 93 | "JsonSplittable")); |
| 94 | assertTrue("s[2] should be JsonSplittable", s.get(2).getClass().getName().endsWith( |
| 95 | "JsonSplittable")); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | public void testIndexed() { |
| 100 | Splittable s = StringQuoter.createIndexed(); |
| 101 | assertTrue(s.isIndexed()); |
| 102 | assertFalse(s.isKeyed()); |
| 103 | assertFalse(s.isString()); |
| 104 | assertEquals(0, s.size()); |
| 105 | |
| 106 | string("foo").assign(s, 0); |
| 107 | string("bar").assign(s, 1); |
| 108 | string("baz").assign(s, 2); |
| 109 | |
| 110 | assertEquals(3, s.size()); |
| 111 | assertEquals("[\"foo\",\"bar\",\"baz\"]", s.getPayload()); |
| 112 | |
| 113 | string("quux").assign(s, 1); |
| 114 | assertEquals("[\"foo\",\"quux\",\"baz\"]", s.getPayload()); |
| 115 | |
| 116 | Splittable s2 = s.deepCopy(); |
| 117 | assertNotSame(s, s2); |
| 118 | assertEquals(s.size(), s2.size()); |
| 119 | for (int i = 0, j = s.size(); i < j; i++) { |
| 120 | assertEquals(s.get(i).asString(), s2.get(i).asString()); |
| 121 | } |
| 122 | |
| 123 | s.setSize(2); |
| 124 | assertEquals(2, s.size()); |
| 125 | assertEquals("[\"foo\",\"quux\"]", s.getPayload()); |
| 126 | |
| 127 | // Make sure reified values aren't in the payload |
| 128 | Object o = new Object(); |
| 129 | s.setReified("reified", o); |
| 130 | assertFalse(s.getPayload().contains("reified")); |
| 131 | assertFalse(s.getPayload().contains("__s")); |
| 132 | assertSame(o, s.getReified("reified")); |
| 133 | } |
| 134 | |
| 135 | public void testKeyed() { |
| 136 | Splittable s = StringQuoter.createSplittable(); |
| 137 | assertFalse(s.isIndexed()); |
| 138 | assertTrue(s.isKeyed()); |
| 139 | assertFalse(s.isString()); |
| 140 | assertTrue(s.getPropertyKeys().isEmpty()); |
| 141 | |
| 142 | string("bar").assign(s, "foo"); |
| 143 | string("quux").assign(s, "baz"); |
| 144 | |
| 145 | // Actual iteration order is undefined |
| 146 | assertEquals(new HashSet<String>(Arrays.asList("foo", "baz")), new HashSet<String>(s |
| 147 | .getPropertyKeys())); |
| 148 | |
| 149 | assertFalse(s.isNull("foo")); |
| 150 | assertTrue(s.isNull("bar")); |
| 151 | |
| 152 | assertEquals("bar", s.get("foo").asString()); |
| 153 | assertEquals("quux", s.get("baz").asString()); |
| 154 | |
| 155 | String payload = s.getPayload(); |
| 156 | assertTrue(payload.startsWith("{")); |
| 157 | assertTrue(payload.endsWith("}")); |
| 158 | assertTrue(payload.contains("\"foo\":\"bar\"")); |
| 159 | assertTrue(payload.contains("\"baz\":\"quux\"")); |
| 160 | |
| 161 | Splittable s2 = s.deepCopy(); |
| 162 | assertNotSame(s, s2); |
| 163 | assertEquals("bar", s2.get("foo").asString()); |
| 164 | assertEquals("quux", s2.get("baz").asString()); |
| 165 | |
| 166 | // Make sure reified values aren't in the payload |
| 167 | Object o = new Object(); |
| 168 | s.setReified("reified", o); |
| 169 | assertFalse("Should not see reified in " + s.getPayload(), s.getPayload().contains("reified")); |
| 170 | assertFalse("Should not see __s in " + s.getPayload(), s.getPayload().contains("__s")); |
| 171 | assertSame(o, s.getReified("reified")); |
| 172 | } |
| 173 | |
| 174 | public void testNested() { |
| 175 | Splittable s = StringQuoter.split("{\"a\":{\"foo\":\"bar\"}}"); |
| 176 | Splittable a = s.get("a"); |
| 177 | assertNotNull(a); |
| 178 | assertEquals("bar", a.get("foo").asString()); |
| 179 | assertSame(a, s.get("a")); |
| 180 | assertEquals(a, s.get("a")); |
| 181 | } |
| 182 | |
| 183 | /** |
| 184 | * Tests attributes of the {@link Splittable#NULL} field. |
| 185 | */ |
| 186 | public void testNull() { |
| 187 | Splittable n = Splittable.NULL; |
| 188 | if (GWT.isScript()) { |
| 189 | assertNull(n); |
| 190 | } else { |
| 191 | assertNotNull(n); |
| 192 | } |
| 193 | assertFalse("boolean", n.isBoolean()); |
| 194 | assertFalse("indexed", n.isIndexed()); |
| 195 | assertFalse("keyed", n.isKeyed()); |
| 196 | assertFalse("string", n.isString()); |
| 197 | assertEquals("null", n.getPayload()); |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * Extra tests in here due to potential to confuse {@code false} and |
| 202 | * {@code null} values. |
| 203 | */ |
| 204 | public void testSplittableListBoolean() { |
| 205 | Coder boolCoder = AutoBeanCodexImpl.valueCoder(Boolean.class); |
| 206 | Splittable s = StringQuoter.createIndexed(); |
| 207 | bool(false).assign(s, 0); |
| 208 | assertFalse("0 should not be null", s.isNull(0)); |
| 209 | assertTrue("s[0] should be a boolean", s.get(0).isBoolean()); |
| 210 | assertFalse("s[0] should be false", s.get(0).asBoolean()); |
| 211 | assertNotNull("Null decode", ValueCodex.decode(Boolean.class, s.get(0))); |
| 212 | Object decodedBoolean = boolCoder.decode(testState, s.get(0)); |
| 213 | assertNotNull("decode should not return null", decodedBoolean); |
| 214 | assertFalse("decoded value should be false", (Boolean) decodedBoolean); |
| 215 | |
| 216 | bool(true).assign(s, 1); |
| 217 | assertTrue("s[1] should be a boolean", s.get(1).isBoolean()); |
| 218 | assertTrue("s[1] should be true", s.get(1).asBoolean()); |
| 219 | assertTrue("boolCoder 1", (Boolean) boolCoder.decode(testState, s.get(1))); |
| 220 | |
| 221 | Splittable.NULL.assign(s, 2); |
| 222 | assertTrue("3 should be null", s.isNull(3)); |
| 223 | assertEquals("payload", "[false,true,null]", s.getPayload()); |
| 224 | List<Boolean> boolList = new SplittableList<Boolean>(s, boolCoder, testState); |
| 225 | assertEquals("boolList", Arrays.<Boolean> asList(false, true, null), boolList); |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Extra tests in here due to potential to confuse 0 and {@code null} values. |
| 230 | */ |
| 231 | public void testSplittableListNumbers() { |
| 232 | Coder intCoder = AutoBeanCodexImpl.valueCoder(Integer.class); |
| 233 | Coder doubleCoder = AutoBeanCodexImpl.valueCoder(Double.class); |
| 234 | Splittable s = StringQuoter.createIndexed(); |
| 235 | number(0).assign(s, 0); |
| 236 | assertFalse("0 should not be null", s.isNull(0)); |
| 237 | assertTrue("s[0] should be a number", s.get(0).isNumber()); |
| 238 | assertNotNull("Null decode", ValueCodex.decode(Integer.class, s.get(0))); |
| 239 | Object decodedInt = intCoder.decode(testState, s.get(0)); |
| 240 | assertNotNull("decode should not return null", decodedInt); |
| 241 | assertEquals("intCoder 0", Integer.valueOf(0), decodedInt); |
| 242 | assertEquals("doubleCoder 0", Double.valueOf(0), doubleCoder.decode(testState, s.get(0))); |
| 243 | |
| 244 | number(3.141592).assign(s, 1); |
| 245 | assertEquals("intCoder 1", Integer.valueOf(3), intCoder.decode(testState, s.get(1))); |
| 246 | assertEquals("doubleCoder 1", Double.valueOf(3.141592), doubleCoder.decode(testState, s.get(1))); |
| 247 | |
| 248 | number(42).assign(s, 2); |
| 249 | Splittable.NULL.assign(s, 3); |
| 250 | assertTrue("3 should be null", s.isNull(3)); |
| 251 | assertEquals("payload", "[0,3.141592,42,null]", s.getPayload()); |
| 252 | List<Double> doubleList = new SplittableList<Double>(s, doubleCoder, testState); |
| 253 | assertEquals(Double.valueOf(0), doubleList.get(0)); |
| 254 | assertEquals("doubleList", Arrays.<Double> asList(0d, 3.141592, 42d, null), doubleList); |
| 255 | |
| 256 | // Don't share backing data between lists |
| 257 | s = StringQuoter.split("[0,3.141592,42,null]"); |
| 258 | List<Integer> intList = new SplittableList<Integer>(s, intCoder, testState); |
| 259 | assertEquals("intList", Arrays.<Integer> asList(0, 3, 42, null), intList); |
| 260 | } |
| 261 | |
| 262 | public void testSplittableListString() { |
| 263 | Splittable data = StringQuoter.split("[\"Hello\",\"World\"]"); |
| 264 | SplittableList<String> list = |
| 265 | new SplittableList<String>(data, AutoBeanCodexImpl.valueCoder(String.class), testState); |
| 266 | assertEquals(2, list.size()); |
| 267 | assertEquals(Arrays.asList("Hello", "World"), list); |
| 268 | list.set(0, "Goodbye"); |
| 269 | assertEquals(Arrays.asList("Goodbye", "World"), list); |
| 270 | assertEquals("[\"Goodbye\",\"World\"]", data.getPayload()); |
| 271 | list.remove(0); |
| 272 | assertEquals(Arrays.asList("World"), list); |
| 273 | assertEquals("[\"World\"]", data.getPayload()); |
| 274 | list.add("Wide"); |
| 275 | list.add("Web"); |
| 276 | assertEquals(Arrays.asList("World", "Wide", "Web"), list); |
| 277 | assertEquals("[\"World\",\"Wide\",\"Web\"]", data.getPayload()); |
| 278 | } |
| 279 | |
| 280 | public void testSplittableMapStringString() { |
| 281 | Splittable data = StringQuoter.split("{\"foo\":\"bar\",\"baz\":\"quux\",\"isNull\":null}"); |
| 282 | assertTrue("isNull should be null", data.isNull("isNull")); |
| 283 | assertFalse("isNull should not be undefined", data.isUndefined("isNull")); |
| 284 | Map<String, String> map = |
| 285 | new SplittableSimpleMap<String, String>(data, AutoBeanCodexImpl.valueCoder(String.class), |
| 286 | AutoBeanCodexImpl.valueCoder(String.class), testState); |
| 287 | assertEquals(3, map.size()); |
| 288 | assertEquals("bar", map.get("foo")); |
| 289 | assertEquals("quux", map.get("baz")); |
| 290 | assertTrue("Map should have isNull key", map.containsKey("isNull")); |
| 291 | assertNull(map.get("isNull")); |
| 292 | assertFalse("Map should not have unknown key", map.containsKey("unknown")); |
| 293 | |
| 294 | map.put("bar", "foo2"); |
| 295 | assertEquals("foo2", map.get("bar")); |
| 296 | String payload = data.getPayload(); |
| 297 | assertTrue(payload.contains("\"bar\":\"foo2\"")); |
| 298 | assertTrue(payload.contains("\"isNull\":null")); |
| 299 | } |
| 300 | |
| 301 | public void testString() { |
| 302 | Splittable s = string("Hello '\" World!"); |
| 303 | assertFalse(s.isIndexed()); |
| 304 | assertFalse(s.isKeyed()); |
| 305 | assertTrue(s.isString()); |
| 306 | assertEquals("Hello '\" World!", s.asString()); |
| 307 | assertEquals("\"Hello '\\\" World!\"", s.getPayload()); |
| 308 | } |
| 309 | |
| 310 | public void testStringEmpty() { |
| 311 | Splittable s = string(""); |
| 312 | assertFalse(s.isIndexed()); |
| 313 | assertFalse(s.isKeyed()); |
| 314 | assertTrue(s.isString()); |
| 315 | assertEquals("", s.asString()); |
| 316 | assertEquals("\"\"", s.getPayload()); |
| 317 | } |
| 318 | |
| 319 | private Splittable bool(boolean value) { |
| 320 | return StringQuoter.split(String.valueOf(value)); |
| 321 | } |
| 322 | |
| 323 | private Splittable number(double number) { |
| 324 | return StringQuoter.split(String.valueOf(number)); |
| 325 | } |
| 326 | |
| 327 | private Splittable string(String value) { |
| 328 | return StringQuoter.split(StringQuoter.quote(value)); |
| 329 | } |
| 330 | } |