blob: bcb009582ca091c3c71b611e91bb66dba5f441f9 [file] [log] [blame]
/*
* Copyright 2015-2016 IBM Corporation
*
* 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 common;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import junit.runner.Version;
/**
* Miscellaneous utilities used in whisk test suite
*/
public class TestUtils {
protected static final Logger logger = Logger.getLogger("basic");
public static final int SUCCESS_EXIT = 0;
public static final int ERROR_EXIT = 1;
public static final int MISUSE_EXIT = 2;
public static final int ACCEPTED = 202; // 202
public static final int BAD_REQUEST = 144; // 400 - 256 = 144
public static final int UNAUTHORIZED = 145; // 401 - 256 = 145
public static final int FORBIDDEN = 147; // 403 - 256 = 147
public static final int NOT_FOUND = 148; // 404 - 256 = 148
public static final int NOTALLOWED = 149; // 405 - 256 = 149
public static final int CONFLICT = 153; // 409 - 256 = 153
public static final int REQUEST_ENTITY_TOO_LARGE = 157; // 413 - 256 = 157
public static final int THROTTLED = 173; // 429 (TOO_MANY_REQUESTS) - 256 =
// 173
public static final int TIMEOUT = 246; // 502 (GATEWAY_TIMEOUT) - 256 = 246
public static final int DONTCARE_EXIT = -1; // any value is ok
public static final int ANY_ERROR_EXIT = -2; // any non-zero value is ok
private static final File catalogDir = WhiskProperties.getFileRelativeToWhiskHome("catalog");
private static final File testActionsDir = WhiskProperties.getFileRelativeToWhiskHome("tests/dat/actions");
private static final File vcapFile = WhiskProperties.getVCAPServicesFile();
private static final String envServices = System.getenv("VCAP_SERVICES");
static {
logger.setLevel(Level.INFO);
System.out.println("JUnit version is: " + Version.id());
}
/**
* Gets path to file relative to catalog directory.
*
* @param name
* relative filename
*/
public static String getCatalogFilename(String name) {
return new File(catalogDir, name).toString();
}
/**
* Gets path to test action file relative to test catalog directory.
*
* @param name
* the filename of the test action
* @return
*/
public static String getTestActionFilename(String name) {
return new File(testActionsDir, name).toString();
}
/**
* Gets the value of VCAP_SERVICES
*
* @return VCAP_SERVICES as a JSON object
*/
public static JsonObject getVCAPServices() {
try {
if (envServices != null) {
return new JsonParser().parse(envServices).getAsJsonObject();
} else {
return new JsonParser().parse(new FileReader(vcapFile)).getAsJsonObject();
}
} catch (Throwable t) {
System.out.println("failed to parse VCAP" + t);
return new JsonObject();
}
}
/*
* Gets a VCAP_SERVICES credentials
*
* @return VCAP credentials as a <String, String> map for each <property,
* value> pair in credentials
*/
public static Map<String, String> getVCAPcredentials(String vcapService) {
try {
JsonArray vcapArray = getVCAPServices().get(vcapService).getAsJsonArray();
JsonObject vcapObject = vcapArray.get(0).getAsJsonObject();
JsonObject credentials = vcapObject.get("credentials").getAsJsonObject();
Map<String, String> map = new HashMap<String, String>();
for (Map.Entry<String, JsonElement> entry : credentials.entrySet()) {
map.put(entry.getKey(), credentials.get(entry.getKey()).getAsString());
}
return map;
} catch (Throwable t) {
System.out.println("failed to parse VCAP" + t);
return Collections.emptyMap();
}
}
/**
* @return a junit {@link TestWatcher} that prints a message when each test
* starts and ends
*/
public static TestWatcher makeTestWatcher() {
return new TestWatcher() {
protected void starting(Description description) {
System.out.format("\nStarting test %s at %s\n", description.getMethodName(), getDateTime());
}
protected void finished(Description description) {
System.out.format("Finished test %s at %s\n\n", description.getMethodName(), getDateTime());
}
};
}
/**
* Sets the number of concurrent tests to run based on system property if it
* is defined.
*/
public static void setForkJoinConcurrency() {
int count = WhiskProperties.concurrentTestCount;
if (count > 0) {
setForkJoinConcurrency(count);
}
}
/**
* Sets the number of concurrent tests to run based on value provided.
*
* @throws IllegalStateException
* if count < 1
*/
public static void setForkJoinConcurrency(int count) {
if (count > 0) {
System.out.format("concurrent test threads %d\n", count);
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", Integer.toString(count));
} else {
throw new IllegalStateException("test thread count must be positive");
}
}
/**
* @return a formatted string representing the date and time
*/
public static String getDateTime() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return sdf.format(date);
}
/**
* @return a formatted string representing the time of day
*/
public static String getTime() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
return sdf.format(date);
}
public static int sleep(int secs) {
if (secs > 0)
try {
Thread.sleep(secs * 1000);
} catch (InterruptedException e) {
}
return secs;
}
public static interface Once<T> {
/**
* a method that returns a T when some condition is satisfied, and
* otherwise returns null.
*
* The intention is that this will be called until satisfied once, and
* then no more.
*/
T once() throws IOException;
}
/**
* wait up to totalWait seconds for a 'step' to return value.
*/
public static boolean waitfor(Once<Boolean> step, int totalWait) throws IOException {
Boolean result = waitfor(step, 0, 1, totalWait);
return result != null && result.booleanValue();
}
/**
* wait up to totalWait seconds for a 'step' to return value.
*/
public static <T> T waitfor(Once<T> step, int initialWait, int pollPeriod, int totalWait) throws IOException {
// Often tests call this routine immediately after starting work.
// Perform an initial wait before hitting the log for the first time.
long endTime = System.currentTimeMillis() + totalWait * 1000;
sleep(initialWait);
while (System.currentTimeMillis() < endTime) {
T satisfied = step.once();
if (satisfied != null && !(satisfied instanceof Boolean)) {
return satisfied;
} else if (satisfied != null && (Boolean) satisfied == true) {
return satisfied;
} else if (System.currentTimeMillis() >= endTime) {
// Make sure we are prompt for the no wait case.
break;
} else {
sleep(pollPeriod);
}
}
return null;
}
@SafeVarargs
public static Map<String, String> makeParameter(Pair<String, String>... params) {
Map<String, String> map = new HashMap<String, String>();
if (params != null) {
for (Pair<String, String> p : params) {
if (p != null && p.fst != null)
map.put(p.fst, p.snd);
}
}
return map;
}
public static Map<String, String> makeParameter(String name, String value) {
return makeParameter(Pair.make(name, value));
}
public static class RunResult {
public final int exitCode;
public final String stdout;
public final String stderr;
private RunResult(int exitCode, String stdout, String stderr) {
this.exitCode = exitCode;
this.stdout = stdout;
this.stderr = stderr;
}
public Pair<String, String> logs() {
return Pair.make(stdout, stderr);
}
public void validateExitCode(int expectedExitCode) {
if (expectedExitCode == TestUtils.DONTCARE_EXIT)
return;
boolean ok = (exitCode == expectedExitCode) || (expectedExitCode == TestUtils.ANY_ERROR_EXIT && exitCode != 0);
if (!ok) {
System.out.format("expected exit code = %d\n%s", expectedExitCode, toString());
assertTrue("Exit code:" + exitCode, exitCode == expectedExitCode);
}
}
@Override
public String toString() {
StringBuilder fmt = new StringBuilder();
fmt.append(String.format("exit code = %d\n", exitCode));
fmt.append(String.format("stdout: %s\n", stdout));
fmt.append(String.format("stderr: %s\n", stderr));
return fmt.toString();
}
}
public static RunResult runCmd(File dir, String... params) throws IOException {
return runCmd(TestUtils.DONTCARE_EXIT, dir, logger, null, params);
}
public static Pair<String, String> runCmd(int expectedExitCode, File dir, String... params) throws IOException {
return runCmd(expectedExitCode, dir, logger, null, params).logs();
}
public static Pair<String, String> runQuietly(int expectedExitCode, File dir, String... params) throws IOException {
return runCmd(expectedExitCode, dir, null, null, params).logs();
}
/*
* Run with no timeout.
*/
public static RunResult runCmd(int expectedExitCode, File dir, Logger logger, Map<String, String> env, String... params) throws IOException {
return runCmd(expectedExitCode, 0, dir, logger, env, params);
}
/**
* Run a command in another process (exec())
*
* @param expectedExitCode
* the exit code expected from the command when it exists
* @param timeoutMilli
* kill the underlying process after this amount of time (0 if no
* timeout)
* @param dir
* the working directory the command runs with
* @param logger
* object to manage logging message
* @param env
* TODO
* @param params
* parameters to pass on the command line to the spawnded command
* @return
* @throws IOException
*/
public static RunResult runCmd(int expectedExitCode, int timeoutMilli, File dir, Logger logger, Map<String, String> env, String... params) throws IOException {
ProcessBuilder pb = new ProcessBuilder(params);
pb.directory(dir);
if (env != null)
pb.environment().putAll(env);
Process p = pb.start();
String stdout = inputStreamToString(p.getInputStream());
String stderr = inputStreamToString(p.getErrorStream());
try {
p.waitFor();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
RunResult rr = new RunResult(p.exitValue(), stdout, stderr);
if (logger != null) {
logger.info("RunResult: " + rr);
}
rr.validateExitCode(expectedExitCode);
return rr;
}
private static String inputStreamToString(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder builder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
return builder.toString();
}
/**
* WARNING: Consider using the WSK_CONFIG_FILE environment variable in tests
* that will be manipulating the CLI property values. Use this method only
* after determining WSK_CONFIG_FILE will not work for the test case.
*/
public static File backupWskProps() throws IOException {
String homedir = System.getProperty("user.home");
Path wskpropsPath = FileSystems.getDefault().getPath(homedir, ".wskprops");
String tempfileName = UUID.randomUUID().toString() + ".wskprops";
Path tempfilePath = FileSystems.getDefault().getPath(homedir, tempfileName);
try {
Files.copy(wskpropsPath, tempfilePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw e;
}
return tempfilePath.toFile();
}
/**
* WARNING: Consider using the WSK_CONFIG_FILE environment variable in tests
* that will be manipulating the CLI property values. Use this method only
* after determining WSK_CONFIG_FILE will not work for the test case.
*/
public static void restoreWskProps(File backupWskProps) throws IOException {
String homedir = System.getProperty("user.home");
Path wskpropsPath = FileSystems.getDefault().getPath(homedir, ".wskprops");
try {
Files.copy(backupWskProps.toPath(), wskpropsPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw e;
}
}
public static File getWskPropsFile() {
String homedir = System.getProperty("user.home");
return new File(homedir + File.separator + ".wskprops");
}
}