blob: a981164da8435513bfefdb2100a0c97a06c91073 [file] [log] [blame]
/**
* HTML via Java(tm) Language Bindings
* Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. apidesign.org
* designates this particular file as subject to the
* "Classpath" exception as provided by apidesign.org
* in the License file that accompanied this code.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
*/
package net.java.html.json;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.apidesign.html.json.spi.ContextBuilder;
import org.apidesign.html.json.spi.FunctionBinding;
import org.apidesign.html.json.spi.PropertyBinding;
import org.apidesign.html.json.spi.Technology;
import static org.testng.Assert.*;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/**
*
* @author Jaroslav Tulach <jtulach@netbeans.org>
*/
@Model(className = "Modelik", properties = {
@Property(name = "value", type = int.class),
@Property(name = "count", type = int.class),
@Property(name = "unrelated", type = long.class),
@Property(name = "names", type = String.class, array = true),
@Property(name = "values", type = int.class, array = true),
@Property(name = "people", type = Person.class, array = true),
@Property(name = "changedProperty", type=String.class)
})
public class ModelTest {
private MockTechnology my;
private Modelik model;
private static Modelik leakedModel;
@BeforeMethod
public void createModel() {
my = new MockTechnology();
model = new Modelik(ContextBuilder.create().withTechnology(my).build());
}
@Test public void classGeneratedWithSetterGetter() {
model.setValue(10);
assertEquals(10, model.getValue(), "Value changed");
}
@Test public void computedMethod() {
model.setValue(4);
assertEquals(16, model.getPowerValue());
}
@Test public void arrayIsMutable() {
assertEquals(model.getNames().size(), 0, "Is empty");
model.getNames().add("Jarda");
assertEquals(model.getNames().size(), 1, "One element");
}
@Test public void arrayChangesNotified() {
model.getNames().add("Hello");
assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
my.mutated.clear();
Iterator<String> it = model.getNames().iterator();
assertEquals(it.next(), "Hello");
it.remove();
assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
my.mutated.clear();
ListIterator<String> lit = model.getNames().listIterator();
lit.add("Jarda");
assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
}
@Test public void autoboxedArray() {
model.getValues().add(10);
assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten");
}
@Test public void derivedArrayProp() {
model.setCount(10);
List<String> arr = model.getRepeat();
assertEquals(arr.size(), 10, "Ten items: " + arr);
my.mutated.clear();
model.setCount(5);
arr = model.getRepeat();
assertEquals(arr.size(), 5, "Five items: " + arr);
assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated);
assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated);
}
@Test public void derivedPropertiesAreNotified() {
model.setValue(33);
// not interested in change of this property
my.mutated.remove("changedProperty");
assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated);
my.mutated.clear();
model.setUnrelated(44);
// not interested in change of this property
my.mutated.remove("changedProperty");
assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated);
assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated");
}
@Test public void computedPropertyCannotWriteToModel() {
leakedModel = model;
try {
String res = model.getNotAllowedWrite();
fail("We should not be allowed to write to the model: " + res);
} catch (IllegalStateException ex) {
// OK, we can't read
}
}
@Test public void computedPropertyCannotReadToModel() {
leakedModel = model;
try {
String res = model.getNotAllowedRead();
fail("We should not be allowed to read from the model: " + res);
} catch (IllegalStateException ex) {
// OK, we can't read
}
}
@OnReceive(url = "{protocol}://{host}?query={query}", data = Person.class)
static void loadPeople(Modelik thiz, People p) {
Modelik m = null;
m.applyBindings();
m.loadPeople("http", "apidesign.org", "query", new Person(Context.EMPTY));
}
@OnReceive(url = "{protocol}://{host}?callback={back}&query={query}", jsonp = "back")
static void loadPeopleViaJSONP(Modelik thiz, People p) {
Modelik m = null;
m.applyBindings();
m.loadPeopleViaJSONP("http", "apidesign.org", "query");
}
@Function
static void doSomething() {
}
@ComputedProperty
static int powerValue(int value) {
return value * value;
}
@OnPropertyChange({ "powerValue", "unrelated" })
static void aPropertyChanged(Modelik m, String name) {
m.setChangedProperty(name);
}
@OnPropertyChange({ "values" })
static void anArrayPropertyChanged(String name, Modelik m) {
m.setChangedProperty(name);
}
@Test public void changeAnything() {
model.setCount(44);
assertNull(model.getChangedProperty(), "No observed value change");
}
@Test public void changeValue() {
model.setValue(33);
assertEquals(model.getChangedProperty(), "powerValue", "power property changed");
}
@Test public void changeUnrelated() {
model.setUnrelated(333);
assertEquals(model.getChangedProperty(), "unrelated", "unrelated changed");
}
@Test public void changeInArray() {
model.getValues().add(10);
assertEquals(model.getChangedProperty(), "values", "Something added into the array");
}
@ComputedProperty
static String notAllowedRead() {
return "Not allowed callback: " + leakedModel.getUnrelated();
}
@ComputedProperty
static String notAllowedWrite() {
leakedModel.setUnrelated(11);
return "Not allowed callback!";
}
@ComputedProperty
static List<String> repeat(int count) {
return Collections.nCopies(count, "Hello");
}
public @Test void hasPersonPropertyAndComputedFullName() {
List<Person> arr = model.getPeople();
assertEquals(arr.size(), 0, "By default empty");
Person p = null;
if (p != null) {
String fullNameGenerated = p.getFullName();
assertNotNull(fullNameGenerated);
}
}
private static class MockTechnology implements Technology<Object> {
private final List<String> mutated = new ArrayList<String>();
@Override
public Object wrapModel(Object model) {
return this;
}
@Override
public void valueHasMutated(Object data, String propertyName) {
mutated.add(propertyName);
}
@Override
public void bind(PropertyBinding b, Object model, Object data) {
}
@Override
public void expose(FunctionBinding fb, Object model, Object d) {
}
@Override
public void applyBindings(Object data) {
}
@Override
public Object wrapArray(Object[] arr) {
return arr;
}
@Override
public <M> M toModel(Class<M> modelClass, Object data) {
return modelClass.cast(data);
}
}
}