/**
* Copyright 2007-2015, Kaazing Corporation. 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 org.kaazing.k3po.control.internal;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.charset.Charset;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.jmock.Expectations;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
import org.jmock.integration.junit4.JUnitRuleMockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.kaazing.k3po.control.internal.command.AbortCommand;
import org.kaazing.k3po.control.internal.command.PrepareCommand;
import org.kaazing.k3po.control.internal.command.StartCommand;
import org.kaazing.k3po.control.internal.event.CommandEvent;
import org.kaazing.k3po.control.internal.event.ErrorEvent;
import org.kaazing.k3po.control.internal.event.FinishedEvent;
import org.kaazing.k3po.control.internal.event.PreparedEvent;
import org.kaazing.k3po.control.internal.event.StartedEvent;
public class ControlTest {
private static final Charset UTF_8 = Charset.forName("UTF-8");
private Control control;
@Rule
public JUnitRuleMockery mockery = new JUnitRuleMockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
private InputStream input;
private OutputStream output;
@Before
public void setupControl() throws Exception {
input = mockery.mock(InputStream.class);
output = mockery.mock(OutputStream.class);
control = new Control(new URL(null, "test://internal", new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL location) throws IOException {
return new URLConnection(location) {
@Override
public void connect() throws IOException {
// no-op
}
@Override
public InputStream getInputStream() {
return input;
}
@Override
public OutputStream getOutputStream() {
return output;
}
};
}
}));
}
@Test(expected = IllegalStateException.class)
public void shouldNotWriteCommand() throws Exception {
StartCommand start = new StartCommand();
control.writeCommand(start);
}
@Test(expected = IllegalStateException.class)
public void shouldNotReadEvent() throws Exception {
control.readEvent();
}
@Test
public void shouldConnect() throws Exception {
control.connect();
}
@Test
public void shouldConnectAndDisconnect() throws Exception {
mockery.checking(new Expectations() {
{
oneOf(input).close();
oneOf(output).close();
}
});
control.connect();
control.disconnect();
}
@Test
public void shouldWritePrepareCommand() throws Exception {
String path = "org/kaazing/robot/control/myscript";
final byte[] expectedPrepare =
("PREPARE\n" +
"version:2.0\n" +
"content-length:0\n" +
"name:" + path + "\n" +
"\n").getBytes(UTF_8);
mockery.checking(new Expectations() {
{
oneOf(output).write(with(hasInitialBytes(expectedPrepare)), with(equal(0)), with(equal(expectedPrepare.length)));
oneOf(output).flush();
}
});
PrepareCommand prepare = new PrepareCommand();
prepare.setName(path);
control.connect();
control.writeCommand(prepare);
}
@Test
public void shouldWriteStartCommand() throws Exception {
final byte[] expectedStart =
("START\n" +
"\n").getBytes(UTF_8);
mockery.checking(new Expectations() {
{
oneOf(output).write(with(hasInitialBytes(expectedStart)), with(equal(0)), with(equal(expectedStart.length)));
oneOf(output).flush();
}
});
StartCommand start = new StartCommand();
control.connect();
control.writeCommand(start);
}
@Test
public void shouldWriteAbortCommand() throws Exception {
final byte[] expectedStart =
("ABORT\n" +
"\n").getBytes(UTF_8);
mockery.checking(new Expectations() {
{
oneOf(output).write(with(hasInitialBytes(expectedStart)), with(equal(0)), with(equal(expectedStart.length)));
oneOf(output).flush();
}
});
AbortCommand abort = new AbortCommand();
control.connect();
control.writeCommand(abort);
}
@Test
public void shouldReadPreparedEvent() throws Exception {
PreparedEvent expectedPrepared = new PreparedEvent();
expectedPrepared.setScript("# comment");
mockery.checking(new Expectations() {
{
oneOf(input).read(with(any(byte[].class)), with(equal(0)), with(any(int.class)));
will(readInitialBytes(0, ("PREPARED\n" +
"content-length:9\n" +
"future-header:future-value\n" + // test forward compatibility
"\n" +
"# comment").getBytes(UTF_8)));
allowing(input).available();
will(returnValue(0));
}
});
control.connect();
CommandEvent finished = control.readEvent();
assertEquals(expectedPrepared, finished);
}
@Test
public void shouldReadStartedEvent() throws Exception {
StartedEvent expectedStarted = new StartedEvent();
mockery.checking(new Expectations() {
{
oneOf(input).read(with(any(byte[].class)), with(equal(0)), with(any(int.class)));
will(readInitialBytes(0, ("STARTED\n" + "future-header:future-value\n" + // test forward compatibility
"\n").getBytes(UTF_8)));
allowing(input).available();
will(returnValue(0));
}
});
control.connect();
CommandEvent started = control.readEvent();
assertEquals(expectedStarted, started);
}
@Test
public void shouldReadFinishedEvent() throws Exception {
FinishedEvent expectedFinished = new FinishedEvent();
expectedFinished.setScript("# comment");
mockery.checking(new Expectations() {
{
oneOf(input).read(with(any(byte[].class)), with(equal(0)), with(any(int.class)));
will(readInitialBytes(0, ("FINISHED\n" +
"content-length:9\n" +
"future-header:future-value\n" + // test forward compatibility
"\n" +
"# comment").getBytes(UTF_8)));
allowing(input).available();
will(returnValue(0));
}
});
control.connect();
CommandEvent finished = control.readEvent();
assertEquals(expectedFinished, finished);
}
@Test
public void shouldReadErrorEvent() throws Exception {
ErrorEvent expectedError = new ErrorEvent();
expectedError.setSummary("summary text");
expectedError.setDescription("description text");
mockery.checking(new Expectations() {
{
oneOf(input).read(with(any(byte[].class)), with(equal(0)), with(any(int.class)));
will(readInitialBytes(0, ("ERROR\n" +
"summary:summary text\n" +
"content-length:16\n" +
"future-header:future-value\n" + // test forward compatibility
"\n" +
"description text").getBytes(UTF_8)));
allowing(input).available();
will(returnValue(0));
}
});
control.connect();
CommandEvent error = control.readEvent();
assertEquals(expectedError, error);
}
private static Matcher<byte[]> hasInitialBytes(final byte[] expected) {
return new BaseMatcher<byte[]>() {
@Override
public boolean matches(Object item) {
if (!(item instanceof byte[])) {
return false;
}
byte[] actual = (byte[]) item;
if (actual.length < expected.length) {
return false;
}
for (int i = 0; i < expected.length; i++) {
if (actual[i] != expected[i]) {
return false;
}
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("has initial bytes");
}
};
}
private static Action readInitialBytes(final int parameter, final byte[] initialBytes) {
return new Action() {
@Override
public Object invoke(Invocation invocation) throws Throwable {
byte[] array = (byte[]) invocation.getParameter(parameter);
if (array.length < initialBytes.length) {
throw new IndexOutOfBoundsException();
}
for (int i = 0; i < initialBytes.length; i++) {
array[i] = initialBytes[i];
}
return initialBytes.length;
}
@Override
public void describeTo(Description description) {
description.appendText("read initial bytes");
}
};
}
}