1   /*
2    *  Copyright 2006 Simon Raess
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package net.sf.beep4j.internal.tcp;
17  
18  import java.nio.ByteBuffer;
19  import java.nio.charset.Charset;
20  
21  import junit.framework.TestCase;
22  import net.sf.beep4j.Message;
23  import net.sf.beep4j.internal.Constants;
24  import net.sf.beep4j.internal.DataHeader;
25  import net.sf.beep4j.internal.MessageType;
26  import net.sf.beep4j.internal.message.DefaultMessage;
27  import net.sf.beep4j.internal.message.MessageHeader;
28  import net.sf.beep4j.transport.Transport;
29  
30  import org.easymock.MockControl;
31  
32  public class DefaultChannelControllerTest extends TestCase {
33  	
34  	private MockControl transportCtrl;
35  	
36  	private Transport transport;
37  	
38  	private ByteBuffer createFrame(int channel, boolean intermediate, 
39  			long seqno, int start, int size, MessageHeader messageHeader) {
40  		ByteBuffer headerBuffer = messageHeader.asByteBuffer();
41  		
42  		ByteBuffer header = new DataHeader(
43  				MessageType.MSG, channel, 1, intermediate, 
44  				seqno, size + headerBuffer.remaining()).asByteBuffer();
45  		
46  		ByteBuffer buffer = ByteBuffer.allocate(header.remaining() 
47  				+ headerBuffer.remaining() + size + 5);
48  		buffer.put(header);
49  		buffer.put(createPayload(headerBuffer, start, size));
50  		buffer.put(Constants.TRAILER_BYTES);
51  		buffer.flip();
52  		
53  		return buffer;
54  	}
55  	
56  	private ByteBuffer createFrame(int channel, boolean intermediate, long seqno, int start, int size) {
57  		ByteBuffer header = new DataHeader(
58  				MessageType.MSG, channel, 1, intermediate, 
59  				seqno, size).asByteBuffer();
60  		
61  		ByteBuffer buffer = ByteBuffer.allocate(header.remaining() + size + 5);
62  		buffer.put(header);
63  		buffer.put(createPayload(start, size));
64  		buffer.put(Constants.TRAILER_BYTES);
65  		buffer.flip();
66  		
67  		return buffer;
68  	}
69  	
70  	private Message createMessage(int start, int length) {
71  		MessageHeader header = new MessageHeader();
72  		header.addHeader("content-type", "application/beep+xml");
73  		return new DefaultMessage(header, createPayload(start, length));
74  	}
75  
76  	private ByteBuffer createPayload(int start, int size) {
77  		ByteBuffer buffer = ByteBuffer.allocate(size);
78  		fill(buffer, start, size);
79  		buffer.flip();
80  		return buffer;
81  	}
82  	
83  	private ByteBuffer createPayload(ByteBuffer header, int start, int size) {
84  		ByteBuffer result = ByteBuffer.allocate(header.capacity() + size);
85  		result.put(header);
86  		fill(result, start, size);
87  		result.flip();
88  		return result;
89  	}
90  	
91  	private void fill(ByteBuffer buffer, int start, int size) {
92  		for (int i = start; i < start + size; i++) {
93  			buffer.put((byte) (i % 128));
94  		}
95  	}
96  	
97  	@Override
98  	protected void setUp() throws Exception {
99  		transportCtrl = MockControl.createStrictControl(Transport.class);
100 		transportCtrl.setDefaultMatcher(MockControl.ARRAY_MATCHER);
101 		transport = (Transport) transportCtrl.getMock();
102 	}
103 	
104 	/* 
105 	 * Tests that sending a message equal to the remaining window size
106 	 * is sent unfragmented.
107 	 */
108 	public void testSendNonFragmented() throws Exception {
109 		ChannelController target = new DefaultChannelController(transport, 0, 88);
110 		MessageHeader header = new MessageHeader();
111 		header.addHeader("content-type", "application/beep+xml");
112 		
113 		// define expectations
114 		transport.sendBytes(createFrame(0, false, 0, 0, 50, header));
115 		
116 		// replay
117 		transportCtrl.replay();
118 		
119 		// test
120 		Message message = createMessage(0, 50);
121 		target.sendMSG(1, message);
122 		
123 		// verify
124 		transportCtrl.verify();
125 	}
126 	
127 	/*
128 	 * Tests that a message is fragmented and that the first fragment
129 	 * is as large as the remaining window size.
130 	 */
131 	public void testSendFragmented() throws Exception {
132 		MessageHeader header = new MessageHeader();
133 		header.addHeader("content-type", "application/beep+xml");
134 		
135 		// define expectations
136 		transport.sendBytes(createFrame(0, true, 0, 0, 50, header));
137 		
138 		// replay
139 		transportCtrl.replay();
140 		
141 		// test
142 		ChannelController target = new DefaultChannelController(transport, 0, 88);
143 		target.sendMSG(1, createMessage(0, 60));
144 		
145 		// verify
146 		transportCtrl.verify();
147 	}
148 	
149 	/*
150 	 * Tests that the updateSendWindow method does what its supposed to do.
151 	 * The test scenario includes an initial message of length 65 and a
152 	 * window size of 50. 
153 	 * 
154 	 * - sendMessage of size 65
155 	 *   -> first frame (fragment): first 50 bytes of the original message
156 	 * - updateSendWindow(10, 50)
157 	 *   -> second frame (fragment): bytes 51 - 60 of original message
158 	 * - updateSendWindow(15, 50)
159 	 *   -> third frame: bytes 61 - 65 of original message
160 	 */
161 	public void testUpdateSendWindow() throws Exception {
162 		ChannelController target = new DefaultChannelController(transport, 0, 88);
163 		MessageHeader header = new MessageHeader();
164 		header.addHeader("content-type", "application/beep+xml");
165 		
166 		// define expectations
167 		transport.sendBytes(createFrame(0, true, 0, 0, 50, header));
168 		transport.sendBytes(createFrame(0, true, 88, 50, 10));
169 		transport.sendBytes(createFrame(0, false, 98, 60, 5));
170 		
171 		// replay
172 		transportCtrl.replay();
173 		
174 		// test
175 		target.sendMSG(1, createMessage(0, 65));
176 		target.updateSendWindow(10, 88);
177 		target.updateSendWindow(15, 88);
178 		
179 		// verify
180 		transportCtrl.verify();
181 	}
182 	
183 	public void testFrameReceived() throws Exception {
184 		// define expectations
185 		transportCtrl.replay();
186 		
187 		// test
188 		ChannelController controller = new DefaultChannelController(transport, 0, 4096);
189 		controller.frameReceived(0, 1024);
190 		
191 		// verify
192 		transportCtrl.verify();
193 	}
194 	
195 	public void testSendUpdateWindow() throws Exception {
196 		// define expectations
197 		transport.sendBytes(createSEQFrame(0, 4096, 4096));
198 		transportCtrl.replay();
199 		
200 		// test
201 		ChannelController controller = new DefaultChannelController(transport, 0, 4096);
202 		controller.frameReceived(0, 4096);
203 		
204 		// verify
205 		transportCtrl.verify();
206 	}
207 	
208 	public void testSendMultipleUpdateWindow() throws Exception {
209 		// define expectations
210 		transport.sendBytes(createSEQFrame(0, 4096, 4096));
211 		transport.sendBytes(createSEQFrame(0, 8192, 4096));
212 		transport.sendBytes(createSEQFrame(0, 10240, 4096));
213 		transportCtrl.replay();
214 		
215 		// test
216 		ChannelController controller = new DefaultChannelController(transport, 0, 4096);
217 		controller.frameReceived(0, 4096);
218 		controller.frameReceived(4096, 4096);
219 		controller.frameReceived(8192, 2048);
220 		
221 		// verify
222 		transportCtrl.verify();
223 	}
224 	
225 	private ByteBuffer createSEQFrame(int channel, long ackno, int window) {
226 		StringBuilder buf = new StringBuilder(SEQHeader.TYPE);
227 		buf.append(" ");
228 		buf.append(channel);
229 		buf.append(" ");
230 		buf.append(ackno);
231 		buf.append(" ");
232 		buf.append(window);
233 		buf.append("\r\n");
234 		return Charset.forName("US-ASCII").encode(buf.toString());
235 	}
236 
237 }