View Javadoc
1   /*
2    * Copyright (C) 2009-2010, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.util.io;
45  
46  import static org.junit.Assert.assertArrayEquals;
47  import static org.junit.Assert.assertEquals;
48  import static org.junit.Assert.assertFalse;
49  import static org.junit.Assert.assertTrue;
50  import static org.junit.Assert.fail;
51  
52  import java.io.IOException;
53  import java.io.InterruptedIOException;
54  import java.io.OutputStream;
55  import java.io.PipedInputStream;
56  import java.io.PipedOutputStream;
57  import java.util.Arrays;
58  import java.util.List;
59  
60  import org.eclipse.jgit.util.IO;
61  import org.junit.After;
62  import org.junit.Before;
63  import org.junit.Test;
64  
65  public class TimeoutOutputStreamTest {
66  	private static final int timeout = 250;
67  
68  	private PipedOutputStream out;
69  
70  	private FullPipeInputStream in;
71  
72  	private InterruptTimer timer;
73  
74  	private TimeoutOutputStream os;
75  
76  	private long start;
77  
78  	@Before
79  	public void setUp() throws Exception {
80  		out = new PipedOutputStream();
81  		in = new FullPipeInputStream(out);
82  		timer = new InterruptTimer();
83  		os = new TimeoutOutputStream(out, timer);
84  		os.setTimeout(timeout);
85  	}
86  
87  	@After
88  	public void tearDown() throws Exception {
89  		timer.terminate();
90  		for (Thread t : active())
91  			assertFalse(t instanceof InterruptTimer.AlarmThread);
92  	}
93  
94  	@Test
95  	public void testTimeout_writeByte_Success1() throws IOException {
96  		in.free(1);
97  		os.write('a');
98  		in.want(1);
99  		assertEquals('a', in.read());
100 	}
101 
102 	@Test
103 	public void testTimeout_writeByte_Success2() throws IOException {
104 		final byte[] exp = new byte[] { 'a', 'b', 'c' };
105 		final byte[] act = new byte[exp.length];
106 		in.free(exp.length);
107 		os.write(exp[0]);
108 		os.write(exp[1]);
109 		os.write(exp[2]);
110 		in.want(exp.length);
111 		in.read(act);
112 		assertArrayEquals(exp, act);
113 	}
114 
115 	@Test
116 	public void testTimeout_writeByte_Timeout() throws IOException {
117 		beginWrite();
118 		try {
119 			os.write('\n');
120 			fail("incorrectly write a byte");
121 		} catch (InterruptedIOException e) {
122 			// expected
123 		}
124 		assertTimeout();
125 	}
126 
127 	@Test
128 	public void testTimeout_writeBuffer_Success1() throws IOException {
129 		final byte[] exp = new byte[] { 'a', 'b', 'c' };
130 		final byte[] act = new byte[exp.length];
131 		in.free(exp.length);
132 		os.write(exp);
133 		in.want(exp.length);
134 		in.read(act);
135 		assertArrayEquals(exp, act);
136 	}
137 
138 	@Test
139 	public void testTimeout_writeBuffer_Timeout() throws IOException {
140 		beginWrite();
141 		try {
142 			os.write(new byte[512]);
143 			fail("incorrectly wrote bytes");
144 		} catch (InterruptedIOException e) {
145 			// expected
146 		}
147 		assertTimeout();
148 	}
149 
150 	@Test
151 	public void testTimeout_flush_Success() throws IOException {
152 		final boolean[] called = new boolean[1];
153 		os = new TimeoutOutputStream(new OutputStream() {
154 			@Override
155 			public void write(int b) throws IOException {
156 				fail("should not have written");
157 			}
158 
159 			@Override
160 			public void flush() throws IOException {
161 				called[0] = true;
162 			}
163 		}, timer);
164 		os.setTimeout(timeout);
165 		os.flush();
166 		assertTrue(called[0]);
167 	}
168 
169 	@Test
170 	public void testTimeout_flush_Timeout() throws IOException {
171 		final boolean[] called = new boolean[1];
172 		os = new TimeoutOutputStream(new OutputStream() {
173 			@Override
174 			public void write(int b) throws IOException {
175 				fail("should not have written");
176 			}
177 
178 			@Override
179 			public void flush() throws IOException {
180 				called[0] = true;
181 				for (;;) {
182 					try {
183 						Thread.sleep(1000);
184 					} catch (InterruptedException e) {
185 						throw new InterruptedIOException();
186 					}
187 				}
188 			}
189 		}, timer);
190 		os.setTimeout(timeout);
191 
192 		beginWrite();
193 		try {
194 			os.flush();
195 			fail("incorrectly flushed");
196 		} catch (InterruptedIOException e) {
197 			// expected
198 		}
199 		assertTimeout();
200 		assertTrue(called[0]);
201 	}
202 
203 	@Test
204 	public void testTimeout_close_Success() throws IOException {
205 		final boolean[] called = new boolean[1];
206 		os = new TimeoutOutputStream(new OutputStream() {
207 			@Override
208 			public void write(int b) throws IOException {
209 				fail("should not have written");
210 			}
211 
212 			@Override
213 			public void close() throws IOException {
214 				called[0] = true;
215 			}
216 		}, timer);
217 		os.setTimeout(timeout);
218 		os.close();
219 		assertTrue(called[0]);
220 	}
221 
222 	@Test
223 	public void testTimeout_close_Timeout() throws IOException {
224 		final boolean[] called = new boolean[1];
225 		os = new TimeoutOutputStream(new OutputStream() {
226 			@Override
227 			public void write(int b) throws IOException {
228 				fail("should not have written");
229 			}
230 
231 			@Override
232 			public void close() throws IOException {
233 				called[0] = true;
234 				for (;;) {
235 					try {
236 						Thread.sleep(1000);
237 					} catch (InterruptedException e) {
238 						throw new InterruptedIOException();
239 					}
240 				}
241 			}
242 		}, timer);
243 		os.setTimeout(timeout);
244 
245 		beginWrite();
246 		try {
247 			os.close();
248 			fail("incorrectly closed");
249 		} catch (InterruptedIOException e) {
250 			// expected
251 		}
252 		assertTimeout();
253 		assertTrue(called[0]);
254 	}
255 
256 	private void beginWrite() {
257 		start = now();
258 	}
259 
260 	private void assertTimeout() {
261 		// Our timeout was supposed to be ~250 ms. Since this is a timing
262 		// test we can't assume we spent *exactly* the timeout period, as
263 		// there may be other activity going on in the system. Instead we
264 		// look for the delta between the start and end times to be within
265 		// 50 ms of the expected timeout.
266 		//
267 		final long wait = now() - start;
268 		assertTrue("waited only " + wait + " ms", timeout - wait < 50);
269 	}
270 
271 	private static List<Thread> active() {
272 		Thread[] all = new Thread[16];
273 		int n = Thread.currentThread().getThreadGroup().enumerate(all);
274 		while (n == all.length) {
275 			all = new Thread[all.length * 2];
276 			n = Thread.currentThread().getThreadGroup().enumerate(all);
277 		}
278 		return Arrays.asList(all).subList(0, n);
279 	}
280 
281 	private static long now() {
282 		return System.currentTimeMillis();
283 	}
284 
285 	private static final class FullPipeInputStream extends PipedInputStream {
286 		FullPipeInputStream(PipedOutputStream src) throws IOException {
287 			super(src);
288 			src.write(new byte[PIPE_SIZE]);
289 		}
290 
291 		void want(int cnt) throws IOException {
292 			IO.skipFully(this, PIPE_SIZE - cnt);
293 		}
294 
295 		void free(int cnt) throws IOException {
296 			IO.skipFully(this, cnt);
297 		}
298 	}
299 }