Files
wlan-lanforge-scripts/gui/kpi.java
Ben Greear 99d5d77891 kpi: Fix slack integration.
Seems to be working now.

Signed-off-by: Ben Greear <greearb@candelatech.com>
2020-08-19 09:08:06 -07:00

1387 lines
46 KiB
Java

//
// LANforge-GUI Source Code
// Copyright (C) 1999-2020 Candela Technologies Inc
// http://www.candelatech.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// Contact: Candela Technologies <support@candelatech.com> if you have any
// questions.
//
import java.text.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.nio.file.*;
/** Process a set of test results generated by the CI/CD process for a test type in a single testbed.
* There are currently no automated comparisons done across different testbeds.
* Generate historical graphs and links to each test run.
* Each test results directory would be packaged up by the lf_gui_report_summary.pl script,
* called by the basic_regression.bash automated regression test script.
* Example: java kpi --dir /var/www/html/tip/testbeds/ferndale-basic-01/reports/basic
*/
public class kpi {
String lc_osname;
String home_dir;
static final String out_sep = "\t";
static final String in_sep = "\t";
public static int PRIORITY_IDX = 6;
public static int TEST_ID_IDX = 7;
public static int SHORT_DESC_IDX = 8;
public static int PASS_FAIL_IDX = 9;
public static int NUMERIC_SCORE_IDX = 10;
public static int NOTES_IDX = 11;
public static int UNITS_IDX = 12;
public static int GRAPH_GROUP_IDX = 13;
public static int SUBTEST_PASS_IDX = 14;
public static int SUBTEST_FAIL_IDX = 15;
public static String TESTBED_TEMPLATE = "testbed_template.html";
public static String AP_AUTO_BASIC_CX = "ap_auto_basic_cx";
public static String HTML_COLOR_PASS = "#bee7aa";
public static String HTML_COLOR_FAIL = "#f9c5c0";
public static String HTML_COLOR_WARNING = "#f8f6ad";
public static String PF = "PASS-FAIL";
public kpi() {
priv_init();
}
static String yellowTd(String txt) {
return "<td><span style='background:" + HTML_COLOR_WARNING + ";color:black;'>" + txt + "</span></td>";
}
static String redTd(String txt) {
return "<td><span style='background:" + HTML_COLOR_FAIL + ";color:black;'>" + txt + "</span></td>";
}
static String greenTd(String txt) {
return "<td><span style='background:" + HTML_COLOR_PASS + ";color:black;'>" + txt + "</span></td>";
}
public boolean is_mac() {
return lc_osname.startsWith("mac os x");
}
public boolean is_win() {
return lc_osname.startsWith("windows");
}
public boolean is_linux() {
return lc_osname.startsWith("linux");
}
private void priv_init() {
lc_osname = System.getProperty("os.name").toLowerCase();
home_dir = System.getProperty("user.home");
}
public static void main(String[] args) {
kpi k = new kpi();
try {
k.work(args);
}
catch (Exception ee) {
ee.printStackTrace();
}
}
public static String toStringNum(double i, int precision) {
NumberFormat nf = NumberFormat.getInstance();
if (0.0 == i) {
// Don't pad precision on '0', just adds needless clutter.
return nf.format(i);
}
else {
nf = (NumberFormat)(nf.clone());
nf.setMaximumFractionDigits(precision);
nf.setMinimumFractionDigits(precision);
return nf.format(i);
}
}
public void work(String[] args) {
String dir = null;
String results_url = "";
String caseid = "";
String slack_fname = "";
String testbed_name = "LANforge CICD";
for (int i = 0; i<args.length; i++) {
if (args[i].equals("--dir")) {
dir = args[i+1];
}
else if (args[i].equals("--caseid")) {
caseid = args[i+1];
}
else if (args[i].equals("--results_url")) {
results_url = args[i+1];
}
else if (args[i].equals("--slack_fname")) {
slack_fname = args[i+1];
}
else if (args[i].equals("--testbed_name")) {
testbed_name = args[i+1];
}
else if (args[i].equals("--help") || args[i].equals("-h") || args[i].equals("-?")) {
System.out.println("Usage: $0 --dir /path/to/test-collection\n" +
"--caseid [case-id]\n" +
"--results_url [url]\n" +
"--slack_fname [slack url webhook file name]\n" +
"--testbed_name [My Testbed]");
System.exit(0);
}
}
Hashtable<String, String> test_names = new Hashtable();
Vector<String> test_namesv = new Vector();
Vector<Run> runs = new Vector();
test_names.put(PF, PF);
test_namesv.add(PF);
try {
DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(dir));
for (Path file: stream) {
File f = file.toFile(); // this is the test run dir
// Inside of it is individual tests.
if (!f.isDirectory()) {
continue;
}
//System.out.println("Checking sub-directory/file (run): " + f.getAbsolutePath());
DirectoryStream<Path> stream2 = Files.newDirectoryStream(file);
Run run = null;
for (Path file2: stream2) {
File f2 = file2.toFile(); // this is the test case dir in the test run
//System.out.println("Checking test-case directory/file: " + f2.getAbsolutePath());
// Directory full of test results?
if (f2.isDirectory()) {
DirectoryStream<Path> stream3 = Files.newDirectoryStream(file2);
Test test = new Test(f2.getName());
if (run == null) {
run = new Run(f.getName());
runs.add(run);
}
System.out.println("Run: " + run + " adding test: " + test);
run.addTest(test);
// TODO:
// Add log processing. In particular, look in console logs for things like:
// "ath10k_pci 0000:01:00.0: wmi command 36967 timeout, restarting hardware"
// "WARNING: CPU: 0 PID: 8907 at backports-4.
for (Path file3: stream3) {
File f3 = file3.toFile();
String fname = f3.getName();
if (fname.equals("kpi.csv")) {
try {
BufferedReader br = new BufferedReader(new FileReader(f3));
if (test_names.get(f2.getName()) == null) {
test_names.put(f2.getName(), f2.getName());
test_namesv.add(f2.getName());
}
String line;
while ((line = br.readLine()) != null) {
test.addLine(line);
}
}
catch (FileNotFoundException enf) {
// ignore
}
catch (Exception e) {
e.printStackTrace();
}
}// if kpi.csv
else if (fname.startsWith("kpi-") && fname.endsWith(".png")) {
test.addKpiImage(fname);
}
else if (fname.equals("logs")) {
File logs_csv = new File(f3.getAbsolutePath() + File.separator + "logs.csv");
if (logs_csv.exists()) {
try {
BufferedReader br = new BufferedReader(new FileReader(logs_csv));
String line;
while ((line = br.readLine()) != null) {
test.addLogCsv(line);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}// for all files in the test dir
}
}
}
} catch (IOException | DirectoryIteratorException x) {
// IOException can never be thrown by the iteration.
// In this snippet, it can only be thrown by newDirectoryStream.
System.err.println(x);
}
// Sort runs so that earliest is first.
class SortbyDate implements Comparator<Run> {
// Used for sorting in ascending order of
// roll number
public int compare(Run a, Run b) {
long c = a.getDateMs() - b.getDateMs();
if (c < 0)
return -1;
if (c > 0)
return 1;
return 0;
}
}
runs.sort(new SortbyDate());
// Create meta-test results for each run.
for (int i = 0; i<runs.size(); i++) {
Run run = runs.elementAt(i);
Test t = new Test(PF);
run.addTest(t);
// Build fake kpi title and add that,
// then add fake kpi results line
// Example lines from a kpi.csv
//Date test-rig dut-hw-version dut-sw-version dut-model-num dut-serial-num test-priority test-id short-description pass/fail numeric-score test details Units Graph-Group Subtest-Pass Subtest-Fail
//1590190791848 Ferndale-01-Basic Linksys-EA8300 d88ea40 Linksys-EA8300 90 AP Auto Basic Client Connectivity Dual-Band MIN 8.0 Minimum CX time (ms) Time (ms) CX-Time 0 0
t.addLine("Date test-rig dut-hw-version dut-sw-version dut-model-num dut-serial-num test-priority test-id short-description pass/fail numeric-score test details Units Graph-Group Subtest-Pass Subtest-Fail");
t.addLine(run.getDateMs() + kpi.in_sep + run.getTestRig() + kpi.in_sep + run.getDutHwVer() + kpi.in_sep + run.getDutSwVer()
+ kpi.in_sep + run.getDutModelNum() + kpi.in_sep + run.getDutSerNum() + kpi.in_sep + "100" + kpi.in_sep + PF
+ kpi.in_sep + "Total Pass" + kpi.in_sep + kpi.in_sep + run.getPass() + kpi.in_sep + "Total passing tests and sub-tests for this run."
+ kpi.in_sep + "Number" + kpi.in_sep + PF + kpi.in_sep + "0" + kpi.in_sep + "0");
t.addLine(run.getDateMs() + kpi.in_sep + run.getTestRig() + kpi.in_sep + run.getDutHwVer() + kpi.in_sep + run.getDutSwVer()
+ kpi.in_sep + run.getDutModelNum() + kpi.in_sep + run.getDutSerNum() + kpi.in_sep + "100" + kpi.in_sep + PF
+ kpi.in_sep + "Total Failures" + kpi.in_sep + kpi.in_sep + run.getFail() + kpi.in_sep + "Total failing tests and sub-tests for this run."
+ kpi.in_sep + "Number" + kpi.in_sep + PF + kpi.in_sep + "0" + kpi.in_sep + "0");
}
// Link to latest test run that has the test id
Hashtable<String, Run> test_id_links = new Hashtable();
// We have read everything into memory.
// For each test, generate data over time.
Hashtable<String, History> hist_data = new Hashtable();
Vector<History> hist_datav = new Vector();
for (String tname: test_namesv) {
System.out.println("Checking testname: " + tname + " runs.size: " + runs.size());
// For each test, find all runs that have this test and consolidate data
for (int i = 0; i<runs.size(); i++) {
Run run = runs.elementAt(i);
Test t = run.findTest(tname);
System.out.println("Run [" + i + "] Test: " + t);
if (t != null) {
test_id_links.put(tname, run);
try {
History hist = hist_data.get(tname);
if (hist == null) {
hist = new History(tname);
hist_data.put(tname, hist);
hist_datav.add(hist);
System.out.println("Adding history datav, tname: " + tname);
}
for (int z = 0; z<t.data.size(); z++) {
Row r = t.data.elementAt(z);
HistRow csv = hist.findRow(r.getShortDescKey());
if (csv == null) {
csv = new HistRow(r);
hist.addRow(csv);
System.out.println("Adding history row: " + r.getShortDescKey());
}
String score = t.data.elementAt(z).getScore();
csv.csv.append(i + kpi.out_sep + score + System.lineSeparator());
csv.scores.add(Double.valueOf(score));
}
}
catch (Exception eee) {
eee.printStackTrace();
}
}
}
}
StringBuffer scores = new StringBuffer();
StringBuffer plots = new StringBuffer();
StringBuffer groups = new StringBuffer();
StringBuffer runs_rows = new StringBuffer();
// For all per-test history, print out csv files
for (History hist : hist_datav) {
String hk = hist.getName();
Hashtable<String, Vector> groupsh = new Hashtable();
Vector<String> groupsn = new Vector();
System.out.println("Checking history-data: " + hk);
for (HistRow csv : hist.csv) {
String ck = csv.getName();
String title = csv.getTitle();
String units = csv.getUnits();
String g = csv.getGraphGroup();
System.out.println("csv name: " + ck);
if (!(g.equals("NA") || units.equals(""))) {
Vector<HistRow> gv = groupsh.get(g);
if (gv == null) {
gv = new Vector();
groupsh.put(g, gv);
groupsn.add(g);
}
gv.add(csv);
}
if (units.equals("NA") || units.equals("")) {
units = "Data";
}
try {
String cf = dir + File.separator + hk + "::" + ck + ".csv";
System.out.println("Creating gnuplot csv: " + cf);
FileWriter f = new FileWriter(cf);
f.write(csv.csv.toString());
f.close();
csv.setFname(cf);
ShellExec exec = new ShellExec(true, true);
int rv = exec.execute("gnuplot", null, true,
"-e", "set ylabel \"" + units + "\"",
"-e", "filename='" + cf + "'",
"-e", "set title '" + title + "'",
"default.plot");
if (rv != 0) {
System.out.println("gnuplot for filename: " + cf + " rv: " + rv);
System.out.println(exec.getOutput());
System.out.println(exec.getError());
}
File png = new File("plot.png");
if (!png.exists()) {
System.out.println("ERROR: File: " + png + " does not exist.");
}
String npng = hk + "::" + ck + ".png";
String nplot_name = dir + File.separator + npng;
System.out.println("Rename plot: " + png + " to: " + nplot_name);
File nf = new File(nplot_name);
Path nfp = Files.move(png.toPath(), nf.toPath(), StandardCopyOption.REPLACE_EXISTING);//boolean renamed = png.renameTo(nf);
if (!nf.exists()) {
System.out.println("ERROR: New file: " + nf + " does not exist.");
}
exec = new ShellExec(true, true);
rv = exec.execute("gnuplot", null, true, "-e", "filename='" + cf + "'",
"mini.plot");
if (rv != 0) {
System.out.println("mini gnuplot for filename: " + cf + " rv: " + rv);
System.out.println(exec.getOutput());
System.out.println(exec.getError());
}
png = new File("plot.png");
String npngt = hk + "::" + ck + "-thumb.png";
Files.move(png.toPath(), new File(dir + File.separator + npngt).toPath(), StandardCopyOption.REPLACE_EXISTING);
String hk_str = hk;
Run last = test_id_links.get(hk);
if (!hk.equals(PF)) {
if (last != null) {
hk_str = "<a href=\"" + last.getName() + "/" + hk + "/index.html\">" + hk + "</a>";
}
}
StringBuffer change = new StringBuffer();
Double cur = csv.scores.elementAt(csv.scores.size() - 1);
change.append("Last Value: " + toStringNum(cur, 2));
if (csv.scores.size() > 1) {
Double prev = csv.scores.elementAt(csv.scores.size() - 2);
change.append("<br>Delta for last run: " + toStringNum((cur - prev), 2));
if (cur != 0) {
change.append("<br>Percentage change: " + toStringNum(100.0 * ((cur - prev) / cur), 2) + "%");
}
}
String row_str = ("<tr><td>" + hk_str + "</td><td>" + title + "</td><td><a href=\"./" + npng + "\"><img src=\"./" + npngt + "\"></a></td><td>"
+ change + "</td></tr>\n");
if (csv.getPriority() >= 100) {
scores.append(row_str);
}
else {
plots.append(row_str);
}
}
catch (Exception ee) {
ee.printStackTrace();
}
}
// Graph groups
for (String g : groupsn) {
Vector<HistRow> datasets = groupsh.get(g);
if (datasets.size() < 2) {
continue; // group of 1 doesn't count
}
HistRow csv0 = datasets.elementAt(0);
String title = g;
String units = csv0.getUnits();
// Don't group scores
if (units.equalsIgnoreCase("Score")) {
continue;
}
if (units.equals("NA") || units.equals("")) {
units = "Data";
}
System.out.println("title: " + title + " units: " + units);
try {
StringBuffer plot = new StringBuffer("plot ");
StringBuffer mplot = new StringBuffer("plot ");
boolean first = true;
for (HistRow csv: datasets) {
if (!first) {
plot.append(", ");
mplot.append(", ");
}
plot.append("\'" + csv.getFname() + "\' using 1:2 with lines title \'" + csv.getName().replace("_", " ") + "\'");
mplot.append("\'" + csv.getFname() + "\' using 1:2 with lines notitle");
first = false;
}
BufferedReader br = new BufferedReader(new FileReader("default_group.plot"));
FileWriter f = new FileWriter("tmp.plot");
String line;
while ((line = br.readLine()) != null) {
f.write(line);
f.write(System.lineSeparator());
}
f.write(plot.toString());
f.write(System.lineSeparator());
f.close();
br.close();
//System.out.println("group plot: " + plot);
ShellExec exec = new ShellExec(true, true);
int rv = exec.execute("gnuplot", null, true,
"-e", "set ylabel '" + units + "'",
"-e", "set title '" + title + "'",
"tmp.plot");
if (rv != 0) {
System.out.println("gnuplot for group: " + title + " rv: " + rv);
System.out.println(exec.getOutput());
System.out.println(exec.getError());
}
File png = new File("plot.png");
String npng = hk + "::" + g + ".png";
Files.move(png.toPath(), new File(dir + File.separator + npng).toPath(), StandardCopyOption.REPLACE_EXISTING);
br = new BufferedReader(new FileReader("mini_group.plot"));
f = new FileWriter("tmp.plot");
while ((line = br.readLine()) != null) {
f.write(line);
f.write(System.lineSeparator());
}
f.write(mplot.toString());
f.write(System.lineSeparator());
f.close();
br.close();
exec = new ShellExec(true, true);
rv = exec.execute("gnuplot", null, true,
"tmp.plot");
if (rv != 0) {
System.out.println("mini gnuplot for group: " + title + " rv: " + rv);
System.out.println(exec.getOutput());
System.out.println(exec.getError());
}
png = new File("plot.png");
String npngt = hk + "::" + g + "-thumb.png";
Files.move(png.toPath(), new File(dir + File.separator + npngt).toPath(), StandardCopyOption.REPLACE_EXISTING);
String hk_str = hk;
Run last = test_id_links.get(hk);
if (!hk.equals(PF)) {
if (last != null) {
hk_str = "<a href=\"" + last.getName() + "/" + hk + "/index.html\">" + hk + "</a>";
}
}
groups.append("<tr><td>" + hk_str + "</td><td>" + title + "</td><td><a href=\"./" + npng + "\"><img src=\"./" + npngt + "\"></a></td></tr>\n");
}
catch (Exception ee) {
ee.printStackTrace();
}
}
}
String test_bed = "Test Bed";
String last_run = "";
String last_run_kpi_images = "";
StringBuffer pngs = new StringBuffer();
boolean cp = true;
for (int i = 0; i<runs.size(); i++) {
Run run = runs.elementAt(i);
test_bed = run.getTestRig();
StringBuffer logs_str = new StringBuffer();
int logv = run.getLogBugs();
if (logv > 0) {
logs_str.append(redTd(logv + ""));
}
else {
logs_str.append(greenTd(logv + ""));
}
logv = run.getLogWarnings();
if (logv > 0) {
logs_str.append(redTd(logv + ""));
}
else {
logs_str.append(greenTd(logv + ""));
}
logv = run.getLogCrashes();
if (logv > 0) {
logs_str.append(redTd(logv + ""));
}
else {
logs_str.append(greenTd(logv + ""));
}
logv = run.getLogRestarting();
if (logv > 0) {
logs_str.append(redTd(logv + ""));
}
else {
logs_str.append(greenTd(logv + ""));
}
String row_text = ("<tr><td>" + i + "</td><td><a href=\"" + run.getName() + "/index.html\">" + run.getName() + "</a></td><td>" + run.getDate()
+ "</td><td>" + run.getDutHwVer() + "</td><td>" + run.getDutSwVer()
+ "</td><td>" + run.getDutModelNum() + "</td>" + greenTd(run.getPass() + "") + redTd(run.getFail() + "") + logs_str + "</tr>\n");
if (i == (runs.size() - 1)) {
// Last run
int png_row_count = 0;
boolean needs_tr = true;
last_run = row_text;
for (Test t : run.testsv) {
for (String png : t.kpi_images) {
if (png.indexOf("-print") >= 0) {
continue; // skip the print variants of the image.
}
if (needs_tr) {
pngs.append("<tr>");
needs_tr = false;
}
String img_title = "Test: " + t.getName();
String fname = run.getName() + "/" + t.getName() + "/" + png;
pngs.append("<td><a href=\"" + fname + "\"><img src=\"" + fname + "\" style='width:400px;max-width:400px' title=\""
+ img_title + "\"></a></td>\n");
png_row_count++;
if (png_row_count == 2) {
png_row_count = 0;
pngs.append("</tr>\n");
needs_tr = true;
}
}
}
if ((!needs_tr) && pngs.length() > 0) {
pngs.append("</tr>\n");
}
String testrails_msg = ("MSG Run: " + run.getName() + " Date: " + run.getDate() + " DUT-HW: " + run.getDutHwVer() + "DUT-SW: " + run.getDutSwVer()
+ " Passed: " + run.getPass() + " Fail: " + run.getFail()
+ " Log-Bugs: " + run.getLogBugs() + " Log-Warnings: " + run.getLogWarnings()
+ " Log-Crashes: " + run.getLogCrashes() + " Log-Restarting: " + run.getLogRestarting());
if (!caseid.equals("")) {
try {
// Create file for propagating results into external tool
String ofile = dir + File.separator + "testrails.txt";
BufferedWriter bw = new BufferedWriter(new FileWriter(ofile));
bw.write("CASE_ID " + caseid);
bw.write(System.lineSeparator());
bw.write("RUN_ID " + i);
bw.write(System.lineSeparator());
bw.write(testrails_msg);
bw.write(System.lineSeparator());
bw.write("MSG URL: " + results_url + "/" + run.getName());
bw.write(System.lineSeparator());
bw.close();
System.out.println("See testrails results: " + ofile);
}
catch (Exception eee) {
eee.printStackTrace();
}
}
if (!slack_fname.equals("")) {
String slack_content_type = "Content-type: application/json";
String slack_msg = "{\"text\":\"<" + results_url + "|" + testbed_name + ">"
+ " Results: <" + results_url + "/" + run.getName() + "|" + run.getName() + ">"
+ " Errors: " + (run.getLogCrashes() + run.getLogRestarting() + run.getLogBugs()) + " Warnings: " + run.getLogWarnings()
+ "\"}";
try {
BufferedReader br = new BufferedReader(new FileReader(slack_fname));
String slack = br.readLine();
String cmd = "curl -X POST -H '" + slack_content_type + "' --data '" + slack_msg + "' " + slack;
System.out.println(cmd);
ShellExec exec = new ShellExec(true, true);
int rv = exec.execute("curl", null, true,
"-X", "POST",
"-H", slack_content_type,
"--data", slack_msg, slack);
if (rv != 0) {
System.out.println("curl slack post of msg: " + slack_msg + " to: " + slack + " failed.\n");
System.out.println(exec.getOutput());
System.out.println(exec.getError());
}
}
catch (Exception eeee) {
System.out.println("slack_msg: " + slack_msg + " slack_fname: " + slack_fname);
eeee.printStackTrace();
}
}
}
runs_rows.append(row_text);
if (cp) {
try {
String fname;
copy("CandelaLogo2-90dpi-200x90-trans.png", dir + File.separator + run.getName(), dir);
copy("candela_swirl_small-72h.png", dir + File.separator + run.getName(), dir);
copy("canvil.ico", dir + File.separator + run.getName(), dir);
copy("custom.css", dir + File.separator + run.getName(), dir);
copy("report.css", dir + File.separator + run.getName(), dir);
cp = false;
}
catch (Exception ee) {
ee.printStackTrace();
}
}
}
try {
// Read in the testbed_template.html and update it with our info
BufferedReader br = new BufferedReader(new FileReader(new File(kpi.TESTBED_TEMPLATE)));
String ofile = dir + File.separator + "index.html";
BufferedWriter bw = new BufferedWriter(new FileWriter(ofile));
String line;
while ((line = br.readLine()) != null) {
line = line.replace("___TITLE___", test_bed + " Report History");
line = line.replace("___SCORE_RUNS___", scores.toString());
line = line.replace("___GROUP_GRAPHS___", groups.toString());
line = line.replace("___DATA_GRAPHS___", plots.toString());
line = line.replace("___TEST_RUNS___", runs_rows.toString());
line = line.replace("___LATEST_RUN___", last_run);
line = line.replace("___LATEST_RUN_PNGS___", pngs.toString());
bw.write(line);
bw.write(System.lineSeparator());
}
br.close();
bw.close();
System.out.println("See " + ofile);
}
catch (Exception eee) {
eee.printStackTrace();
}
} // ~work()
public void copy(String fname, String from, String to) throws Exception {
Path fromp = Paths.get(from + File.separator + fname);
Path top = Paths.get(to + File.separator + fname);
Files.copy(fromp, top, StandardCopyOption.REPLACE_EXISTING);
}
}
/** This holds a datapoint for a particular test result for each of the Runs.
* This is used to generate historical graphs and comparisons.
* If a test generates two KPI results, there will be two HistRows for that
* test case.
*/
class HistRow {
String fname;
String name;
String title = "";
String units = "";
String graph_group = "";
int prio = 0;
StringBuffer csv = new StringBuffer();
Vector<Double> scores = new Vector();
public HistRow(Row r) {
name = r.getShortDescKey();
title = r.getShortDesc();
units = r.getUnits();
graph_group = r.getGraphGroup();
prio = r.getPriority();
}
int getPriority() {
return prio;
}
String getFname() {
return fname;
}
void setFname(String s) {
fname = s;
}
String getName() {
return name;
}
String getTitle() {
return title;
}
String getUnits() {
return units;
}
String getGraphGroup() {
return graph_group;
}
}
/** This holds all HistRow objects for each test name for each run.
* It is used for generating historical graphs and comparisons.
*/
class History {
String name;
Vector<HistRow> csv = new Vector();
Hashtable<String, HistRow> csvh = new Hashtable(); // lookup by name
public History(String n) {
name = n;
}
public String getName() {
return name;
}
HistRow findRow(String n) {
//System.out.println("findCsv, n: " + n);
return csvh.get(n);
}
void addRow(HistRow r) {
csv.add(r);
csvh.put(r.getName(), r);
}
}
/** A Row represents a single KPI csv data point. The csv is split into tokens
* for easier manipulation by other code.
*/
class Row {
Vector<String> rdata = new Vector();
String short_desc_key = null;
String getNotes() {
return rdata.elementAt(kpi.NOTES_IDX);
}
String getSubtestPassed() {
if (rdata.size() > kpi.SUBTEST_PASS_IDX)
return rdata.elementAt(kpi.SUBTEST_PASS_IDX);
return "";
}
String getSubtestFailed() {
if (rdata.size() > kpi.SUBTEST_FAIL_IDX)
return rdata.elementAt(kpi.SUBTEST_FAIL_IDX);
return "";
}
String getUnits() {
try {
return rdata.elementAt(kpi.UNITS_IDX);
}
catch (Exception e) {
return "";
}
}
String getGraphGroup() {
try {
return rdata.elementAt(kpi.GRAPH_GROUP_IDX);
}
catch (Exception e) {
return "";
}
}
int getPriority() {
try {
return Long.valueOf(rdata.elementAt(kpi.PRIORITY_IDX)).intValue();
}
catch (Exception e) {
return 0;
}
}
String getPassFail() {
return rdata.elementAt(kpi.PASS_FAIL_IDX);
}
String getScore() {
return rdata.elementAt(kpi.NUMERIC_SCORE_IDX);
}
String getShortDesc() {
return rdata.elementAt(kpi.SHORT_DESC_IDX);
}
String getTestId() {
return rdata.elementAt(kpi.TEST_ID_IDX);
}
String getShortDescKey() {
return short_desc_key;
}
void setShortDescKey(String s) {
short_desc_key = s;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Row " + getShortDescKey() + " ");
for (int i = 0; i<rdata.size(); i++) {
sb.append("[" + i + "] == " + rdata.elementAt(i) + " ");
}
return sb.toString();
}
}
/** A test contains information on one executation of one test. For instance,
* a wifi-capacity-test would be a single test. It may create a KPI.csv file with
* multiple rows. It may also have logs for this test run, and KPI graph images.
* A Run consists of multiple tests.
*/
class Test {
String name;
Vector<String> titles = null;
Vector<Row> data = new Vector();
Hashtable<String, String> descs = new Hashtable();
Vector<String> kpi_images = new Vector();
Vector<String> log_csv = new Vector();
int pass = 0;
int fail = 0;
int log_bugs = 0;
int log_warnings = 0;
int log_crashes = 0;
int log_restarting = 0;
long date_ms = 0;
public String date = "NA";
public String test_rig = "NA";
public String dut_hw_version = "NA";
public String dut_sw_version = "NA";
public String dut_model_num = "NA";
public String dut_serial_num = "NA";
public Test(String n) {
name = n;
}
public int getLogBugs() {
return log_bugs;
}
public int getLogWarnings() {
return log_warnings;
}
public int getLogCrashes() {
return log_crashes;
}
public int getLogRestarting() {
return log_restarting;
}
public String toString() {
return "Name: " + name;
}
public int getPass() {
return pass;
}
public int getFail() {
return fail;
}
void addKpiImage(String s) {
kpi_images.add(s);
}
long getDateMs() {
return date_ms;
}
String getTestRig() {
return test_rig;
}
String getDutHwVer() {
return dut_hw_version;
}
String getDutSwVer() {
return dut_sw_version;
}
String getDutSerialNum() {
return dut_serial_num;
}
String getDutModelNum() {
//System.out.println("Test: " + getName() + " model-num: " + dut_model_num);
return dut_model_num;
}
String getDate() {
return date;
}
String getName() {
return name;
}
void addLogCsv(String l) {
log_csv.add(l);
try {
StringTokenizer st = new StringTokenizer(l, "\t");
String tok = st.nextToken();
if (tok.equals("FILE")) {
// title, ignore rest of this title
return;
}
log_bugs += Long.valueOf(st.nextToken());
log_warnings += Long.valueOf(st.nextToken());
log_crashes += Long.valueOf(st.nextToken());
log_restarting += Long.valueOf(st.nextToken());
}
catch (Exception e) {
e.printStackTrace();
}
}
void addLine(String l) {
if (titles == null) {
titles = new Vector();
StringTokenizer st = new StringTokenizer(l, kpi.in_sep, true);
boolean last_was_sep = false;
while (st.hasMoreTokens()) {
String tok = st.nextToken();
if (tok.equals(kpi.in_sep)) {
if (last_was_sep) {
titles.add(new String());
}
last_was_sep = true;
}
else {
titles.add(tok);
last_was_sep = false;
}
}
}
else {
Row row = new Row();
data.add(row);
StringTokenizer st = new StringTokenizer(l, kpi.in_sep, true);
int idx = 0;
System.out.println("new line: " + l);
boolean last_was_sep = false;
while (st.hasMoreTokens()) {
String rtok = st.nextToken();
if (rtok.equals(kpi.in_sep)) {
if (last_was_sep) {
row.rdata.add(new String());
idx++;
}
last_was_sep = true;
}
else {
row.rdata.add(rtok);
idx++;
last_was_sep = false;
}
if ((data.size() >= 1) && (!last_was_sep) && dut_sw_version.equals("NA")) { // first row is being added
if (titles.elementAt(idx - 1).equalsIgnoreCase("test-rig")) {
test_rig = rtok;
}
else if (titles.elementAt(idx - 1).equalsIgnoreCase("dut-hw-version")) {
dut_hw_version = rtok;
}
else if (titles.elementAt(idx - 1).equalsIgnoreCase("dut-sw-version")) {
dut_sw_version = rtok;
}
else if (titles.elementAt(idx - 1).equalsIgnoreCase("dut-model-num")) {
dut_model_num = rtok;
}
else if (titles.elementAt(idx - 1).equalsIgnoreCase("Date")) {
//System.out.println("idx: " + idx + " rtok: " + rtok);
date_ms = Long.valueOf(rtok).longValue();
date = new Date(date_ms).toString();
}
}
//System.out.println("idx: " + idx);
}
//System.out.println("done tok reading loop");
String pf = row.getPassFail().toLowerCase();
if (pf.indexOf("pass") >= 0) {
pass++;
}
else if (pf.indexOf("fail") >= 0) {
fail++;
}
String spass = row.getSubtestPassed();
try {
if (!spass.equals("")) {
pass += Long.valueOf(spass).longValue();
}
}
catch (Exception ee) {
ee.printStackTrace();
}
String sfail = row.getSubtestFailed();
try {
if (!sfail.equals("")) {
fail += Long.valueOf(sfail).longValue();
}
}
catch (Exception ee) {
ee.printStackTrace();
}
row.setShortDescKey(row.getShortDesc().replace(" ", "_"));
//System.out.println("Row: " + row);
descs.put(row.getShortDesc(), row.getShortDesc());
}
}//addline
}//Test
/** A Run is a collection of tests. This class encompasses the entire regression test
* for a particular flavor of test (basic, fast, etc).
* The CI/CD process will create one or more Runs per build artifact per testbed.
*/
class Run {
String name;
Hashtable<String, Test> tests = new Hashtable();
Vector<Test> testsv = new Vector();
public Run(String n) {
name = n;
}
public String toString() {
return "Name: " + name;
}
int getPass() {
int pass = 0;
for (Test t: testsv) {
pass += t.getPass();
}
return pass;
}
int getFail() {
int fail = 0;
for (Test t: testsv) {
fail += t.getFail();
}
return fail;
}
int getLogBugs() {
int fail = 0;
for (Test t: testsv) {
fail += t.getLogBugs();
}
return fail;
}
int getLogWarnings() {
int fail = 0;
for (Test t: testsv) {
fail += t.getLogWarnings();
}
return fail;
}
int getLogCrashes() {
int fail = 0;
for (Test t: testsv) {
fail += t.getLogCrashes();
}
return fail;
}
int getLogRestarting() {
int fail = 0;
for (Test t: testsv) {
fail += t.getLogRestarting();
}
return fail;
}
Test getFirstTest() {
return testsv.elementAt(0);
}
String getDate() {
Test t = getFirstTest();
if (t != null)
return t.getDate();
return "";
}
long getDateMs() {
Test t = getFirstTest();
if (t != null)
return t.getDateMs();
return 0;
}
String getTestRig() {
Test t = getFirstTest();
if (t != null)
return t.getTestRig();
return "";
}
String getDutHwVer() {
Test t = getFirstTest();
if (t != null)
return t.getDutHwVer();
return "";
}
String getDutSwVer() {
Test t = getFirstTest();
if (t != null)
return t.getDutSwVer();
return "";
}
String getDutSerNum() {
Test t = getFirstTest();
if (t != null)
return t.getDutSerialNum();
return "";
}
String getDutModelNum() {
Test t = getFirstTest();
if (t != null)
return t.getDutModelNum();
return "";
}
String getName() {
return name;
}
void addTest(Test t) {
tests.put(t.getName(), t);
testsv.add(t);
}
Test findTest(String n) {
return tests.get(n);
}
}//Run
// From: https://stackoverflow.com/questions/882772/capturing-stdout-when-calling-runtime-exec
/**
* Execute external process and optionally read output buffer.
*/
class ShellExec {
private int exitCode;
private boolean readOutput, readError;
private StreamGobbler errorGobbler, outputGobbler;
public ShellExec() {
this(false, false);
}
public ShellExec(boolean readOutput, boolean readError) {
this.readOutput = readOutput;
this.readError = readError;
}
/**
* Execute a command.
* @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh")
* @param workdir working directory or NULL to use command folder
* @param wait wait for process to end
* @param args 0..n command line arguments
* @return process exit code
*/
public int execute(String command, String workdir, boolean wait, String...args) throws IOException {
String[] cmdArr;
StringBuffer dbg = new StringBuffer();
dbg.append("Command: " + command);
if (args != null && args.length > 0) {
cmdArr = new String[1+args.length];
cmdArr[0] = command;
System.arraycopy(args, 0, cmdArr, 1, args.length);
for (int i = 0; i<args.length; i++) {
dbg.append(" " + args[i]);
}
} else {
cmdArr = new String[] { command };
}
System.out.println("Running cmd: " + dbg.toString());
ProcessBuilder pb = new ProcessBuilder(cmdArr);
File workingDir = (workdir==null ? new File(command).getParentFile() : new File(workdir) );
pb.directory(workingDir);
Process process = pb.start();
// Consume streams, older jvm's had a memory leak if streams were not read,
// some other jvm+OS combinations may block unless streams are consumed.
errorGobbler = new StreamGobbler(process.getErrorStream(), readError);
outputGobbler = new StreamGobbler(process.getInputStream(), readOutput);
errorGobbler.start();
outputGobbler.start();
exitCode = 0;
if (wait) {
try {
process.waitFor();
exitCode = process.exitValue();
} catch (InterruptedException ex) { }
}
return exitCode;
}
public int getExitCode() {
return exitCode;
}
public boolean isOutputCompleted() {
return (outputGobbler != null ? outputGobbler.isCompleted() : false);
}
public boolean isErrorCompleted() {
return (errorGobbler != null ? errorGobbler.isCompleted() : false);
}
public String getOutput() {
return (outputGobbler != null ? outputGobbler.getOutput() : null);
}
public String getError() {
return (errorGobbler != null ? errorGobbler.getOutput() : null);
}
//********************************************
//********************************************
/**
* StreamGobbler reads inputstream to "gobble" it.
* This is used by Executor class when running
* a commandline applications. Gobblers must read/purge
* INSTR and ERRSTR process streams.
* http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
*/
private class StreamGobbler extends Thread {
private InputStream is;
private StringBuilder output;
private volatile boolean completed; // mark volatile to guarantee a thread safety
public StreamGobbler(InputStream is, boolean readStream) {
this.is = is;
this.output = (readStream ? new StringBuilder(256) : null);
}
public void run() {
completed = false;
try {
String NL = System.getProperty("line.separator", "\r\n");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ( (line = br.readLine()) != null) {
if (output != null)
output.append(line + NL);
}
} catch (IOException ex) {
// ex.printStackTrace();
}
completed = true;
}
/**
* Get inputstream buffer or null if stream
* was not consumed.
* @return
*/
public String getOutput() {
return (output != null ? output.toString() : null);
}
/**
* Is input stream completed.
* @return
*/
public boolean isCompleted() {
return completed;
}
}
}