/*
* Copyright (c) 2012-2015 Spotify AB
*
* 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.spotify.netty4.handler.codec.zmtp;
import com.google.common.collect.Lists;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import static com.spotify.netty4.handler.codec.zmtp.ZMTPVersion.ZMTP10;
import static com.spotify.netty4.handler.codec.zmtp.ZMTPWireFormats.wireFormat;
import static io.netty.buffer.Unpooled.copiedBuffer;
import static io.netty.util.CharsetUtil.UTF_8;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.sameInstance;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.junit.Assert.assertThat;
@RunWith(MockitoJUnitRunner.class)
public class ZMTPWriterTest {
private final List<Object> out = Lists.newArrayList();
@Test
public void testOneFrame() throws Exception {
final ZMTPWriter writer = ZMTPWriter.create(ZMTP10);
final ByteBuf buf = Unpooled.buffer();
writer.reset(buf);
ByteBuf frame = writer.frame(11, false);
assertThat(frame, is(sameInstance(buf)));
final ByteBuf content = copiedBuffer("hello world", UTF_8);
frame.writeBytes(content.duplicate());
final ZMTPFramingDecoder decoder = new ZMTPFramingDecoder(wireFormat(ZMTP10), new RawDecoder());
decoder.decode(null, buf, out);
assertThat(out, hasSize(1));
assertThat(out, contains((Object) singletonList(content)));
}
@Test
public void testTwoFrames() throws Exception {
final ZMTPWriter writer = ZMTPWriter.create(ZMTP10);
final ByteBuf buf = Unpooled.buffer();
writer.reset(buf);
final ByteBuf f0 = copiedBuffer("hello ", UTF_8);
final ByteBuf f1 = copiedBuffer("hello ", UTF_8);
writer.frame(f0.readableBytes(), true).writeBytes(f0.duplicate());
writer.frame(f1.readableBytes(), false).writeBytes(f1.duplicate());
final ZMTPFramingDecoder decoder = new ZMTPFramingDecoder(wireFormat(ZMTP10), new RawDecoder());
decoder.decode(null, buf, out);
assertThat(out, hasSize(1));
assertThat(out, contains((Object) asList(f0, f1)));
}
@Test
public void testReframe() throws Exception {
final ZMTPFramingDecoder decoder = new ZMTPFramingDecoder(wireFormat(ZMTP10), new RawDecoder());
final ZMTPWriter writer = ZMTPWriter.create(ZMTP10);
final ByteBuf buf = Unpooled.buffer();
writer.reset(buf);
// Request a frame with margin in anticipation of a larger payload...
// ... but write a smaller payload
final ByteBuf content = copiedBuffer("hello world", UTF_8);
writer.frame(content.readableBytes() * 2, true).writeBytes(content.duplicate());
// And rewrite the frame accordingly
writer.reframe(content.readableBytes(), false);
// Verify that the message can be parsed
decoder.decode(null, buf, out);
assertThat(out, hasSize(1));
assertThat(out, contains((Object) singletonList(content)));
// Write and verify another message
final ByteBuf next = copiedBuffer("next", UTF_8);
writer.frame(next.readableBytes(), false).writeBytes(next.duplicate());
out.clear();
decoder.decode(null, buf, out);
assertThat(out, hasSize(1));
assertThat(out, contains((Object) singletonList(next)));
}
private class RawDecoder implements ZMTPDecoder {
private long length;
private List<ByteBuf> frames = Lists.newArrayList();
public void header(final ChannelHandlerContext ctx, final long length, final boolean more,
final List<Object> out) {
this.length = length;
}
@Override
public void content(final ChannelHandlerContext ctx, final ByteBuf data,
final List<Object> out) {
if (data.readableBytes() < length) {
return;
}
frames.add(data.readBytes((int) length));
}
@Override
public void finish(final ChannelHandlerContext ctx, final List<Object> out) {
out.add(frames);
frames = Lists.newArrayList();
}
@Override
public void close() {
for (final ByteBuf frame : frames) {
frame.release();
}
frames.clear();
}
}
}