/* * Copyright 2017 Google Inc. * * 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.google.firebase.database; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.firebase.database.snapshot.EmptyNode; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.junit.Test; public class ObjectMapTest { private MutableData emptyData() { return new MutableData(EmptyNode.Empty()); } @Test public void basicObjectMapping() throws IOException { MutableData data = emptyData(); Author author = new Author("Greg", 3); Message message = new Message("hello world", author); data.setValue(message); Message result = data.getValue(Message.class); assertEquals(message, result); try { data.getValue(Author.class); fail("Should throw"); } catch (DatabaseException e) { // No-op, expected to throw } } @Test public void nativeToObject() { MutableData data = emptyData(); Map<String, Object> toSet = new MapBuilder() .put("text", "hello world") .put("author", new MapBuilder().put("name", "Greg").put("id", 3).build()) .build(); data.setValue(toSet); Message m = data.getValue(Message.class); assertEquals("hello world", m.getText()); Author a = m.getAuthor(); assertEquals("Greg", a.getName()); assertEquals(3, a.getId()); // An extra field should cause the deserializer to throw data.child("foo").setValue("bar"); try { m = data.getValue(Message.class); fail("Should throw"); } catch (DatabaseException e) { // No-op, expected to throw } } @Test public void objectToNative() { MutableData data = emptyData(); Author a = new Author("Greg", 3); Message m = new Message("hello world", a); data.setValue(m); @SuppressWarnings("unchecked") Map<String, Object> result = (Map<String, Object>) data.getValue(); Map<String, Object> expected = new MapBuilder() .put("text", "hello world") .put("author", new MapBuilder().put("name", "Greg").put("id", 3L).build()) .build(); TestHelpers.assertDeepEquals(expected, result); } @Test public void nullsWork() { MutableData data = emptyData(); Message m = new Message("hello world", null); data.setValue(m); // Author is gone, equivalent to setting child("author") to null @SuppressWarnings("unchecked") Map<String, Object> result = (Map<String, Object>) data.getValue(); Map<String, Object> expected = new MapBuilder().put("text", "hello world").build(); TestHelpers.assertDeepEquals(expected, result); Message resultMessage = data.getValue(Message.class); assertEquals(m, resultMessage); } @Test public void numbersCanBeConverted() { MutableData data = emptyData(); data.setValue(3); Object result = data.getValue(); assertTrue(result instanceof Long); result = data.getValue(Integer.class); assertTrue(result != null); } @Test public void basicGenerics() { List<Author> authors = new ArrayList<>(); authors.addAll( Arrays.asList(new Author("Greg", 3), new Author("Vikrum", 4), new Author("Michael", 5))); MutableData data = emptyData(); data.setValue(authors); GenericTypeIndicator<List<Author>> t = new GenericTypeIndicator<List<Author>>() {}; List<Author> result = data.getValue(t); TestHelpers.assertDeepEquals(authors, result); } @Test public void nestedGenerics() throws IOException { MutableData data = emptyData(); Author author = new Author("Greg", 3); List<Message> messages = new ArrayList<>(); messages.add(new Message("hello world", author)); messages.add(new Message("foo bar", author)); AuthorMessages am = new AuthorMessages(author, messages); data.setValue(am); AuthorMessages result = data.getValue(AuthorMessages.class); assertEquals(am.getAuthor(), result.getAuthor()); TestHelpers.assertDeepEquals(am.getMessages(), result.getMessages()); } @Test public void incompleteObject() { MutableData data = emptyData(); Incomplete i = new Incomplete(1, 2); data.setValue(i); i = data.getValue(Incomplete.class); assertEquals(1, i.getFoo()); // bar doesn't get serialized, its getter isn't public assertEquals(0, i.getBar()); } @Test public void intsAndLongs() { MutableData data = emptyData(); // Verify that jackson does the right thing when it encounters a number too large for an int data.setValue(new MapBuilder().put("foo", (long) Integer.MAX_VALUE + 1).build()); Map<String, Object> output = data.getValue(new GenericTypeIndicator<Map<String, Object>>() {}); Object value = output.get("foo"); assertEquals(Long.class, value.getClass()); // Verify that our default is Long data.setValue(3); value = data.getValue(); assertEquals(Long.class, value.getClass()); } @ThrowOnExtraProperties private static class Author { private String name; private int id; private Author() {} private Author(String name, int id) { this.name = name; this.id = id; } public String getName() { return name; } public int getId() { return id; } @Override public String toString() { return "Name: " + name + " id: " + id; } @Override public boolean equals(Object o) { return (o instanceof Author) && ((Author) o).name.equals(name) && ((Author) o).id == id; } @Override public int hashCode() { return id << 2 | name.hashCode(); } } @ThrowOnExtraProperties private static class Message { private String text; private Author author; private Message() {} private Message(String text, Author author) { this.text = text; this.author = author; } public String getText() { return text; } public Author getAuthor() { return author; } @Override public String toString() { return "Message: " + text + " Author: " + author.toString(); } @Override public boolean equals(Object o) { return (o instanceof Message) && ((Message) o).text.equals(text) && (((((Message) o).author == null) && (author == null)) || ((Message) o).author.equals(author)); } @Override public int hashCode() { return text.hashCode() << 2 | author.hashCode(); } } private static class AuthorMessages { private Author author; private List<Message> messages; private AuthorMessages() {} private AuthorMessages(Author author, List<Message> messages) { this.author = author; this.messages = messages; } public Author getAuthor() { return author; } public List<Message> getMessages() { return messages; } @Override public String toString() { return "Author: " + author + " messages: " + messages; } } private static class Incomplete { private int foo; private int bar; private Incomplete() {} private Incomplete(int foo, int bar) { this.foo = foo; this.bar = bar; } public int getFoo() { return foo; } int getBar() { return bar; } } }