193 lines
9.4 KiB
Java
193 lines
9.4 KiB
Java
package jmri.server.json.time;
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
import com.fasterxml.jackson.databind.util.StdDateFormat;
|
|
|
|
import java.beans.PropertyChangeEvent;
|
|
import java.beans.PropertyChangeListener;
|
|
import java.io.DataOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.Date;
|
|
import java.util.Locale;
|
|
|
|
import jmri.InstanceManager;
|
|
import jmri.JmriException;
|
|
import jmri.Timebase;
|
|
import jmri.server.json.JSON;
|
|
import jmri.server.json.JsonException;
|
|
import jmri.server.json.JsonMockConnection;
|
|
import jmri.server.json.JsonRequest;
|
|
import jmri.util.JUnitAppender;
|
|
import jmri.util.JUnitUtil;
|
|
|
|
import org.junit.jupiter.api.*;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
|
|
public class JsonTimeSocketServiceTest {
|
|
|
|
private final Locale locale = Locale.ENGLISH;
|
|
|
|
@Test
|
|
public void testOnMessage() throws IOException, JmriException, JsonException {
|
|
JsonMockConnection connection = new JsonMockConnection((DataOutputStream) null);
|
|
JsonTimeSocketService service = new JsonTimeSocketService(connection);
|
|
Timebase manager = InstanceManager.getDefault(Timebase.class);
|
|
TimebaseTimeListener listener = new TimebaseTimeListener();
|
|
Assertions.assertEquals(0, listener.getTime().getTime());
|
|
StdDateFormat formatter = new StdDateFormat();
|
|
int rate = 60; // rate is one minute every six seconds
|
|
assertEquals( 0, manager.getPropertyChangeListeners().length, "No change listeners");
|
|
manager.setRun(false); // stop for testing
|
|
// GET method
|
|
service.onMessage(JSON.TIME, connection.getObjectMapper().createObjectNode(),
|
|
new JsonRequest(locale, JSON.V5, JSON.GET, 42));
|
|
JsonNode message = connection.getMessage();
|
|
Date current = manager.getTime();
|
|
assertNotNull( message, "Message is not null");
|
|
assertEquals( 1.0, message.path(JSON.DATA).path(JSON.RATE).asDouble(), 0.0, "Rate is realtime");
|
|
assertEquals( JSON.OFF, message.path(JSON.DATA).path(JSON.STATE).asInt(), "Timebase is on");
|
|
assertEquals( formatter.format(current),
|
|
message.findPath(JSON.DATA).path(JSON.TIME).asText(), "Time is correct");
|
|
assertEquals( 1, manager.getPropertyChangeListeners().length, "Service is listening to changes");
|
|
// Add second listener
|
|
manager.addPropertyChangeListener("time", listener);
|
|
// POST method
|
|
ObjectNode data = connection.getObjectMapper().createObjectNode();
|
|
data.put(JSON.RATE, rate); // integer
|
|
data.put(JSON.STATE, JSON.ON); // start the fast clock -- to test that listeners set in onMessage work
|
|
service.onMessage(JSON.TIME, data, new JsonRequest(locale, JSON.V5, JSON.POST, 42));
|
|
message = connection.getMessage();
|
|
current = manager.getTime(); // time before fast clock starts
|
|
assertNotNull( message, "Message is not null");
|
|
assertEquals( rate, message.path(JSON.DATA).path(JSON.RATE).asDouble(), 0.0, "Rate is fast");
|
|
assertEquals( JSON.ON, message.path(JSON.DATA).path(JSON.STATE).asInt(), "Timebase is on");
|
|
// a timing issue can cause the message turning the fast clock on to
|
|
// not get the time at start of running, so don't test that
|
|
assertEquals( 2, manager.getPropertyChangeListeners().length, "Service and listener are listening to changes");
|
|
Date waitFor = current;
|
|
JUnitUtil.waitFor(() -> {
|
|
return !manager.getTime().equals(waitFor);
|
|
},"current time not different to manager getTime");
|
|
// current = listener.getTime(); // get time from listener
|
|
message = connection.getMessage();
|
|
assertNotNull( message, "Message is not null");
|
|
assertEquals( rate, message.path(JSON.DATA).path(JSON.RATE).asDouble(), 0.0, "Rate is fast");
|
|
assertEquals( JSON.ON, message.path(JSON.DATA).path(JSON.STATE).asInt(), "Timebase is on");
|
|
data.put(JSON.STATE, JSON.OFF); // stop the fast clock
|
|
service.onMessage(JSON.TIME, data, new JsonRequest(Locale.ENGLISH, JSON.V5, JSON.POST, 42));
|
|
current = manager.getTime();
|
|
message = connection.getMessage();
|
|
assertNotNull( message, "Message is not null");
|
|
assertEquals( rate, message.path(JSON.DATA).path(JSON.RATE).asDouble(), 0.0, "Rate is fast");
|
|
assertEquals( JSON.OFF, message.path(JSON.DATA).path(JSON.STATE).asInt(), "Timebase is off");
|
|
// a failure on next line indicates JsonTimeHttpService has been changed to send message to client
|
|
// before posting changes instead of after posting changes and that change should be undone
|
|
assertEquals( formatter.format(current),
|
|
message.path(JSON.DATA).path(JSON.TIME).asText(), "Time is current");
|
|
// POST unreasonable rate
|
|
data.put(JSON.RATE, 123456.789); // double so that both integers and doubles are tested
|
|
JsonException ex = assertThrows( JsonException.class, () ->
|
|
service.onMessage(JSON.TIME, data,
|
|
new JsonRequest(locale, JSON.V5, JSON.POST, 42)),
|
|
"Expected exception not thrown");
|
|
assertEquals( 400, ex.getCode(), "HTTP Invalid Request");
|
|
assertEquals( "Error setting rate.", ex.getMessage(), "Error message");
|
|
JUnitAppender.assertErrorMessage("rate of 123456.789 is out of reasonable range 0.1 - 100.0");
|
|
// POST bad time
|
|
data.put(JSON.RATE, 100); // set rate to max valid rate
|
|
data.put(JSON.TIME, "this is not a time");
|
|
ex = assertThrows( JsonException.class, () ->
|
|
service.onMessage(JSON.TIME, data, new JsonRequest(locale, JSON.V5, JSON.POST, 42)),
|
|
"Expected exception not thrown");
|
|
assertEquals( 400, ex.getCode(), "HTTP Invalid Request");
|
|
assertEquals( "Time not in ISO 8601 format.", ex.getMessage(), "Error message");
|
|
// POST good time
|
|
data.put(JSON.TIME, formatter.format(waitFor));
|
|
service.onMessage(JSON.TIME, data, new JsonRequest(locale, JSON.V5, JSON.POST, 42));
|
|
message = connection.getMessage();
|
|
current = manager.getTime();
|
|
assertNotNull( message, "Message is not null");
|
|
assertEquals( 100, message.path(JSON.DATA).path(JSON.RATE).asDouble(), 0.0, "Rate is fast");
|
|
assertEquals( JSON.OFF, message.path(JSON.DATA).path(JSON.STATE).asInt(), "Timebase is off");
|
|
assertEquals( formatter.format(current),
|
|
message.path(JSON.DATA).path(JSON.TIME).asText(), "Time is current");
|
|
service.onClose(); // clean up listeners
|
|
manager.removePropertyChangeListener("time", listener);
|
|
assertEquals( 0, manager.getPropertyChangeListeners().length, "Service is not listening to changes");
|
|
}
|
|
|
|
@Test
|
|
public void testOnList() {
|
|
JsonMockConnection connection = new JsonMockConnection((DataOutputStream) null);
|
|
JsonTimeSocketService service = new JsonTimeSocketService(connection);
|
|
JsonException ex = assertThrows( JsonException.class, () ->
|
|
service.onList(JSON.TIME, connection.getObjectMapper().createObjectNode(),
|
|
new JsonRequest(locale, JSON.V5, JSON.GET, 42)),
|
|
"Expected exception not thrown");
|
|
assertEquals( 400, ex.getCode(), "Code is HTTP BAD REQUEST");
|
|
assertEquals( "time cannot be listed.", ex.getMessage(), "Message is unlistable");
|
|
}
|
|
|
|
/**
|
|
* Test that listener handles error states correctly.
|
|
*
|
|
* @throws JsonException if unexpected exception occurs
|
|
* @throws JmriException if unexpected exception occurs
|
|
* @throws IOException if unexpected exception occurs
|
|
*/
|
|
@Test
|
|
public void testListenerErrorHandling() throws IOException, JmriException, JsonException {
|
|
JsonMockConnection connection = new JsonMockConnection((DataOutputStream) null);
|
|
JsonTimeHttpService http = new JsonTimeHttpService(connection.getObjectMapper());
|
|
JsonTimeSocketService service = new JsonTimeSocketService(connection, http);
|
|
Timebase manager = InstanceManager.getDefault(Timebase.class);
|
|
manager.setRun(false); // stop for testing
|
|
// GET method
|
|
service.onMessage(JSON.TIME, connection.getObjectMapper().createObjectNode(),
|
|
new JsonRequest(locale, JSON.V5, JSON.GET, 42));
|
|
// We should be listening so make a change
|
|
manager.setRate(60); // one minute per second
|
|
// Thrown IOException on next message
|
|
connection.setThrowIOException(true);
|
|
int size = connection.getMessages().size();
|
|
manager.setRate(10);
|
|
// The deliberately thrown IOException should have been caught and discarded
|
|
assertEquals( size, connection.getMessages().size(), "message not sent sfter throwing exception");
|
|
}
|
|
|
|
@BeforeEach
|
|
public void setUp() {
|
|
JUnitUtil.setUp();
|
|
}
|
|
|
|
@AfterEach
|
|
public void tearDown() {
|
|
JUnitUtil.tearDown();
|
|
}
|
|
|
|
private static class TimebaseTimeListener implements PropertyChangeListener {
|
|
|
|
private Date time = new Date();
|
|
|
|
private TimebaseTimeListener() {
|
|
time.setTime(0);
|
|
}
|
|
|
|
@Override
|
|
public void propertyChange(PropertyChangeEvent evt) {
|
|
if (evt.getPropertyName().equals("time")) {
|
|
this.time = (Date) evt.getNewValue();
|
|
}
|
|
}
|
|
|
|
public Date getTime() {
|
|
return new Date(this.time.getTime());
|
|
}
|
|
}
|
|
|
|
}
|