1/*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8171385
27 * @summary Test JShell#stop
28 * @modules jdk.jshell/jdk.internal.jshell.tool
29 * @build KullaTesting TestingInputStream
30 * @run testng StopExecutionTest
31 */
32
33import java.io.IOException;
34import java.io.PipedInputStream;
35import java.io.PipedOutputStream;
36import java.io.PrintWriter;
37import java.io.StringWriter;
38import java.util.Random;
39import java.util.concurrent.CountDownLatch;
40import java.util.function.Consumer;
41
42import jdk.internal.jshell.tool.StopDetectingInputStream;
43import jdk.internal.jshell.tool.StopDetectingInputStream.State;
44import jdk.jshell.JShell;
45import org.testng.annotations.Test;
46
47import static org.testng.Assert.assertEquals;
48import static org.testng.Assert.fail;
49
50@Test
51public class StopExecutionTest extends KullaTesting {
52
53    private final Object lock = new Object();
54    private boolean isStopped;
55
56    @Test(enabled = false) // TODO 8129546
57    public void testStopLoop() throws InterruptedException {
58        scheduleStop("while (true) ;");
59    }
60
61    @Test(enabled = false) // TODO 8129546
62    public void testStopASleep() throws InterruptedException {
63        scheduleStop("while (true) { try { Thread.sleep(100); } catch (InterruptedException ex) { } }");
64    }
65
66    @Test(enabled = false) // TODO 8129546
67    public void testScriptCatchesStop() throws Exception {
68        scheduleStop("for (int i = 0; i < 30; i++) { try { Thread.sleep(100); } catch (Throwable ex) { } }");
69    }
70
71    private void scheduleStop(String src) throws InterruptedException {
72        JShell state = getState();
73        isStopped = false;
74        StringWriter writer = new StringWriter();
75        PrintWriter out = new PrintWriter(writer);
76        Thread t = new Thread(() -> {
77            int i = 1;
78            int n = 30;
79            synchronized (lock) {
80                do {
81                    state.stop();
82                    if (!isStopped) {
83                        out.println("Not stopped. Try again: " + i);
84                        try {
85                            lock.wait(1000);
86                        } catch (InterruptedException ignored) {
87                        }
88                    }
89                } while (i++ < n && !isStopped);
90                if (!isStopped) {
91                    System.err.println(writer.toString());
92                    fail("Evaluation was not stopped: '" + src + "'");
93                }
94            }
95        });
96        t.start();
97        assertEval(src);
98        synchronized (lock) {
99            out.println("Evaluation was stopped successfully: '" + src + "'");
100            isStopped = true;
101            lock.notify();
102        }
103        // wait until the background thread finishes to prevent from calling 'stop' on closed state.
104        t.join();
105    }
106
107    public void testStopDetectingInputRandom() throws IOException {
108        long seed = System.nanoTime();
109        Random r = new Random(seed);
110
111        for (int m = 0; m < 10; m++) {
112            StopDetectingInputStream buffer = new StopDetectingInputStream(null, null);
113
114            buffer.setState(State.BUFFER);
115
116            for (int i = 0; i < 1000; i++) {
117                int chunkSize = r.nextInt(StopDetectingInputStream.INITIAL_SIZE * 3);
118
119                doChunk(buffer, chunkSize);
120            }
121        }
122    }
123
124    private void doChunk(StopDetectingInputStream buffer, int chunkSize) throws IOException {
125        for (int c = 0; c < chunkSize; c++) {
126            buffer.write(c);
127        }
128
129        for (int c = 0; c < chunkSize; c++) {
130            int read = buffer.read();
131
132            assertEquals(read, c);
133        }
134    }
135
136    public void testStopDetectingInputBufferWaitStop() throws Exception {
137        Runnable shouldNotHappenRun =
138                () -> { throw new AssertionError("Should not happen."); };
139        Consumer<Exception> shouldNotHappenExc =
140                exc -> { throw new AssertionError("Should not happen.", exc); };
141        StopDetectingInputStream sd = new StopDetectingInputStream(shouldNotHappenRun, shouldNotHappenExc);
142        CountDownLatch reading = new CountDownLatch(1);
143        PipedInputStream is = new PipedInputStream() {
144            @Override
145            public int read() throws IOException {
146                reading.countDown();
147                return super.read();
148            }
149        };
150        PipedOutputStream os = new PipedOutputStream(is);
151
152        sd.setInputStream(is);
153        sd.setState(State.BUFFER);
154        reading.await();
155        sd.setState(State.WAIT);
156        os.write(3);
157        int value = sd.read();
158
159        if (value != 3) {
160            throw new AssertionError();
161        }
162    }
163}
164