ServiceTest2.java [src/csip/test] Revision: beaf35d680e39fda49d78cf7e170b55dc5710c27 Date: Tue Apr 25 16:47:08 MDT 2017
/*
* $Id$
*
* This file is part of the Cloud Services Integration Platform (CSIP),
* a Model-as-a-Service framework, API and application suite.
*
* 2012-2017, Olaf David and others, OMSLab, Colorado State University.
*
* OMSLab licenses this file to you under the MIT license.
* See the LICENSE file in the project root for more information.
*/
package csip.test;
import csip.Client;
import csip.ModelDataService;
import csip.utils.JSONUtils;
import csip.utils.Services;
import java.io.File;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
import org.apache.commons.io.FileUtils;
import org.apache.http.client.utils.URIBuilder;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.skyscreamer.jsonassert.JSONAssert;
/**
* Service Testing.
*
* @author odavid
*/
public class ServiceTest2 {
static final String KEYSONLY = "keysonly";
static final String STRICT = "strict";
static final String KEYVALUE = "keyvalue";
static final String NONE = "none";
static final List<String> VALIDTESTING = Arrays.asList(KEYSONLY, KEYVALUE, STRICT, NONE);
File folder;
File[] files;
File target;
String testing = NONE;
int concurrent = 1;
int timeout = 1000;
int delay = 0;
int rounds = 1;
String contextUrl = null;
boolean downloadFiles = true;
final JSONArray reports = new JSONArray();
static final Logger log = Logger.getLogger("ServiceTest");
// static final StreamHandler sh = new StreamHandler(System.out, new SimpleFormatter());
static final int _100KB = 102400;
static final int _64KB = 64 * 1024;
static final int LIMIT = _64KB;
static {
// sh.setLevel(Level.OFF);
log.setLevel(Level.OFF);
// log.addHandler(sh);
log.setUseParentHandlers(false);
}
private ServiceTest2(File target, Properties config) throws Exception {
if (!target.exists()) {
throw new IllegalArgumentException("Not a folder or file: " + target);
}
this.target = target;
if (target.getName().endsWith(".json") && target.isFile()) {
folder = target.getParentFile();
files = new File[]{target};
} else if (target.isDirectory()) {
folder = target;
files = folder.listFiles((File pathname) -> pathname.getName().endsWith(".json"));
Arrays.sort(files);
} else {
throw new IllegalArgumentException("Invalid argument: " + target);
}
// load the service testing settings.
String s = config.getProperty("csip.test.verify", System.getProperty("csip.test.verify"));
if (VALIDTESTING.contains(s)) {
testing = s;
}
s = config.getProperty("csip.test.contexturl", System.getProperty("csip.test.contexturl"));
if (s != null && !s.isEmpty()) {
contextUrl = s;
}
s = config.getProperty("csip.test.concurrency", System.getProperty("csip.test.concurrency"));
if (s != null) {
try {
concurrent = Integer.parseInt(s);
} catch (NumberFormatException E) {
}
}
s = config.getProperty("csip.test.timeout", System.getProperty("csip.test.timeout"));
if (s != null) {
try {
timeout = Integer.parseInt(s);
} catch (NumberFormatException E) {
}
}
s = config.getProperty("csip.test.delay", System.getProperty("csip.test.delay"));
if (s != null) {
try {
delay = Integer.parseInt(s);
} catch (NumberFormatException E) {
}
}
s = config.getProperty("csip.test.rounds", System.getProperty("csip.test.rounds"));
if (s != null) {
try {
rounds = Integer.parseInt(s);
} catch (NumberFormatException E) {
}
}
// warmup phase: 0-off, 1-on
s = config.getProperty("csip.test.downloadfiles", System.getProperty("csip.test.downloadfiles"));
if (s != null) {
downloadFiles = Boolean.parseBoolean(s);
}
s = config.getProperty("csip.test.loglevel", System.getProperty("csip.test.loglevel"));
if (s != null && !s.isEmpty()) {
// sh.setLevel(Level.parse(s));
log.setLevel(Level.parse(s));
}
}
/**
* Execute the test.
*
* @return
*/
JSONObject test() {
int attempts = 4;
int bq_size = 4;
final Stats stats = new Stats();
for (int i = 0; i < rounds; i++) {
Services.runParallel(files.length, concurrent, attempts, bq_size, new Services.CallableFactory() {
@Override
public Callable<?> create(final int i) {
return (Callable<?>) () -> {
if (delay > 0) {
try {
Thread.sleep(delay);
} catch (InterruptedException ex) {
}
}
JSONObject report = new JSONObject();
JSONObject result = null;
try {
File f = files[i];
JSONObject json = new JSONObject(FileUtils.readFileToString(f));
String url = JSONUtils.getMetaInfo(json).getString("service_url");
if (contextUrl != null) {
url = contextUrl + getServicePath(url);
}
System.out.println(f.getName() + " >>>>>>>>> " + url + " ");
System.out.flush();
report.put("file", f.toString());
report.put("service", url);
long p = Client.ping(url, timeout);
if (p == -1) {
throw new Exception("Url not reachable: " + url);
}
JSONObject req = JSONUtils.newRequest();
JSONObject req_meta = new JSONObject();
if (JSONUtils.getMetaInfo(json).has("request-results")) {
JSONArray a = JSONUtils.getMetaInfo(json).getJSONArray("request-results");
for (int j = 0; j < a.length(); j++) {
req_meta.accumulate("request-results", a.getString(j));
}
}
req.put("parameter", json.getJSONArray("parameter"));
req.put("metainfo", req_meta);
// System.out.println("REQ>>>>>>>>>>>>>>>>>>>>>> " + req.toString(4));
Client client = new Client(timeout, log);
JSONObject response = client.doPOST(url, req);
String resp = response.toString(2);
if (resp.length() > LIMIT) {
System.out.println("RESPONSE >>>>>>>>>>>>>>>>>>>>>> \n" + resp.substring(0, LIMIT) + " \n .... truncated, > 100KB");
} else {
System.out.println("RESPONSE >>>>>>>>>>>>>>>>>>>>>> \n" + resp);
}
if (response.has("metainfo")) {
report.put("metainfo", response.getJSONObject("metainfo"));
}
if (JSONUtils.getStatus(response).equals("Failed")) {
JSONObject respose_metainfo = response.getJSONObject("metainfo");
throw new Exception("Failed, " + respose_metainfo.getString("error"));
}
if (!JSONUtils.hasResult(response)) {
throw new Exception("No 'result' in response.");
}
// if (downloadFiles) {
// download(res, getResFolder(reqFile), client);
// }
//
result = runtest(json, response);
report.put("test", result);
stats.incSuccess();
} catch (Exception E) {
try {
report.put("test", E.getMessage());
stats.incFailed();
} catch (JSONException ex) {
log.log(Level.SEVERE, null, ex);
}
} finally {
synchronized (reports) {
reports.put(report);
System.out.println("SUMMARY >>>>>>>>>>>>>>>>>>>>>> " + report.toString(2));
System.out.println();
}
}
return null;
};
}
});
}
JSONObject r = new JSONObject();
try {
r.put("target", target.toString());
r.put("date", new Date());
r.put("total", stats.getTotal());
r.put("successful", stats.getSucceeded());
r.put("failed", stats.getFailed());
r.put("tests", reports);
} catch (JSONException ex) {
ex.printStackTrace();
}
return r;
}
/**
*
*/
static class Stats {
int succeeded;
int failed;
void add(Stats other) {
succeeded += other.getSucceeded();
failed += other.getFailed();
}
public int getTotal() {
return succeeded + failed;
}
public int getFailed() {
return failed;
}
public int getSucceeded() {
return succeeded;
}
synchronized void incSuccess() {
succeeded++;
}
synchronized void incFailed() {
failed++;
}
}
JSONObject runtest(JSONObject golden, JSONObject response) throws Exception {
JSONArray response_result = response.getJSONArray("result");
JSONArray response_golden = golden.getJSONArray("result");
Map<String, JSONObject> rmap = JSONUtils.preprocess(response_result);
Map<String, JSONObject> golmap = JSONUtils.preprocess(response_golden);
for (String key : rmap.keySet()) {
JSONObject val = rmap.get(key);
String url = val.getString("value");
if (url.startsWith("http") && url.endsWith(key)) {
response_result.remove(golmap.get(key));
response_golden.remove(golmap.get(key));
}
}
JSONObject o = new JSONObject();
o.put("testing", testing);
switch (testing) {
case KEYVALUE:
o.put("result", compareKeyValues(response_result, response_golden));
break;
case STRICT:
o.put("result", compareStrict(response_result, response_golden));
break;
case KEYSONLY:
o.put("result", compareKeys(response_result, response_golden));
break;
case NONE:
o.put("result", "no comparison tests.");
break;
default:
o.put("result", "unknown testing config: " + testing);
}
return o;
}
// static void download(JSONObject resp, File resultFolder, Client client) throws Exception {
// JSONArray arr = resp.getJSONArray("reports");
// // download files using the 'q' service.
// Map<String, JSONObject> rmap = JSONUtils.preprocess(arr);
// for (String key : rmap.keySet()) {
// JSONObject val = rmap.get(key);
// String url = val.getString("value");
// if (url == null) {
// throw new Exception("Expecting file in reports: " + key);
// }
// if (url.startsWith("http") && url.contains("/q/") && url.endsWith(key)) {
// if (!resultFolder.exists()) {
// resultFolder.mkdirs();
// }
// File outFile = new File(resultFolder, key);
// client.doGET(url, outFile);
// if (!outFile.exists()) {
// throw new Exception("Missing output file: " + key);
// }
// }
// }
// }
static JSONObject compareKeyValues(JSONArray res, JSONArray golden) throws Exception {
Map<String, JSONObject> pres = JSONUtils.preprocess(res);
Map<String, JSONObject> pgol = JSONUtils.preprocess(golden);
JSONObject r = new JSONObject();
for (String name : pgol.keySet()) {
if (!pres.containsKey(name)) {
r.accumulate("missing", name);
continue;
}
String resval = pres.get(name).get(ModelDataService.KEY_VALUE).toString();
String golval = pgol.get(name).get("value").toString();
if (resval.startsWith("http:") || resval.startsWith("https:") || resval.startsWith("file:")) {
r.accumulate("ignored", name);
continue;
}
try {
double dgolval = Double.parseDouble(resval);
double dresval = Double.parseDouble(golval);
if (dgolval != dresval) {
r.accumulate("differ", "'" + name + "': " + dgolval + " != " + dresval + " \n");
} else {
r.accumulate("ok", name);
}
} catch (NumberFormatException E) {
if (!golval.equals(resval)) {
r.accumulate("differ", "'" + name + "': '" + golval + "' != '" + resval + "' \n");
} else {
r.accumulate("ok", name);
}
}
}
return r;
}
static JSONObject compareKeys(JSONArray res, JSONArray golden) throws Exception {
JSONObject r = new JSONObject();
Map<String, JSONObject> pres = JSONUtils.preprocess(res);
Map<String, JSONObject> pgol = JSONUtils.preprocess(golden);
for (String key : pgol.keySet()) {
if (!pres.containsKey(key)) {
r.append("missing", key);
}
pres.remove(key);
}
if (!pres.keySet().isEmpty()) {
r.append("extra", pres.keySet().toString());
}
return r;
}
static JSONObject compareStrict(JSONArray res, JSONArray gol) throws Exception {
JSONObject o = new JSONObject();
try {
JSONAssert.assertEquals(gol.toString(), res.toString(), false);
o.put("result", "ok");
return o;
} catch (AssertionError E) {
o.put("differ", E.getMessage());
}
return o;
}
public static JSONArray run(Properties p, String... targets) throws Exception {
JSONArray summary = new JSONArray();
for (String target : targets) {
JSONObject result = new ServiceTest2(new File(target), p).test();
summary.put(result);
}
return summary;
}
static String getServicePath(String url) throws URISyntaxException {
// URIBuilder b = new URIBuilder("http://localhost:8080/csip-example/m/simpleservice/2.0");
URIBuilder b = new URIBuilder(url);
return b.getPath().substring(b.getPath().indexOf('/', 1));
}
public static void main(String[] args) throws Exception {
//// args = new String[]{"/od/projects/csip-all/csip-example/test/service_tests/all"};
// args = new String[]{"/od/tmp/csip-testing"};
// Properties config = new Properties();
// config.setProperty("concurrency", "1");
// config.setProperty("verify", "keyvalue");
//// config.setProperty("rounds", "3");
//// config.setProperty("delay", "3000");
// System.out.println(run(config, args).toString(4).replace("\\/", "/"));
}
}