blob: a5be99ca20dbaa4ed78024c021c6b37a5b5d61b3 [file] [log] [blame]
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +00001// Copyright 2015 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.shell;
15
16import static org.junit.Assert.assertEquals;
17import static org.junit.Assert.assertFalse;
18import static org.junit.Assert.assertTrue;
19import static org.junit.Assert.fail;
20
21import org.junit.After;
22import org.junit.Before;
23import org.junit.Test;
24import org.junit.runner.RunWith;
25import org.junit.runners.JUnit4;
26
27/**
28 * Tests of the interaction of Thread.interrupt and Command.execute.
29 *
30 * Read http://www-128.ibm.com/developerworks/java/library/j-jtp05236.html
31 * for background material.
32 *
33 * NOTE: This test is dependent on thread timings. Under extreme machine load
34 * it's possible that this test could fail spuriously or intermittently. In
35 * that case, adjust the timing constants to increase the tolerance.
36 */
37@RunWith(JUnit4.class)
38public class InterruptibleTest {
39
40 private final Thread mainThread = Thread.currentThread();
41
42 // Interrupt main thread after 1 second. Hopefully by then /bin/sleep
43 // should be running.
44 private final Thread interrupter = new Thread() {
45 @Override
46 public void run() {
47 try {
48 Thread.sleep(1000); // 1 sec
49 } catch (InterruptedException e) {
50 throw new IllegalStateException("Unexpected interrupt!");
51 }
52 mainThread.interrupt();
53 }
54 };
55
56 private Command command;
57 private long before;
58
59 @Before
60 public void setUp() throws Exception {
61
62 Thread.interrupted(); // side effect: clear interrupted status
63 assertFalse("Unexpected interruption!", mainThread.isInterrupted());
64
65 this.command = new Command(new String[] { "/bin/sleep", "2" });
66 this.before = System.nanoTime();
67
68 interrupter.start();
69 }
70
71 @After
72 public void tearDown() throws Exception {
73 interrupter.join();
74 Thread.interrupted(); // Clear interrupted status, or else other tests may fail.
75 }
76
77 private void assertDuration(long minMillis, long maxMillis)
78 throws Exception {
79 long after = System.nanoTime();
80 long millis = (after - before) / 1000000;
81 if (millis < minMillis || millis > maxMillis) {
82 fail("Duration " + millis + "ms was not in range "
83 + minMillis + "-" + maxMillis + "ms");
84 }
85 }
86
87 /**
88 * Test that interrupting a thread in an "uninterruptible" Command.execute
89 * preserves the thread's interruptible status, and does not terminate the
90 * subprocess.
91 */
92 @Test
93 public void testUninterruptibleCommandRunsToCompletion() throws Exception {
94 command.execute();
95
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000096 // The interrupter thread should have exited about 1000ms ago.
97 assertFalse("Interrupter thread is still alive!",
98 interrupter.isAlive());
99
100 // The interrupter thread should have set the main thread's interrupt flag.
101 assertTrue("Main thread was not interrupted during command execution!",
102 mainThread.isInterrupted());
103 }
104
105 /**
106 * Test that interrupting a thread in an "interruptible" Command.execute
107 * causes preserves the thread's interruptible status, terminates the
108 * subprocess, and returns promptly.
109 */
110 @Test
111 public void testInterruptibleCommand() throws Exception {
112 try {
113 command.execute(Command.NO_INPUT,
114 Command.NO_OBSERVER,
115 System.out,
116 System.err,
117 true); // => interruptible
118 fail("Subprocess not aborted!");
119 } catch (AbnormalTerminationException e) {
120 assertEquals("Process terminated by signal 15", // SIGINT
121 e.getMessage());
122 }
123
Han-Wen Nienhuys96bf5342015-02-24 17:56:54 +0000124 // Subprocess execution should be around 1000ms, but less than 2000, the subprocess sleep time.
125 assertDuration(1000, 1900);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000126
127 // We don't assert that the interrupter thread has exited; due to prompt
128 // termination it might still be running.
129
130 // The interrupter thread should have set the main thread's interrupt flag.
131 assertTrue("Main thread was not interrupted during command execution!",
132 mainThread.isInterrupted());
133
134 }
135}