PipedInputStreamTest.java revision e09ba12220c2cbbe9d91514da4d0f8fd8543b239
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17package tests.api.java.io;
18
19import java.io.IOException;
20import java.io.PipedInputStream;
21import java.io.PipedOutputStream;
22
23import dalvik.annotation.TestLevel;
24import dalvik.annotation.TestTargetClass;
25import dalvik.annotation.TestTargetNew;
26
27@TestTargetClass(PipedInputStream.class)
28public class PipedInputStreamTest extends junit.framework.TestCase {
29
30    private final int BUFFER_SIZE = 1024;
31
32    static class PWriter implements Runnable {
33        PipedOutputStream pos;
34
35        public byte bytes[];
36
37        public void run() {
38            try {
39                pos.write(bytes);
40                synchronized (this) {
41                    notify();
42                }
43            } catch (Exception e) {
44                e.printStackTrace(System.out);
45                System.out.println("Error while running the writer thread.");
46            }
47        }
48
49        public PWriter(PipedOutputStream pout, int nbytes) {
50            pos = pout;
51            bytes = new byte[nbytes];
52            for (int i = 0; i < bytes.length; i++)
53                bytes[i] = (byte) (System.currentTimeMillis() % 9);
54        }
55    }
56
57    static class PWriter2 implements Runnable {
58        PipedOutputStream pos;
59
60        public boolean keepRunning = true;
61
62        public void run() {
63            try {
64                pos.write(42);
65                pos.close();
66                while (keepRunning) {
67                    Thread.sleep(1000);
68                }
69            } catch (Exception e) {
70                e.printStackTrace(System.out);
71                System.out.println("Error while running the writer thread.");
72            }
73        }
74
75        public PWriter2(PipedOutputStream pout) {
76            pos = pout;
77        }
78    }
79
80    Thread t;
81
82    PWriter pw;
83
84    PipedInputStream pis;
85
86    PipedOutputStream pos;
87
88    /**
89     * @tests java.io.PipedInputStream#PipedInputStream()
90     */
91    @TestTargetNew(
92        level = TestLevel.COMPLETE,
93        notes = "",
94        method = "PipedInputStream",
95        args = {}
96    )
97    public void test_Constructor() throws IOException {
98        pis = new PipedInputStream();
99        assertEquals("There should not be any bytes available. ", 0, pis.available());
100        pis.close();
101    }
102
103    /**
104     * @tests java.io.PipedInputStream#PipedInputStream(java.io.PipedOutputStream)
105     */
106    @TestTargetNew(
107        level = TestLevel.COMPLETE,
108        notes = "",
109        method = "PipedInputStream",
110        args = {java.io.PipedOutputStream.class}
111    )
112    public void test_ConstructorLjava_io_PipedOutputStream() throws IOException {
113        pos = new PipedOutputStream(new PipedInputStream());
114
115        try {
116            pis = new PipedInputStream(pos);
117            fail("IOException expected since the output stream is already connected.");
118        } catch (IOException e) {
119            // Expected.
120        }
121
122        pis = new PipedInputStream(new PipedOutputStream());
123        assertEquals("There should not be any bytes available. ", 0, pis.available());
124
125        pis.close();
126        pos.close();
127    }
128
129    /**
130     * @tests java.io.PipedInputStream#available()
131     */
132    @TestTargetNew(
133        level = TestLevel.COMPLETE,
134        notes = "No IOException checking because it is never thrown in the source code.",
135        method = "available",
136        args = {}
137    )
138    public void test_available() throws Exception {
139        // Test for method int java.io.PipedInputStream.available()
140        pis = new PipedInputStream();
141        pos = new PipedOutputStream();
142
143        pis.connect(pos);
144        t = new Thread(pw = new PWriter(pos, 1000));
145        t.start();
146
147        synchronized (pw) {
148            pw.wait(10000);
149        }
150        assertEquals("Test 1: Incorrect number of bytes available. ",
151                     1000, pis.available());
152
153        PipedInputStream pin = new PipedInputStream();
154        PipedOutputStream pout = new PipedOutputStream(pin);
155        // Writing another byte would cause the write to wait
156        // for a read before returning
157        for (int i = 0; i < BUFFER_SIZE; i++)
158            pout.write(i);
159        assertEquals("Test 2: Incorrect number of bytes available. ",
160                     BUFFER_SIZE, pin.available());
161    }
162
163    /**
164     * @tests java.io.PipedInputStream#close()
165     */
166    @TestTargetNew(
167        level = TestLevel.COMPLETE,
168        notes = "No IOException checking because it is never thrown in the source code.",
169        method = "close",
170        args = {}
171    )
172    public void test_close() throws IOException {
173        // Test for method void java.io.PipedInputStream.close()
174        pis = new PipedInputStream();
175        pos = new PipedOutputStream();
176        pis.connect(pos);
177        pis.close();
178        try {
179            pos.write((byte) 127);
180            fail("IOException expected.");
181        } catch (IOException e) {
182            // The spec for PipedInput says an exception should be thrown if
183            // a write is attempted to a closed input. The PipedOuput spec
184            // indicates that an exception should be thrown only when the
185            // piped input thread is terminated without closing
186            return;
187        }
188    }
189
190    /**
191     * @tests java.io.PipedInputStream#connect(java.io.PipedOutputStream)
192     */
193    @TestTargetNew(
194        level = TestLevel.COMPLETE,
195        notes = "",
196        method = "connect",
197        args = {java.io.PipedOutputStream.class}
198    )
199    public void test_connectLjava_io_PipedOutputStream() throws Exception {
200        // Test for method void
201        // java.io.PipedInputStream.connect(java.io.PipedOutputStream)
202        pis = new PipedInputStream();
203        pos = new PipedOutputStream();
204        assertEquals("Test 1: Not-connected pipe returned more than zero available bytes. ",
205                     0, pis.available());
206
207        pis.connect(pos);
208        t = new Thread(pw = new PWriter(pos, 1000));
209        t.start();
210
211        synchronized (pw) {
212            pw.wait(10000);
213        }
214        assertEquals("Test 2: Unexpected number of bytes available. ",
215                     1000, pis.available());
216
217        try {
218            pis.connect(pos);
219            fail("Test 3: IOException expected when reconnecting the pipe.");
220        } catch (IOException e) {
221            // Expected.
222        }
223
224        pis.close();
225        pos.close();
226    }
227
228    /**
229     * @tests java.io.PipedInputStream#read()
230     */
231    @TestTargetNew(
232        level = TestLevel.PARTIAL_COMPLETE,
233        notes = "",
234        method = "read",
235        args = {}
236    )
237    public void test_read() throws Exception {
238        pis = new PipedInputStream();
239        pos = new PipedOutputStream();
240
241        try {
242            pis.read();
243            fail("Test 1: IOException expected since the stream is not connected.");
244        } catch (IOException e) {
245            // Expected.
246        }
247
248        pis.connect(pos);
249        t = new Thread(pw = new PWriter(pos, 100));
250        t.start();
251
252        synchronized (pw) {
253            pw.wait(5000);
254        }
255        assertEquals("Test 2: Unexpected number of bytes available. ",
256                     100, pis.available());
257
258        for (int i = 0; i < 100; i++) {
259            assertEquals("Test 3: read() returned incorrect byte. ",
260                         pw.bytes[i], (byte) pis.read());
261        }
262
263        try {
264            pis.read();
265            fail("Test 4: IOException expected since the thread that has " +
266                 "written to the pipe is no longer alive.");
267        } catch (IOException e) {
268            // Expected.
269        }
270
271        pis.close();
272        try {
273            pis.read();
274            fail("Test 5: IOException expected since the stream is closed.");
275        } catch (IOException e) {
276            // Expected.
277        }
278    }
279
280    /**
281     * @tests java.io.PipedInputStream#read()
282     */
283    @TestTargetNew(
284        level = TestLevel.PARTIAL_COMPLETE,
285        notes = "Checks that read returns -1 if the PipedOutputStream connected to this PipedInputStream is closed.",
286        method = "read",
287        args = {}
288    )
289    public void test_read_2() throws Exception {
290        Thread writerThread;
291        PWriter2 pwriter;
292
293        pos = new PipedOutputStream();
294        pis = new PipedInputStream(pos);
295        writerThread = new Thread(pwriter = new PWriter2(pos));
296        writerThread.start();
297
298        synchronized (pwriter) {
299            pwriter.wait(5000);
300        }
301        pis.read();
302        assertEquals("Test 1: No more data indication expected. ", -1, pis.read());
303        pwriter.keepRunning = false;
304
305        pis.close();
306    }
307
308    /**
309     * @tests java.io.PipedInputStream#read(byte[], int, int)
310     */
311    @TestTargetNew(
312        level = TestLevel.PARTIAL_COMPLETE,
313        notes = "Tests read from unconnected, connected and closed pipe.",
314        method = "read",
315        args = {byte[].class, int.class, int.class}
316    )
317    public void test_read$BII() throws Exception {
318        byte[] buf = new byte[400];
319        pis = new PipedInputStream();
320        pos = new PipedOutputStream();
321
322        try {
323            pis.read(buf, 0, 10);
324            fail("Test 1: IOException expected since the stream is not connected.");
325        } catch (IOException e) {
326            // Expected.
327        }
328
329        pis.connect(pos);
330        t = new Thread(pw = new PWriter(pos, 1000));
331        t.start();
332
333        synchronized (pw) {
334            pw.wait(10000);
335        }
336        assertEquals("Test 2: Unexpected number of bytes available. ",
337                     1000, pis.available());
338        pis.read(buf, 0, 400);
339        for (int i = 0; i < 400; i++) {
340            assertEquals("Test 3: read() returned incorrect byte. ",
341                         pw.bytes[i], buf[i]);
342        }
343
344        pis.close();
345        try {
346            pis.read(buf, 0, 10);
347            fail("Test 4: IOException expected since the stream is closed.");
348        } catch (IOException e) {
349            // Expected.
350        }
351    }
352
353    /**
354     * @tests java.io.PipedInputStream#read(byte[], int, int)
355     * Regression for HARMONY-387
356     */
357    @TestTargetNew(
358        level = TestLevel.PARTIAL_COMPLETE,
359        notes = "Tests illegal length argument.",
360        method = "read",
361        args = {byte[].class, int.class, int.class}
362    )
363    public void test_read$BII_2() throws IOException {
364        PipedInputStream obj = new PipedInputStream();
365        try {
366            obj.read(new byte[0], 0, -1);
367            fail("IndexOutOfBoundsException expected.");
368        } catch (IndexOutOfBoundsException t) {
369            assertEquals(
370                    "IndexOutOfBoundsException rather than a subclass expected.",
371                    IndexOutOfBoundsException.class, t.getClass());
372        }
373    }
374
375    /**
376     * @tests java.io.PipedInputStream#read(byte[], int, int)
377     */
378    @TestTargetNew(
379        level = TestLevel.PARTIAL_COMPLETE,
380        notes = "Tests illegal offset argument.",
381        method = "read",
382        args = {byte[].class, int.class, int.class}
383    )
384    public void test_read$BII_3() throws IOException {
385        PipedInputStream obj = new PipedInputStream();
386        try {
387            obj.read(new byte[10], -1, 1);
388            fail("IndexOutOfBoundsException expected.");
389        } catch (IndexOutOfBoundsException e) {
390            // Expected
391            assertTrue(e.getClass().equals(IndexOutOfBoundsException.class));
392        }
393        try {
394            obj.read(new byte[10], 0, -1);
395            fail("IndexOutOfBoundsException expected.");
396        } catch (IndexOutOfBoundsException e) {
397            // Expected
398            assertTrue(e.getClass().equals(IndexOutOfBoundsException.class));
399        }
400        try {
401            obj.read(new byte[10], 9, 2);
402            fail("IndexOutOfBoundsException expected.");
403        } catch (IndexOutOfBoundsException e) {
404            // Expected
405            assertTrue(e.getClass().equals(IndexOutOfBoundsException.class));
406        }
407    }
408
409    /**
410     * @tests java.io.PipedInputStream#receive(int)
411     */
412    @TestTargetNew(
413        level = TestLevel.COMPLETE,
414        notes = "",
415        method = "receive",
416        args = {int.class}
417    )
418    public void test_receive() throws IOException {
419        pis = new PipedInputStream();
420        pos = new PipedOutputStream();
421
422        // test if writer recognizes dead reader
423        pis.connect(pos);
424        class WriteRunnable implements Runnable {
425
426            boolean pass = false;
427
428            boolean readerAlive = true;
429
430            public void run() {
431                try {
432                    pos.write(1);
433                    while (readerAlive) {
434                        Thread.sleep(100);
435                    }
436                    try {
437                        pos.write(new byte[BUFFER_SIZE]);
438                        // should throw exception since buffer is full and
439                        // reader thread is now dead
440                        pos.write(1);
441                    } catch (IOException e) {
442                        pass = true;
443                    }
444                } catch (IOException e) {
445                    // ignore
446                } catch (InterruptedException e) {
447                    // ignore
448                }
449            }
450        }
451        WriteRunnable writeRunnable = new WriteRunnable();
452        Thread writeThread = new Thread(writeRunnable);
453        class ReadRunnable implements Runnable {
454
455            boolean pass;
456
457            public void run() {
458                try {
459                    pis.read();
460                    pass = true;
461                } catch (IOException e) {}
462            }
463        }
464        ;
465        ReadRunnable readRunnable = new ReadRunnable();
466        Thread readThread = new Thread(readRunnable);
467        writeThread.start();
468        readThread.start();
469        while (readThread.isAlive())
470            ;
471        writeRunnable.readerAlive = false;
472        assertTrue("reader thread failed to read", readRunnable.pass);
473        while (writeThread.isAlive())
474            ;
475        assertTrue("writer thread failed to recognize dead reader",
476                writeRunnable.pass);
477
478        // attempt to write to stream after writer closed
479        pis = new PipedInputStream();
480        pos = new PipedOutputStream();
481
482        pis.connect(pos);
483        class MyRunnable implements Runnable {
484
485            boolean pass;
486
487            public void run() {
488                try {
489                    pos.write(1);
490                } catch (IOException e) {
491                    pass = true;
492                }
493            }
494        }
495        MyRunnable myRun = new MyRunnable();
496        synchronized (pis) {
497            t = new Thread(myRun);
498            // thread t will be blocked inside pos.write(1)
499            // when it tries to call the synchronized method pis.receive
500            // because we hold the monitor for object pis
501            t.start();
502            try {
503                // wait for thread t to get to the call to pis.receive
504                Thread.sleep(100);
505            } catch (InterruptedException e) {}
506            // now we close
507            pos.close();
508        }
509        // we have exited the synchronized block, so now thread t will make
510        // a call to pis.receive AFTER the output stream was closed,
511        // in which case an IOException should be thrown
512        while (t.isAlive()) {
513            ;
514        }
515        assertTrue(
516                "write failed to throw IOException on closed PipedOutputStream",
517                myRun.pass);
518    }
519
520    /**
521     * Tears down the fixture, for example, close a network connection. This
522     * method is called after a test is executed.
523     */
524    protected void tearDown() throws Exception {
525        try {
526            if (t != null) {
527                t.interrupt();
528            }
529        } catch (Exception ignore) {
530        }
531        super.tearDown();
532    }
533}
534