// Copyright 2016 Twitter. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.twitter.heron.spi.utils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.junit.Assert;
import org.junit.Test;
public class ShellUtilsTest {
private static final Logger LOG = Logger.getLogger(ShellUtilsTest.class.getName());
private static String generateRandomLongString(int size) {
StringBuilder builder = new StringBuilder();
Random random = new Random();
for (int i = 0; i < size; ++i) {
builder.append('a' + random.nextInt(26));
}
return builder.toString();
}
@Test
public void testRunProcess() {
String testString = "testString";
StringBuilder stderr = new StringBuilder();
Assert.assertEquals(0, ShellUtils.runProcess("echo " + testString, stderr));
Assert.assertEquals(testString, stderr.toString().trim());
Assert.assertTrue(!stderr.toString().trim().isEmpty());
}
@Test(timeout = 60000)
public void testLargeOutput() throws IOException, InterruptedException {
File testScript = File.createTempFile("foo-", ".sh");
try {
// A command that fulfills the output buffers.
String command = "printf '.%.0s' {1..1000000}\nprintf '.%.0s' {1..1000000} >&2";
FileOutputStream input = new FileOutputStream(testScript);
try {
input.write(command.getBytes(StandardCharsets.UTF_8));
} finally {
input.close();
}
Assert.assertTrue("Cannot make the test script executable", testScript.setExecutable(true));
StringBuilder stderr = new StringBuilder();
Assert.assertEquals(0,
ShellUtils.runProcess(
"/bin/bash -c " + testScript.getAbsolutePath(), stderr));
// Only checks stdout and stderr are not empty. Correctness is checked in "testRunProcess".
Assert.assertTrue(!stderr.toString().trim().isEmpty());
} finally {
testScript.delete();
}
}
@Test
public void testRunAsyncProcess() throws IOException {
Process p = ShellUtils.runASyncProcess("sleep 30");
try {
p.exitValue();
Assert.fail(p + " was dead");
} catch (IllegalThreadStateException e) {
// This is expected. If the process is alive, "exitValue" will throw
// IllegalThreadStateException
}
p.destroy();
}
@Test
public void testRunAsyncProcessRedirectErrorStream() throws IOException {
Process p = ShellUtils.runASyncProcess("/bin/bash blahblah.sh");
String stdout = ShellUtils.inputstreamToString(p.getInputStream());
// "blahblah.sh" doesn't exist so "bash" will output the error message to stderr. Since stderr
// is redirected to stdout, stdout should contain the error message.
Assert.assertTrue(stdout.contains("blahblah.sh: No such file or directory"));
p.destroy();
}
@Test
public void testInputstreamToString() {
String testString = generateRandomLongString(1024);
ByteArrayInputStream is = new ByteArrayInputStream(testString.getBytes());
Assert.assertEquals(testString, ShellUtils.inputstreamToString(is));
}
@Test
public void testGetProcessBuilder() throws IOException, InterruptedException {
String[] command = {"printenv"};
Map<String, String> env = new HashMap<>();
String key = "heron-shell-utils-test-env-key";
String value = "heron-shell-utils-test-env-value";
env.put(key, value);
// The process should not inherit IO
ProcessBuilder pb = ShellUtils.getProcessBuilder(false, command, new File("."), env);
// Process running normally
Process p = pb.start();
p.waitFor(2, TimeUnit.SECONDS);
Assert.assertTrue(p.getInputStream().available() > 0);
String output = ShellUtils.inputstreamToString(p.getInputStream());
// Environment variables setting properly
String entry = String.format("%s=%s", key, value);
Assert.assertTrue(output.contains(entry));
}
}