diff --git a/.gitattributes b/.gitattributes index b4a19905..382e93d0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -139,30 +139,39 @@ testresults/resources/schemas/dbscripts/postgresql/testresults-13.30-13.31.sql - testresults/resources/schemas/dbscripts/postgresql/testresults-13.31-13.32.sql -text testresults/resources/schemas/dbscripts/postgresql/testresults-13.32-13.33.sql -text testresults/resources/schemas/dbscripts/postgresql/testresults-13.33-13.34.sql -text +testresults/resources/schemas/dbscripts/postgresql/testresults-13.34-13.35.sql -text testresults/resources/schemas/dbscripts/sqlserver/testresults-13.20-13.21.sql -text testresults/resources/schemas/testresults.xml -text -testresults/src/org/labkey/testresults/RunDetail.java -text testresults/src/org/labkey/testresults/SendTestResultsEmail.java -text testresults/src/org/labkey/testresults/TRStats.java -text -testresults/src/org/labkey/testresults/TestFailDetail.java -text -testresults/src/org/labkey/testresults/TestLeakDetail.java -text -testresults/src/org/labkey/testresults/TestPassDetail.java -text testresults/src/org/labkey/testresults/TestResultsContainerListener.java -text testresults/src/org/labkey/testresults/TestResultsController.java -text testresults/src/org/labkey/testresults/TestResultsManager.java -text testresults/src/org/labkey/testresults/TestResultsModule.java -text testresults/src/org/labkey/testresults/TestResultsSchema.java -text testresults/src/org/labkey/testresults/TestResultsWebPart.java -text -testresults/src/org/labkey/testresults/TestsDataBean.java -text -testresults/src/org/labkey/testresults/User.java -text +testresults/src/org/labkey/testresults/model/BackgroundColor.java -text +testresults/src/org/labkey/testresults/model/RunDetail.java -text +testresults/src/org/labkey/testresults/model/TestFailDetail.java -text +testresults/src/org/labkey/testresults/model/TestHandleLeakDetail.java -text +testresults/src/org/labkey/testresults/model/TestLeakDetail.java -text +testresults/src/org/labkey/testresults/model/TestMemoryLeakDetail.java -text +testresults/src/org/labkey/testresults/model/TestPassDetail.java -text +testresults/src/org/labkey/testresults/model/User.java -text +testresults/src/org/labkey/testresults/view/LongTermBean.java -text +testresults/src/org/labkey/testresults/view/RunDownBean.java -text +testresults/src/org/labkey/testresults/view/TestsDataBean.java -text +testresults/src/org/labkey/testresults/view/errorFiles.jsp -text testresults/src/org/labkey/testresults/view/failureDetail.jsp -text testresults/src/org/labkey/testresults/view/flagged.jsp -text testresults/src/org/labkey/testresults/view/longTerm.jsp -text +testresults/src/org/labkey/testresults/view/multiFailureDetail.jsp -text testresults/src/org/labkey/testresults/view/runDetail.jsp -text testresults/src/org/labkey/testresults/view/rundown.jsp -text testresults/src/org/labkey/testresults/view/trainingdata.jsp -text testresults/src/org/labkey/testresults/view/user.jsp -text testresults/webapp/TestResults/css/style.css -text +testresults/webapp/TestResults/css/tablesorter-default.css -text testresults/webapp/TestResults/img/asc.gif -text testresults/webapp/TestResults/img/bg.gif -text testresults/webapp/TestResults/img/desc.gif -text diff --git a/testresults/resources/schemas/dbscripts/postgresql/testresults-13.34-13.35.sql b/testresults/resources/schemas/dbscripts/postgresql/testresults-13.34-13.35.sql new file mode 100644 index 00000000..83e5ffbd --- /dev/null +++ b/testresults/resources/schemas/dbscripts/postgresql/testresults-13.34-13.35.sql @@ -0,0 +1,18 @@ +ALTER TABLE testresults.testleaks RENAME TO memoryleaks; +ALTER TABLE testresults.memoryleaks ADD COLUMN type VARCHAR(200); + +CREATE TABLE testresults.handleleaks ( + id serial, + testrunid INTEGER NOT NULL, + testName VARCHAR(200) NOT NULL, + handles double precision DEFAULT 0.0, + type VARCHAR(200), + + + CONSTRAINT PK_memoryleaks PRIMARY KEY (id), + CONSTRAINT FK_memoryleaks_testruns FOREIGN KEY (testrunid) REFERENCES testresults.testruns(id) +); + +ALTER TABLE testresults.testpasses ADD COLUMN userandgdihandles INTEGER, + ADD COLUMN committedmemory INTEGER, + ADD COLUMN handles INTEGER; diff --git a/testresults/src/org/labkey/testresults/SendTestResultsEmail.java b/testresults/src/org/labkey/testresults/SendTestResultsEmail.java index b0f28ff4..8f1a207c 100644 --- a/testresults/src/org/labkey/testresults/SendTestResultsEmail.java +++ b/testresults/src/org/labkey/testresults/SendTestResultsEmail.java @@ -12,6 +12,13 @@ import org.labkey.api.util.Pair; import org.labkey.api.util.Path; import org.labkey.api.view.ActionURL; +import org.labkey.testresults.model.BackgroundColor; +import org.labkey.testresults.model.RunDetail; +import org.labkey.testresults.model.TestFailDetail; +import org.labkey.testresults.model.TestMemoryLeakDetail; +import org.labkey.testresults.model.TestMemoryLeakDetail; +import org.labkey.testresults.model.User; +import org.labkey.testresults.view.RunDownBean; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @@ -46,6 +53,10 @@ public SendTestResultsEmail() } + private String getBackgroundStyle(BackgroundColor color) { + return "background-color:" + color + ";"; + } + public Pair getHTMLEmail(org.labkey.api.security.User from) { // Sends email for all runs since 8:01 the previous morning, at 8am every morning Container parent = ContainerManager.getForPath(new Path(new String[]{"home","development"})); @@ -83,9 +94,9 @@ public Pair getHTMLEmail(org.labkey.api.security.User from) { User[] users = TestResultsController.getTrainingDataForContainer(container); - TestsDataBean data = new TestsDataBean(runs, users); + RunDownBean data = new RunDownBean(runs, users); Map> todaysFailures = data.getFailedTestsByDate(new Date(), true); - Map> todaysLeaks = data.getLeaksByDate(new Date(), true); + Map> todaysLeaks = data.getLeaksByDate(new Date(), true); User[] missingUsers = data.getMissingUsers(data.getRuns()); List nightsUsers = new ArrayList<>(); // build message as an HTML email message @@ -118,37 +129,50 @@ public Pair getHTMLEmail(org.labkey.api.security.User from) { totalPasses += run.getPassedtests(); nightsUsers.add(u); boolean isGoodRun = true; - String style = "background-color:#3EC23E;"; + boolean highlightDuration = false, highlightRuns = false, highlightMemory = false; + String style = getBackgroundStyle(BackgroundColor.pass); // ERROR: duration < 540, failures or leaks count > 0, more then 3 standard deviations away // WARNING: Between 2 and 3 standard deviations away // PASS: within 2 standard deviations away as well as not an ERROR or a WARNING int errorCount = errorRuns; - if (isGoodRun && (u.getMeanmemory() == 0d || u.getMeantestsrun() == 0d)) - { // IF NO TRAINING DATA FOR USER - style = "background-color:#cccccc;"; + if (isGoodRun && (u.getMeanmemory() == 0d || u.getMeantestsrun() == 0d || !u.isActive())) + { // IF NO TRAINING DATA FOR USER or INACTIVE + style = getBackgroundStyle(BackgroundColor.unknown); isGoodRun = false; } - if (run.getDuration() < 540 || run.getFailures().length > 0 || run.getLeaks().length > 0) + highlightDuration = run.getDuration() < 539; + if (highlightDuration || run.getFailures().length > 0 || run.getTestmemoryleaks().length > 0) { - style = "background-color:#F24E4E;"; + style = getBackgroundStyle(BackgroundColor.error); isGoodRun = false; errorRuns++; } - if (isGoodRun && (!u.fitsMemoryTrainingData(run.getAverageMemory(), 3) || !u.fitsRunCountTrainingData(run.getPassedtests(), 3))) + if (isGoodRun) { - style = "background-color:#F24E4E;"; - isGoodRun = false; - if (errorCount == errorRuns) - errorRuns++; + highlightMemory = !u.fitsMemoryTrainingData(run.getAverageMemory(), 3); + highlightRuns = !u.fitsRunCountTrainingData(run.getPassedtests(), 3); + if (highlightMemory || highlightRuns) + { + style = getBackgroundStyle(BackgroundColor.error); + isGoodRun = false; + if (errorCount == errorRuns) + errorRuns++; + } + else + { + highlightMemory = !u.fitsMemoryTrainingData(run.getAverageMemory(), 2); + highlightRuns = !u.fitsRunCountTrainingData(run.getPassedtests(), 2); + if (highlightMemory || highlightRuns) + { + style = getBackgroundStyle(BackgroundColor.warn); + isGoodRun = false; + warningRuns++; + } + } } - if (isGoodRun && (!u.fitsMemoryTrainingData(run.getAverageMemory(), 2) || !u.fitsRunCountTrainingData(run.getPassedtests(), 2))) + if(isGoodRun && run.hasHang()) { - style = "background-color:#F2C94E;"; - isGoodRun = false; - warningRuns++; - } - if(isGoodRun && run.hasHang()) { - style = "background-color:#F2C94E;"; + style = getBackgroundStyle(BackgroundColor.warn); isGoodRun = false; warningRuns++; } @@ -160,13 +184,13 @@ public Pair getHTMLEmail(org.labkey.api.security.User from) { message.append("\n" - + run.getUsername() + ""); - message.append("\n" + data.round(run.getAverageMemory(), 2) + ""); - message.append("\n" + run.getPassedtests() + ""); + + run.getUserName() + ""); + message.append("\n" + data.round(run.getAverageMemory(), 2) + ""); + message.append("\n" + run.getPassedtests() + ""); message.append("\n" + run.getPostTime() + ""); - message.append("\n" + run.getDuration() + ""); - message.append("\n" + run.getFailedtests() + ""); - message.append("\n" + run.getLeakedtests() + ""); + message.append("\n" + run.getDuration() + ""); + message.append("\n" + run.getFailedtests() + ""); + message.append("\n" + run.getLeakedtests() + ""); message.append(""); } } @@ -180,13 +204,9 @@ public Pair getHTMLEmail(org.labkey.api.security.User from) { for (User u : missingUsers) { message.append(""); - message.append("\nMissing " + u.getUsername() + ""); - message.append("\n"); - message.append("\n"); - message.append("\n"); - message.append("\n"); - message.append("\n"); - message.append("\n"); + message.append("\nMissing " + u.getUsername() + ""); + for (int i = 0; i < 6; i++) + message.append("\n"); message.append(""); } message.append("" + goodRuns + "/" + errorRuns + " (Pass/Fail)"); @@ -211,7 +231,7 @@ public Pair getHTMLEmail(org.labkey.api.security.User from) { addUser = true; } if (addUser) - message.append("\n" + run.getUsername() + ""); + message.append("\n" + run.getUserName() + ""); else noFailLeakRuns.add(run.getId()); @@ -244,7 +264,7 @@ public Pair getHTMLEmail(org.labkey.api.security.User from) { } message.append("\n"); } - for (Map.Entry> entry : todaysLeaks.entrySet()) + for (Map.Entry> entry : todaysLeaks.entrySet()) { message.append("\n"); message.append("\n" + entry.getKey() + ""); @@ -253,9 +273,9 @@ public Pair getHTMLEmail(org.labkey.api.security.User from) { if (noFailLeakRuns.contains(run.getId())) continue; message.append("\n"); - TestLeakDetail matchingLeak = null; + TestMemoryLeakDetail matchingLeak = null; - for (TestLeakDetail leak : entry.getValue()) + for (TestMemoryLeakDetail leak : entry.getValue()) { if (leak.getTestRunId() == run.getId()) { diff --git a/testresults/src/org/labkey/testresults/TestResultsController.java b/testresults/src/org/labkey/testresults/TestResultsController.java index 4b8774db..9d4d2ffb 100644 --- a/testresults/src/org/labkey/testresults/TestResultsController.java +++ b/testresults/src/org/labkey/testresults/TestResultsController.java @@ -15,6 +15,7 @@ */ package org.labkey.testresults; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.validator.routines.EmailValidator; @@ -38,6 +39,7 @@ import org.labkey.api.data.SqlSelector; import org.labkey.api.data.Table; import org.labkey.api.data.TableSelector; +import org.labkey.api.files.FileContentService; import org.labkey.api.query.FieldKey; import org.labkey.api.security.RequiresNoPermission; import org.labkey.api.security.RequiresPermission; @@ -46,11 +48,21 @@ import org.labkey.api.security.ValidEmail; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.util.FileUtil; import org.labkey.api.util.Pair; -import org.labkey.api.util.StringUtilsLabKey; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.api.view.ViewContext; +import org.labkey.testresults.model.RunDetail; +import org.labkey.testresults.model.TestFailDetail; +import org.labkey.testresults.model.TestHandleLeakDetail; +import org.labkey.testresults.model.TestMemoryLeakDetail; +import org.labkey.testresults.model.TestMemoryLeakDetail; +import org.labkey.testresults.model.TestPassDetail; +import org.labkey.testresults.model.User; +import org.labkey.testresults.view.LongTermBean; +import org.labkey.testresults.view.RunDownBean; +import org.labkey.testresults.view.TestsDataBean; import org.quartz.CronScheduleBuilder; import org.quartz.JobBuilder; import org.quartz.JobDetail; @@ -60,9 +72,11 @@ import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; +import org.springframework.util.StringUtils; import org.springframework.validation.BindException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartRequest; +import org.springframework.web.multipart.commons.CommonsMultipartFile; import org.springframework.web.servlet.ModelAndView; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -70,21 +84,22 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import javax.management.modelmbean.XMLParseException; import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.StringReader; -import java.text.DateFormat; +import java.nio.charset.Charset; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; -import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; @@ -105,12 +120,15 @@ public class TestResultsController extends SpringActionController { private static final Logger _log = Logger.getLogger(TestResultsController.class); + private static final SimpleDateFormat MDYFormat = new SimpleDateFormat("MM/dd/yyyy"); private static final DefaultActionResolver _actionResolver = new DefaultActionResolver(TestResultsController.class); + public TestResultsController() { setActionResolver(_actionResolver); } + public static final int POINT_RATIO = 30; /** @@ -122,7 +140,7 @@ public class BeginAction extends SimpleViewAction public ModelAndView getView(Object o, BindException errors) throws Exception { - TestsDataBean bean = getRunDownData(getUser(), getContainer(), getViewContext()); + RunDownBean bean = getRunDownBean(getUser(), getContainer(), getViewContext()); return new JspView("/org/labkey/testresults/view/rundown.jsp", bean); } @@ -133,16 +151,16 @@ public NavTree appendNavTrail(NavTree root) } // return TestDataBean specifically for rundown.jsp aka the home page of the module - public static TestsDataBean getRunDownData(org.labkey.api.security.User user, Container c, ViewContext viewContext) throws ParseException, IOException + public static RunDownBean getRunDownBean(org.labkey.api.security.User user, Container c, ViewContext viewContext) throws ParseException, IOException { String end = viewContext.getRequest().getParameter("end"); String viewType = viewContext.getRequest().getParameter("viewType"); - SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); boolean givenDate = end != null && !end.equals(""); Date endDate = new Date(); if(givenDate) - endDate = formatter.parse(end); + endDate = MDYFormat.parse(end); +// endDate = setDateToEightAM(endDate); Calendar cal = Calendar.getInstance(); cal.setTime(endDate); cal.set(Calendar.HOUR_OF_DAY, 8); @@ -173,7 +191,7 @@ public static TestsDataBean getRunDownData(org.labkey.api.security.User user, Co // show blank page if no runs exist if(todaysRuns.size() == 0 && monthRuns.size() == 0) - return new TestsDataBean(new RunDetail[0]); + return new RunDownBean(new RunDetail[0], new User[0]); RunDetail[] today = todaysRuns.toArray(new RunDetail[todaysRuns.size()]); if(todaysRuns.size() > 0) @@ -188,24 +206,33 @@ public static TestsDataBean getRunDownData(org.labkey.api.security.User user, Co ensureRunDataCached(allRuns, false); User[] users = getTrainingDataForContainer(c); - return new TestsDataBean(allRuns, users, viewType, null, endDate); + return new RunDownBean(allRuns, users, viewType, null, endDate); } private static String getViewType(org.labkey.api.security.User u, String viewType, String defaultViewType, String groupName, Container c) { PropertyManager.PropertyMap m = PropertyManager.getWritableProperties(u, c, TR_VIEW, true); boolean isValidView = isValidViewType(viewType); - if(!m.containsKey(groupName)) { - if(isValidView) - m.put(groupName, viewType); - else - m.put(groupName, defaultViewType); + if(groupName == null) { // no group and don't store setring in writable props + if(isValidView) { + return viewType; + } else { + return defaultViewType; + } } else { - if(isValidView) - m.put(groupName, viewType); - else - viewType = m.get(groupName); + if(!m.containsKey(groupName)) { + if(isValidView) + m.put(groupName, viewType); + else + m.put(groupName, defaultViewType); + } else { + if(isValidView) + m.put(groupName, viewType); + else + viewType = m.get(groupName); + } + m.save(); } - m.save(); + return viewType; } @@ -223,7 +250,7 @@ public static void ensureRunDataCached(RunDetail[] runs, boolean keepObjData) { { TestPassDetail[] passes = run.getPasses(); TestFailDetail[] failures = run.getFailures(); - TestLeakDetail[] leaks = run.getLeaks(); + TestMemoryLeakDetail[] leaks = run.getTestmemoryleaks(); if (passes == null) { passes = getPassesForRun(run); @@ -275,7 +302,7 @@ public static void ensureRunDataCached(RunDetail[] runs, boolean keepObjData) { if(!keepObjData) { run.setPasses(new TestPassDetail[0]); run.setFailures(new TestFailDetail[0]); - run.setLeaks(new TestLeakDetail[0]); + run.setTestmemoryleaks(new TestMemoryLeakDetail[0]); } } } @@ -283,31 +310,6 @@ public static void ensureRunDataCached(RunDetail[] runs, boolean keepObjData) { } } - - private static void populatePassCounts(List runIds, List runs) { - SQLFragment sqlFragment = new SQLFragment(); - sqlFragment.append("\tSELECT testpasses.testrunid, COUNT(*)\n" + - " \tFROM testresults.testpasses\n" + - " \tWHERE\n" + - " \t(testrunid= ANY(?))\n" + - " \tGROUP BY testrunid ;\n" + - " \t;"); - sqlFragment.add(runIds); - SqlSelector sqlSelector = new SqlSelector(TestResultsSchema.getSchema(), sqlFragment); - Map runPasses = new HashMap<>(); - sqlSelector.forEach(rs -> runPasses.put(rs.getInt("testrunid"), rs.getInt("count"))); - for(RunDetail run: runs) { - if(run.getPasses() == null || run.getPasses().length == 0) { - if(runPasses.get(run.getId()) != null) { - int passCount = runPasses.get(run.getId()); - run.setPasses(new TestPassDetail[passCount]); - } else { - run.setPasses(new TestPassDetail[0]); - } - } - } - } - public static User[] getTrainingDataForContainer(Container c) { User[] users = new TableSelector(TestResultsSchema.getInstance().getTableInfoUser(), null, null).getArray(User.class); SQLFragment sqlFragment = new SQLFragment(); @@ -340,7 +342,7 @@ public ModelAndView getView(Object o, BindException errors) throws Exception { List foundRuns = new ArrayList<>(); SQLFragment sqlFragment = new SQLFragment(); - sqlFragment.append("SELECT * FROM testresults.trainruns;"); + sqlFragment.append("SELECT * FROM " + TestResultsSchema.getTableInfoTrain() + ";"); SqlSelector sqlSelector = new SqlSelector(TestResultsSchema.getSchema(), sqlFragment); sqlSelector.forEach(rs -> foundRuns.add(rs.getInt("runid"))); SimpleFilter filter = new SimpleFilter(); @@ -373,7 +375,7 @@ public Object execute(Object o, BindException errors) throws Exception boolean train = Boolean.parseBoolean(getViewContext().getRequest().getParameter("train")); List foundRuns = new ArrayList<>(); SQLFragment sqlFragment = new SQLFragment(); - sqlFragment.append("SELECT * FROM testresults.trainruns "); + sqlFragment.append("SELECT * FROM " + TestResultsSchema.getTableInfoTrain() + " "); sqlFragment.append("WHERE runid = ?;"); sqlFragment.add(rowId); SqlSelector sqlSelector = new SqlSelector(TestResultsSchema.getSchema(), sqlFragment); @@ -382,20 +384,17 @@ public Object execute(Object o, BindException errors) throws Exception filter.addCondition(FieldKey.fromParts("id"), rowId); RunDetail[] details = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestRuns(), filter, null).getArray(RunDetail.class); if(details.length == 0) { - rowId = -1; // run Does not exist + return new ApiSimpleResponse("Success", false); // run Does not exist } - if(rowId == -1) { + if(train && foundRuns.size() > 0) { return new ApiSimpleResponse("Success", false); - } else if(train && foundRuns.size() > 0) { - return new ApiSimpleResponse("Success", false); - } else - { + } else { try (DbScope.Transaction transaction = TestResultsSchema.getSchema().getScope().ensureTransaction()) { if (train && foundRuns.size() == 0) { SQLFragment sqlFragmentInsert = new SQLFragment(); - sqlFragmentInsert.append("INSERT INTO testresults.trainruns (runid) VALUES (?);"); + sqlFragmentInsert.append("INSERT INTO " + TestResultsSchema.getTableInfoTrain() + " (runid) VALUES (?);"); sqlFragmentInsert.add(rowId); new SqlExecutor(TestResultsSchema.getSchema()).execute(sqlFragmentInsert); somethingChanged = true; @@ -403,7 +402,7 @@ public Object execute(Object o, BindException errors) throws Exception else if (!train && foundRuns.size() == 1) { SQLFragment sqlFragmentDelete = new SQLFragment(); - sqlFragmentDelete.append("DELETE FROM testresults.trainruns WHERE runid = ?;"); + sqlFragmentDelete.append("DELETE FROM " + TestResultsSchema.getTableInfoTrain() + " WHERE runid = ?;"); sqlFragmentDelete.add(rowId); new SqlExecutor(TestResultsSchema.getSchema()).execute(sqlFragmentDelete); somethingChanged = true; @@ -413,8 +412,8 @@ else if (!train && foundRuns.size() == 1) // UPDATE USER TABLE CALCULATIONS int userId = details[0].getUserid(); SQLFragment sqlFragmentUserRuns = new SQLFragment(); - sqlFragmentUserRuns.append("SELECT testruns.*, u.username, EXISTS(SELECT 1 FROM testresults.trainruns WHERE runid = testruns.id) AS traindata FROM testresults.testruns "); - sqlFragmentUserRuns.append("JOIN testresults.user AS u ON testruns.userid = u.id "); + sqlFragmentUserRuns.append("SELECT testruns.*, u.username, EXISTS(SELECT 1 FROM " + TestResultsSchema.getTableInfoTrain() + " WHERE runid = testruns.id) AS traindata FROM "+ TestResultsSchema.getTableInfoTestRuns() +" "); + sqlFragmentUserRuns.append("JOIN "+ TestResultsSchema.getTableInfoUser() +" AS u ON testruns.userid = u.id "); sqlFragmentUserRuns.append("WHERE userid = ? "); sqlFragmentUserRuns.add(userId); RunDetail[] userRuns = executeGetRunsSQLFragment(sqlFragmentUserRuns, getContainer(), false, false); @@ -426,7 +425,7 @@ else if (!train && foundRuns.size() == 1) } // See if training data for a user in a container exists first SQLFragment containerUserExists = new SQLFragment(); - containerUserExists.append("SELECT id FROM testresults.userdata WHERE userid = ? AND container = ?;"); + containerUserExists.append("SELECT id FROM " + TestResultsSchema.getTableInfoUserData() + " WHERE userid = ? AND container = ?;"); containerUserExists.add(userId); containerUserExists.add(getContainer().getEntityId()); SqlSelector selector = new SqlSelector(TestResultsSchema.getSchema(), containerUserExists); @@ -435,21 +434,20 @@ else if (!train && foundRuns.size() == 1) foundRowId = selector.getObject(Integer.class); SQLFragment sqlFragmentUpdate = new SQLFragment(); if(foundRowId != 0) { - sqlFragmentUpdate.append("UPDATE testresults.userdata SET "); + sqlFragmentUpdate.append("UPDATE " + TestResultsSchema.getTableInfoUserData() + " SET "); sqlFragmentUpdate.append("meantestsrun = ?, "); sqlFragmentUpdate.append("meanmemory = ?, "); sqlFragmentUpdate.append("stddevtestsrun = ?, "); sqlFragmentUpdate.append("stddevmemory = ? "); sqlFragmentUpdate.append("WHERE id = ? "); } else { - sqlFragmentUpdate.append("INSERT INTO testresults.userdata (userid, container, meantestsrun, meanmemory, stddevtestsrun, stddevmemory) "); - sqlFragmentUpdate.append("VALUES(?, ?, ?,?,?,?);"); + sqlFragmentUpdate.append("INSERT INTO " + TestResultsSchema.getTableInfoUserData() + " (userid, container, meantestsrun, meanmemory, stddevtestsrun, stddevmemory) "); + sqlFragmentUpdate.append("VALUES(?, ?, ?, ?, ?, ?);"); sqlFragmentUpdate.add(userId); sqlFragmentUpdate.add(getContainer().getEntityId()); } - if(trendDataForUser.size() == 0) { sqlFragmentUpdate.add(0.0); sqlFragmentUpdate.add(0.0); @@ -487,11 +485,11 @@ else if (!train && foundRuns.size() == 1) } - /** - * action to view user.jsp and all run details for user in date selection - * accepts a url parameter "user" which will be the user that the jsp displays runs for - * accepts url parameter "start" and "end" which will be the date range of selected runs for that user to display - */ + /** + * action to view user.jsp and all run details for user in date selection + * accepts a url parameter "user" which will be the user that the jsp displays runs for + * accepts url parameter "start" and "end" which will be the date range of selected runs for that user to display + */ @RequiresPermission(ReadPermission.class) public class ShowUserAction extends SimpleViewAction { @@ -501,16 +499,14 @@ public ModelAndView getView(Object o, BindException errors) throws Exception String start = getViewContext().getRequest().getParameter("start"); String end = getViewContext().getRequest().getParameter("end"); String userName = getViewContext().getRequest().getParameter("user"); - - SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); Date endDate = new Date(); Date startDate; if(start == null) startDate = DateUtils.addDays(new Date(), -6); // DEFUALT TO LAST WEEK's RUNS else - startDate = formatter.parse(start); + startDate = MDYFormat.parse(start); if(end != null) - endDate = DateUtils.addMilliseconds(DateUtils.ceiling(formatter.parse(end), Calendar.DATE), 0); + endDate = DateUtils.addMilliseconds(DateUtils.ceiling(MDYFormat.parse(end), Calendar.DATE), 0); RunDetail[] runs; // If no username specified show info summary for all users @@ -524,7 +520,7 @@ public ModelAndView getView(Object o, BindException errors) throws Exception } ensureRunDataCached(runs, false); - TestsDataBean bean = new TestsDataBean(runs); + TestsDataBean bean = new TestsDataBean(runs, new User[0]); return new JspView("/org/labkey/testresults/view/user.jsp", bean); } @@ -535,10 +531,10 @@ public NavTree appendNavTrail(NavTree root) } } - /** - * action to view runDetail.jsp (detail for a single run) - * accepts a url parameter "runId" which will be the run that the jsp displays the information of - */ + /** + * action to view runDetail.jsp (detail for a single run) + * accepts a url parameter "runId" which will be the run that the jsp displays the information of + */ @RequiresPermission(ReadPermission.class) public class ShowRunAction extends SimpleViewAction { @@ -558,7 +554,7 @@ public ModelAndView getView(Object o, BindException errors) throws Exception TestFailDetail[] fails = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestFails(), filter, null).getArray(TestFailDetail.class); TestPassDetail[] passes = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestPasses(), filter, null).getArray(TestPassDetail.class); - TestLeakDetail[] leaks = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestLeaks(), filter, null).getArray(TestLeakDetail.class); + TestMemoryLeakDetail[] memoryLeaks = new TableSelector(TestResultsSchema.getInstance().getTableInfoMemoryLeaks(), filter, null).getArray(TestMemoryLeakDetail.class); SQLFragment sqlFragment = new SQLFragment(); sqlFragment.append("SELECT testruns.*, u.username, EXISTS(SELECT 1 FROM testresults.trainruns WHERE runid = testruns.id) AS traindata FROM testresults.testruns "); @@ -591,9 +587,9 @@ public ModelAndView getView(Object o, BindException errors) throws Exception Arrays.sort(passes); } run.setFailures(fails); - run.setLeaks(leaks); + run.setTestmemoryleaks(memoryLeaks); run.setPasses(passes); - TestsDataBean bean = new TestsDataBean(runs); + TestsDataBean bean = new TestsDataBean(runs, new User[0]); return new JspView("/org/labkey/testresults/view/runDetail.jsp", bean); } @@ -604,41 +600,41 @@ public NavTree appendNavTrail(NavTree root) } } - /** - * action to view longTerm.jsp - * accepts a url parameter "viewType" of either wk(week), mo(month), or yr(year) and defaults to month - */ + /** + * action to view longTerm.jsp + * accepts a url parameter "viewType" of either wk(week), mo(month), or yr(year) and defaults to month + */ @RequiresPermission(ReadPermission.class) public class LongTermAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object o, BindException errors) throws Exception - { - String viewType = getViewContext().getRequest().getParameter("viewType"); - - TestsDataBean bean = new TestsDataBean(new RunDetail[0]); // bean that will be handed to jsp - viewType = getViewType(getViewContext().getUser(), viewType, ViewType.YEAR, "longterm", getContainer()); - Date startDate = getStartDate(viewType, ViewType.YEAR, new Date()); // defaults to month - bean.setViewType(viewType); - RunDetail[] runs = getRunsSinceDate(startDate, null, getContainer(), null, false, false); - bean.setRuns(runs); - - SimpleFilter filter = new SimpleFilter(); - filter.addClause(new CompareType.CompareClause(FieldKey.fromParts("timestamp"), CompareType.DATE_GTE, startDate)); - Sort s = new Sort("testrunid"); - TestFailDetail[] failures = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestFails(), filter, s).getArray(TestFailDetail.class); - bean.setNonAssociatedFailures(failures); - - ensureRunDataCached(runs, true); - return new JspView("/org/labkey/testresults/view/longTerm.jsp", bean); - } - - @Override - public NavTree appendNavTrail(NavTree root) - { - return root; - } - } + { + @Override + public ModelAndView getView(Object o, BindException errors) throws Exception + { + String viewType = getViewContext().getRequest().getParameter("viewType"); + + LongTermBean bean = new LongTermBean(new RunDetail[0], new User[0]); // bean that will be handed to jsp + viewType = getViewType(getViewContext().getUser(), viewType, ViewType.YEAR, "longterm", getContainer()); + Date startDate = getStartDate(viewType, ViewType.YEAR, new Date()); // defaults to month + bean.setViewType(viewType); + RunDetail[] runs = getRunsSinceDate(startDate, null, getContainer(), null, false, false); + bean.setRuns(runs); + + SimpleFilter filter = new SimpleFilter(); + filter.addClause(new CompareType.CompareClause(FieldKey.fromParts("timestamp"), CompareType.DATE_GTE, startDate)); + Sort s = new Sort("testrunid"); + TestFailDetail[] failures = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestFails(), filter, s).getArray(TestFailDetail.class); + bean.setNonAssociatedFailures(failures); + + ensureRunDataCached(runs, true); + return new JspView("/org/labkey/testresults/view/longTerm.jsp", bean); + } + + @Override + public NavTree appendNavTrail(NavTree root) + { + return root; + } + } /** * action to view failureDetail.jsp @@ -651,27 +647,44 @@ public class ShowFailures extends SimpleViewAction @Override public ModelAndView getView(Object o, BindException errors) throws Exception { - String failedTest = getViewContext().getRequest().getParameter("failedTest"); - String viewType = getViewContext().getRequest().getParameter("viewType"); + ViewContext vc = getViewContext(); + String end = vc.getRequest().getParameter("end"); + String failedTest = vc.getRequest().getParameter("failedTest"); + String viewType = vc.getRequest().getParameter("viewType"); - viewType = getViewType(getViewContext().getUser(), viewType, ViewType.DAY, "failures", getContainer()); - Date startDate = getStartDate(viewType, ViewType.DAY, new Date()); // defaults to day - RunDetail[] runs = getRunsSinceDate(startDate, null, getContainer(), null, false, false); + boolean givenDate = end != null && !end.equals(""); + Date endDate = new Date(); + if(givenDate) + endDate = MDYFormat.parse(end); + endDate = setDateToEightAM(endDate); + viewType = getViewType(getViewContext().getUser(), viewType, ViewType.DAY, null, getContainer()); + Date startDate = getStartDate(viewType, ViewType.DAY, endDate); // defaults to day + RunDetail[] runs = getRunsSinceDate(startDate, endDate, getContainer(), null, false, false); populateFailures(runs); - - if(failedTest == null) - return new JspView("/org/labkey/testresults/view/failureDetail.jsp", runs); + TestsDataBean b = new TestsDataBean(runs, new User[0]); + b.setViewType(viewType); + b.setStartDate(startDate); + b.setEndDate(endDate); + if(failedTest == null || failedTest.equals("")) + return new JspView("/org/labkey/testresults/view/multiFailureDetail.jsp", b); List failureRuns = new ArrayList<>(); for(RunDetail run: runs) { - for(TestFailDetail fail: run.getFailures()) + List f = new ArrayList<>(); + for(TestFailDetail fail: run.getFailures()) { if(fail.getTestName().equals(failedTest)) { - failureRuns.add(run); - break; + f.add(fail); } + } + if(f.size() > 0) { + run.setFailures(f.toArray(new TestFailDetail[f.size()])); + failureRuns.add(run); + } } - TestsDataBean bean = new TestsDataBean(failureRuns.toArray(new RunDetail[failureRuns.size()])); + TestsDataBean bean = new TestsDataBean(failureRuns.toArray(new RunDetail[failureRuns.size()]), new User[0]); bean.setViewType(viewType); + bean.setStartDate(startDate); + bean.setEndDate(endDate); return new JspView("/org/labkey/testresults/view/failureDetail.jsp", bean); } @@ -697,7 +710,7 @@ public ModelAndView getView(Object o, BindException errors) throws Exception try (DbScope.Transaction transaction = TestResultsSchema.getInstance().getSchema().getScope().ensureTransaction()) { Table.delete(TestResultsSchema.getInstance().getTableInfoTestFails(), filter); // delete failures Table.delete(TestResultsSchema.getInstance().getTableInfoTestPasses(), filter); // delete passes - Table.delete(TestResultsSchema.getInstance().getTableInfoTestLeaks(), filter); // delete leaks + Table.delete(TestResultsSchema.getInstance().getTableInfoMemoryLeaks(), filter); // delete leaks Table.delete(TestResultsSchema.getInstance().getTableInfoTestRuns(), rowId); // delete run last because of foreign key transaction.commit(); } @@ -754,7 +767,7 @@ public ModelAndView getView(Object o, BindException errors) throws Exception SimpleFilter filter = new SimpleFilter(); filter.addCondition(FieldKey.fromParts("flagged"), true); RunDetail[] details = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestRuns(), filter, null).getArray(RunDetail.class); - return new JspView("/org/labkey/testresults/view/flagged.jsp", new TestsDataBean(details)); + return new JspView("/org/labkey/testresults/view/flagged.jsp", new TestsDataBean(details, new User[0])); } @Override public NavTree appendNavTrail(NavTree root) @@ -940,16 +953,12 @@ public Object execute(Object o, BindException errors) throws Exception * action for posting test output as an xml file */ @RequiresNoPermission - public class PostAction extends SimpleViewAction { + public class PostAction extends ApiAction { @Override - public ModelAndView getView(Object o, BindException errors) throws Exception + public Object execute(Object o, BindException errors) throws Exception { - String NA = "N/A"; - _log.info("Handling SkylineNightly posted results"); - // DebugRequest(getViewContext().getRequest()); - if(!(getViewContext().getRequest() instanceof MultipartRequest)) { throw new Exception("Expected a request of type MultipartRequest got " + getViewContext().getRequest().getClass().toString()); @@ -982,6 +991,143 @@ else if (xml.length() == 0) throw new Exception("xml_file not found in request"); } + Map res = new HashMap<>(); + + // try to parse and store xml, if fails save xml file to server to attempt to re-post manually by user + try { + _log.info("Handling SkylineNightly posted results"); + NIGHTLY_POSTER.ParseAndStoreXML(xml, getContainer()); + } catch (Exception e) { + _log.info("XML failed to parse/store"); + _log.info("Attempting to save file for a future post attempt"); + res.put("Success", false); + res.put("Message", "Error Parsing XML attempting to save the XML file... " + NIGHTLY_POSTER.SaveXML(file, getContainer())); + res.put("Exception", e + NIGHTLY_POSTER.getStackTraceText(e)); + return new ApiSimpleResponse(res); + } + + return new ApiSimpleResponse("Success", true); + } + + private void DebugRequest(HttpServletRequest hsRequest) + { + _log.info("Request is " + hsRequest.getClass().toString()); + _log.info("Content length is : "+ hsRequest.getContentLength()); + _log.info("Content type: " + hsRequest.getContentType()); + Enumeration headerNames = hsRequest.getHeaderNames(); + while(headerNames.hasMoreElements()) + { + String headerName = headerNames.nextElement(); + _log.info("Header " + headerName + ": " + hsRequest.getHeader(headerName)); + } + + if(hsRequest instanceof MultipartRequest) + { + MultipartRequest request = (MultipartRequest) hsRequest; + _log.info("Multi part content type for xml: " + request.getMultipartContentType("xml")); + _log.info("Multi part content type for xml_file: " + request.getMultipartContentType("xml_file")); + } + } + } + + + @RequiresPermission(ReadPermission.class) + public class ErrorFilesAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) throws Exception + { + File local = NIGHTLY_POSTER.getLocalPath(getContainer()); + File[] files = local.listFiles(); + return new JspView("/org/labkey/testresults/view/errorFiles.jsp", files); + } + + @Override + public NavTree appendNavTrail(NavTree root) + { + return root; + } + } + + @RequiresPermission(ReadPermission.class) + public class PostErrorFilesAction extends ApiAction + { + @Override + public Object execute(Object o, BindException errors) + { + Container c = getContainer(); + File local = NIGHTLY_POSTER.getLocalPath(c); + File[] files = local.listFiles(); + Map res = new HashMap<>(); + for(File f: files) { + if (f.getName().equals(".upload.log")) // LabKey system file + continue; + try { + String xml = FileUtils.readFileToString(f, Charset.defaultCharset()); + NIGHTLY_POSTER.ParseAndStoreXML(xml, c); + f.delete(); + res.put(f.getName(), "Success!"); + } catch (Exception e) { + res.put(f.getName(), Arrays.toString(e.getStackTrace())); + } + } + return new ApiSimpleResponse(res); + } + } + + public static class NIGHTLY_POSTER + { + + // used for formatting timestamps in hh:mm format as they may roll over multiple days + public static Date addDays(Date date, int days) + { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.DATE, days); //minus number would decrement the days + return cal.getTime(); + } + + public static String getStackTraceText(Exception e) + { + StringBuilder sb = new StringBuilder(); + for (StackTraceElement el : e.getStackTrace()) { + sb.append(el).append("\r\n"); // Use windows new lines to display correctly in NotePad + } + return sb.toString(); + } + public static File getLocalPath(Container c) + { + return FileContentService.get().getFileRootPath(c, FileContentService.ContentType.files).toFile(); + } + + public static File makeFile(Container c, String filename) + { + return new File(getLocalPath(c), FileUtil.makeLegalName(filename)); + } + + private static String SaveXML(MultipartFile file, Container c) { + String fileName = ((CommonsMultipartFile) file).getFileItem().getName(); + try + { + File f = makeFile(c, fileName); + if(f.exists()) { + _log.info("A file by the name " + fileName + " is already stored."); + return "File not saved - file already exists in file system."; + } + file.transferTo(f); + } + catch (IOException e) + { + _log.error("Failed to save " + fileName + "."); + e.printStackTrace(); + return "Failed to save the file."; + } + return "File saved to system."; + } + + private static void ParseAndStoreXML(String xml, Container c) throws Exception + { + String NA = "N/A"; try (DbScope.Transaction transaction = TestResultsSchema.getSchema().getScope().ensureTransaction()) { @@ -1009,7 +1155,7 @@ else if (xml.length() == 0) } } else { - userid = details[0].getId(); + userid = details[0].getId(); } if(userid == -1) throw new Exception("Issue with user/userid, may not be set"); @@ -1034,7 +1180,8 @@ else if (xml.length() == 0) docElement.removeChild(logN); // remove log at the end so that it doesn't get stored with the xml } // Get leaks, failures, and passes - List leaks = new ArrayList<>(); + List memoryLeaks = new ArrayList<>(); + List handleLeaks = new ArrayList<>(); List failures = new ArrayList<>(); List passes = new ArrayList<>(); // stores leaks in database @@ -1042,8 +1189,17 @@ else if (xml.length() == 0) NodeList nlLeak = ((Element) nListLeaks.item(0)).getElementsByTagName("leak"); for(int leakIndex = 0; leakIndex < nlLeak.getLength(); leakIndex++) { Element elLeak = (Element) nlLeak.item(leakIndex); - TestLeakDetail leak = new TestLeakDetail(0, elLeak.getAttribute("name"), Integer.parseInt(elLeak.getAttribute("bytes"))); - leaks.add(leak); + String type = elLeak.getAttribute("type"); + if(elLeak.getAttribute("bytes") != "") { // process memory leak + TestMemoryLeakDetail leak = new TestMemoryLeakDetail(0, elLeak.getAttribute("name"), type, (int)Float.parseFloat(elLeak.getAttribute("bytes"))); + memoryLeaks.add(leak); + } else if(elLeak.getAttribute("handles") != "") { // process handle leak + TestHandleLeakDetail leak = new TestHandleLeakDetail(0, elLeak.getAttribute("name"), type, Float.parseFloat(elLeak.getAttribute("handles"))); + handleLeaks .add(leak); + } else { + _log.error("Error parsing Leak " + elLeak.getAttribute("name") + "."); + throw new XMLParseException(); + } } // parse passes @@ -1055,7 +1211,7 @@ else if (xml.length() == 0) for(int i = 0; i < nListPasses.getLength(); i++) { NodeList nlTests = ((Element) nListPasses.item(i)).getElementsByTagName("test"); int passId = Integer.parseInt(((Element) nListPasses.item(i)).getAttribute("id")); - for(int j = 0; j < nlTests.getLength(); j++) { + for(int j = 0; j < nlTests.getLength(); j++) { Element test = (Element) nlTests.item(j); Date timestamp; String ts = test.getAttribute("timestamp"); @@ -1065,7 +1221,7 @@ else if (xml.length() == 0) timestamp = null; } // if "xmlTimestamp" is not a proper date assume it is in hh:mm format - if(ts!=null && timestamp == null) { + if(ts!=null && timestamp == null) { int hour = new Integer(ts.split(":")[0]); if(hour >= lastHour) { if (lastHour == 0) @@ -1075,8 +1231,7 @@ else if (xml.length() == 0) timestampDay = addDays(timestampDay, 1); lastHour = hour; } - DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); - String originalDate = df.format(timestampDay); + String originalDate = MDYFormat.format(timestampDay); try { timestamp = new SimpleDateFormat("MM/dd/yyyy HH:mm").parse(originalDate + " " + ts); } catch (IllegalArgumentException e) { @@ -1085,10 +1240,23 @@ else if (xml.length() == 0) } if(test.getAttribute("duration").equals(NA) || test.getAttribute("managed").equals(NA) || test.getAttribute("total").equals(NA)) continue; - TestPassDetail pass = new TestPassDetail(0, passId, Integer.parseInt(test.getAttribute("id")), test.getAttribute("name"), - test.getAttribute("language"), Integer.parseInt(test.getAttribute("duration")), Double.parseDouble(test.getAttribute("managed")), Double.parseDouble(test.getAttribute("total")), timestamp); - avgMemory += pass.getTotalMemory(); - passes.add(pass); + String committedAttr = test.getAttribute("committed"); + String usergdiAttr = test.getAttribute("user_gdi"); + String handlesAttr = test.getAttribute("handles"); + TestPassDetail pass = new TestPassDetail(0, passId, + Integer.parseInt(test.getAttribute("id")), + test.getAttribute("name"), + test.getAttribute("language"), + Integer.parseInt(test.getAttribute("duration")), + Double.parseDouble(test.getAttribute("managed")), + Double.parseDouble(test.getAttribute("total")), + // New leak tracking values + StringUtils.hasText(committedAttr) ? Double.parseDouble(committedAttr) : 0, + StringUtils.hasText(usergdiAttr) ? Integer.parseInt(usergdiAttr) : 0, + StringUtils.hasText(handlesAttr) ? Integer.parseInt(handlesAttr) : 0, + timestamp); + avgMemory += pass.getTotalMemory(); + passes.add(pass); } } if(passes.size() != 0) @@ -1116,8 +1284,7 @@ else if (xml.length() == 0) timestampDay = addDays(timestampDay, 1); lastHour = hour; } - DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); - String originalDate = df.format(timestampDay); + String originalDate = MDYFormat.format(timestampDay); try { timestamp = new SimpleDateFormat("MM/dd/yyyy HH:mm").parse(originalDate + " " + ts); } catch (IllegalArgumentException e) { @@ -1133,18 +1300,23 @@ else if (xml.length() == 0) byte[] compressedXML = null; byte[] compressedLog = null; if(xml != null) - compressString(docElement.toString()); + compressString(docElement.toString()); if(log != null) compressedLog = compressString(log); - RunDetail run = new RunDetail(userid, duration, postTime, xmlTimestamp, os, revision, gitHash, getViewContext().getContainer(), false, - compressedXML, pointSummary, passes.size(), failures.size(), leaks.size(), avgMemory, compressedLog); //TODO change date AND USERID + RunDetail run = new RunDetail(userid, duration, postTime, xmlTimestamp, os, revision, gitHash, c, false, compressedXML, + pointSummary, passes.size(), failures.size(), memoryLeaks.size(), avgMemory, compressedLog); //TODO change date AND USERID // stores test run in database and gets the id(foreign key) run = Table.insert(null, TestResultsSchema.getInstance().getTableInfoTestRuns(), run); int runId = run.getId(); - for(TestLeakDetail leak : leaks) { + + for(TestHandleLeakDetail leak : handleLeaks) { + leak.setTestRunId(runId); + Table.insert(null, TestResultsSchema.getInstance().getTableInfoHandleLeaks(), leak); + } + for(TestMemoryLeakDetail leak : memoryLeaks) { leak.setTestRunId(runId); - Table.insert(null, TestResultsSchema.getInstance().getTableInfoTestLeaks(), leak); + Table.insert(null, TestResultsSchema.getInstance().getTableInfoMemoryLeaks(), leak); } for(TestFailDetail fail: failures) { fail.setTestRunId(runId); @@ -1157,14 +1329,13 @@ else if (xml.length() == 0) transaction.commit(); } catch (Exception e) { - _log.error("Error parsing xml", e); + _log.error("Error parsing xml"); // e.getStackTrace(); throw e; } - return null; } - private byte[] compressString(String s) { + private static byte[] compressString(String s) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { OutputStream out = new GZIPOutputStream(baos); @@ -1176,40 +1347,6 @@ private byte[] compressString(String s) { } return baos.toByteArray(); } - - private void DebugRequest(HttpServletRequest hsRequest) - { - _log.info("Request is " + hsRequest.getClass().toString()); - _log.info("Content length is : "+ hsRequest.getContentLength()); - _log.info("Content type: " + hsRequest.getContentType()); - Enumeration headerNames = hsRequest.getHeaderNames(); - while(headerNames.hasMoreElements()) - { - String headerName = headerNames.nextElement(); - _log.info("Header " + headerName + ": " + hsRequest.getHeader(headerName)); - } - - if(hsRequest instanceof MultipartRequest) - { - MultipartRequest request = (MultipartRequest) hsRequest; - _log.info("Multi part content type for xml: " + request.getMultipartContentType("xml")); - _log.info("Multi part content type for xml_file: " + request.getMultipartContentType("xml_file")); - } - } - - // used for formatting timestamps in hh:mm format as they may roll over multiple days - public Date addDays(Date date, int days) - { - Calendar cal = Calendar.getInstance(); - cal.setTime(date); - cal.add(Calendar.DATE, days); //minus number would decrement the days - return cal.getTime(); - } - @Override - public NavTree appendNavTrail(NavTree root) - { - return root; - } } // Encodes pass point summary @@ -1259,8 +1396,8 @@ public static RunDetail[] getRunsSinceDate(Date startDate, Date end, Container c end = DateUtils.addDays(start, 1); } SQLFragment sqlFragment = new SQLFragment(); - sqlFragment.append("SELECT testruns.*, u.username, EXISTS(SELECT 1 FROM testresults.trainruns WHERE runid = testruns.id) AS traindata "); - sqlFragment.append("FROM testresults.testruns "); + sqlFragment.append("SELECT testruns.*, u.username, EXISTS(SELECT 1 FROM "+TestResultsSchema.getTableInfoTrain()+" WHERE runid = testruns.id) AS traindata "); + sqlFragment.append("FROM "+TestResultsSchema.getTableInfoTestRuns()+" "); sqlFragment.append("JOIN testresults.user AS u ON testruns.userid = u.id "); sqlFragment.append("WHERE posttime >= ? "); sqlFragment.append("AND posttime < ? "); @@ -1323,7 +1460,7 @@ static Boolean isValidViewType(String ds) { return false; switch (ds) { case ViewType.DAY: - return true; + return true; case ViewType.WEEK: return true; case ViewType.MONTH: @@ -1404,12 +1541,12 @@ static TestFailDetail[] getFailuresForRun(RunDetail run) { populateFailures(runs); return runs[0].getFailures(); } - static TestLeakDetail[] getLeaksForRun(RunDetail run) { + static TestMemoryLeakDetail[] getLeaksForRun(RunDetail run) { if(run == null) return null; RunDetail[] runs = new RunDetail[]{run}; populateLeaks(runs); - return runs[0].getLeaks(); + return runs[0].getTestmemoryleaks(); } /* * Given a set of run details this method queries and populates each RunDetail with corresponding @@ -1448,6 +1585,9 @@ static void populateLastPassForRuns(RunDetail[] runs) { runIds.add(r.getId()); SQLFragment sqlFragment = new SQLFragment(); +// SimpleFilter filter = new SimpleFilter(); +// filter.addCondition() +// TestPassDetail[] passes = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestFails(), filter, null).getArray(TestPassDetail.class); sqlFragment.append(" SELECT * FROM testresults.testpasses WHERE id = ANY(SELECT MAX(id) FROM " + "testresults.testpasses WHERE (testrunid = ANY(?)) GROUP BY testrunid);"); sqlFragment.add(runIds); @@ -1470,7 +1610,6 @@ static void populateLastPassForRuns(RunDetail[] runs) { r.setPasses(new TestPassDetail[]{pass}); } } - } static void populateFailures(RunDetail[] runs) { @@ -1503,26 +1642,36 @@ static void populateFailures(RunDetail[] runs) { static void populateLeaks(RunDetail[] runs) { SimpleFilter filter = filterByRunId(runs); - TestLeakDetail[] leaks = new TableSelector(TestResultsSchema.getInstance().getTableInfoTestLeaks(), filter, null).getArray(TestLeakDetail.class); - Map> testLeakDetails = new HashMap<>(); + TestHandleLeakDetail[] handleLeaks = new TableSelector(TestResultsSchema.getInstance().getTableInfoHandleLeaks(), filter, null).getArray(TestHandleLeakDetail.class); + TestMemoryLeakDetail[] memoryLeaks = new TableSelector(TestResultsSchema.getInstance().getTableInfoMemoryLeaks(), filter, null).getArray(TestMemoryLeakDetail.class); + Map> testLeakDetails = new HashMap<>(); - for (TestLeakDetail leak : leaks) { - List list = testLeakDetails.get(leak.getTestRunId()); + for (TestMemoryLeakDetail leak : memoryLeaks) { + List list = testLeakDetails.get(leak.getTestRunId()); if (null == list) { list = new ArrayList<>(); } list.add(leak); testLeakDetails.put(leak.getTestRunId(), list); } - for (RunDetail run : runs) - { + for (RunDetail run : runs) { int runId = run.getId(); - List leakList = testLeakDetails.get(runId); + List leakList = testLeakDetails.get(runId); if (leakList != null) - run.setLeaks(leakList.toArray(new TestLeakDetail[leakList.size()])); + run.setTestmemoryleaks(leakList.toArray(new TestMemoryLeakDetail[leakList.size()])); else - run.setLeaks(new TestLeakDetail[0]); + run.setTestmemoryleaks(new TestMemoryLeakDetail[0]); } } + + private static Date setDateToEightAM(Date d) { + Calendar cal = Calendar.getInstance(); + cal.setTime(d); + cal.set(Calendar.HOUR_OF_DAY, 8); + cal.set(Calendar.MINUTE, 1); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return cal.getTime(); + } } \ No newline at end of file diff --git a/testresults/src/org/labkey/testresults/TestResultsModule.java b/testresults/src/org/labkey/testresults/TestResultsModule.java index 1cebaca1..7cf727b1 100644 --- a/testresults/src/org/labkey/testresults/TestResultsModule.java +++ b/testresults/src/org/labkey/testresults/TestResultsModule.java @@ -63,7 +63,7 @@ public String getName() @Override public double getVersion() { - return 13.34; + return 13.35; } @Override diff --git a/testresults/src/org/labkey/testresults/TestResultsSchema.java b/testresults/src/org/labkey/testresults/TestResultsSchema.java index 4f524503..327a746a 100644 --- a/testresults/src/org/labkey/testresults/TestResultsSchema.java +++ b/testresults/src/org/labkey/testresults/TestResultsSchema.java @@ -45,42 +45,44 @@ public static DbSchema getSchema() return DbSchema.get("testresults"); } - public TableInfo getTableInfoTestRuns() + public static TableInfo getTableInfoTestRuns() { return getSchema().getTable("testruns"); } - public TableInfo getTableInfoUser() + public static TableInfo getTableInfoUser() { return getSchema().getTable("user"); } - public TableInfo getTableInfoUserData() + public static TableInfo getTableInfoUserData() { return getSchema().getTable("userdata"); } - public TableInfo getTableInfoTrain() + public static TableInfo getTableInfoTrain() { return getSchema().getTable("trainruns"); } - public TableInfo getTableInfoTestLeaks() + public static TableInfo getTableInfoMemoryLeaks() { - return getSchema().getTable("testleaks"); + return getSchema().getTable("memoryleaks"); } - public TableInfo getTableInfoTestPasses() + public static TableInfo getTableInfoHandleLeaks() { return getSchema().getTable("handleleaks"); } + + public static TableInfo getTableInfoTestPasses() { return getSchema().getTable("testpasses"); } - public TableInfo getTableInfoTestFails() + public static TableInfo getTableInfoTestFails() { return getSchema().getTable("testfails"); } - public SqlDialect getSqlDialect() + public static SqlDialect getSqlDialect() { return getSchema().getSqlDialect(); } diff --git a/testresults/src/org/labkey/testresults/TestResultsWebPart.java b/testresults/src/org/labkey/testresults/TestResultsWebPart.java index fd1bcb2b..f9566870 100644 --- a/testresults/src/org/labkey/testresults/TestResultsWebPart.java +++ b/testresults/src/org/labkey/testresults/TestResultsWebPart.java @@ -1,15 +1,6 @@ package org.labkey.testresults; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.time.DateUtils; -import org.labkey.api.data.CompareType; import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerManager; -import org.labkey.api.data.SQLFragment; -import org.labkey.api.data.SimpleFilter; -import org.labkey.api.data.SqlSelector; -import org.labkey.api.data.TableSelector; -import org.labkey.api.query.FieldKey; import org.labkey.api.view.BaseWebPartFactory; import org.labkey.api.view.JspView; import org.labkey.api.view.Portal; @@ -17,22 +8,10 @@ import org.labkey.api.view.WebPartConfigurationException; import org.labkey.api.view.WebPartFactory; import org.labkey.api.view.WebPartView; +import org.labkey.testresults.view.TestsDataBean; import java.io.IOException; import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.labkey.testresults.TestResultsController.getRunsSinceDate; -import static org.labkey.testresults.TestResultsController.getTrainingDataForContainer; -import static org.labkey.testresults.TestResultsController.populateFailures; -import static org.labkey.testresults.TestResultsController.populateLeaks; -import static org.labkey.testresults.TestResultsController.populatePassesLeaksFails; public class TestResultsWebPart extends BaseWebPartFactory { @@ -48,7 +27,7 @@ public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) TestsDataBean bean = null; try { - bean = TestResultsController.getRunDownData(portalCtx.getUser(), c, portalCtx); + bean = TestResultsController.getRunDownBean(portalCtx.getUser(), c, portalCtx); } catch (ParseException e) { diff --git a/testresults/src/org/labkey/testresults/TestsDataBean.java b/testresults/src/org/labkey/testresults/TestsDataBean.java deleted file mode 100644 index c0da3149..00000000 --- a/testresults/src/org/labkey/testresults/TestsDataBean.java +++ /dev/null @@ -1,774 +0,0 @@ -/* - * Copyright (c) 2015 LabKey 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 org.labkey.testresults; - -import org.apache.commons.lang3.ArrayUtils; -import org.json.JSONArray; -import org.json.JSONObject; -import org.labkey.api.data.statistics.MathStat; -import org.labkey.api.data.statistics.StatsService; -import org.labkey.api.services.ServiceRegistry; -import org.labkey.remoteapi.assay.Run; - -import java.lang.Override; -import java.lang.String; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -/** - * User: Yuval Boss, yuval(at)uw.edu - * Date: 1/14/2015 - */ -public class TestsDataBean -{ - private Map runs; - private RunDetail[] statRuns; - private User[] users; - private String viewType; - private Date startDate; - private Date endDate; - - public Date getStartDate() - { - if(startDate == null) - startDate = new Date(); - return startDate; - } - - public void setStartDate(Date startDate) - { - this.startDate = startDate; - } - - public Date getEndDate() - { - if(endDate == null) - setEndDate(new Date()); - return endDate; - } - - public void setEndDate(Date day) - { - this.endDate = day; - } - - public String getViewType() - { - return viewType; - } - - public void setViewType(String viewType) - { - this.viewType = viewType; - } - - - private TestFailDetail[] nonAssociatedFailures; - - public TestsDataBean(RunDetail[] runs) { - setRuns(runs); - statRuns = setStatRuns(); - } - - public TestsDataBean(RunDetail[] runs, User[] users) { - this(runs); - setUsers(users); - } - - public TestsDataBean(RunDetail[] runs, User[] users, String viewType) { - this(runs, users); - setViewType(viewType); - } - - public TestsDataBean(RunDetail[] runs, User[] users, String viewType, Date startDate, Date endDate) { - this(runs, users, viewType); - setStartDate(startDate); - setEndDate(endDate); - } - - // Getters and Setters for fields - public RunDetail[] getRuns() { - RunDetail[] r = runs.values().toArray(new RunDetail[runs.size()]); - Arrays.sort(r); - return r; - } - public RunDetail[] setStatRuns() { - List statRuns = new ArrayList<>(); - for(RunDetail run: runs.values()) - if(!excludeRun(run.getId())) - statRuns.add(run); - Collections.sort(statRuns); - return statRuns.toArray(new RunDetail[statRuns.size()]); - } - - public RunDetail[] getStatRuns() { - return statRuns; - } - - public Map> getLanguageBreakdown(Map> topFailures){ - Map> m = new TreeMap>(); - for(String f: topFailures.keySet()) { - double total = 0.0; - if(!m.containsKey(f)) - m.put(f, new TreeMap()); - List l = topFailures.get(f); - for(TestFailDetail detail: l) { - if(detail != null) { - if(!m.get(f).containsKey(detail.getLanguage())) - m.get(f).put(detail.getLanguage(), 0.0); - m.get(f).put(detail.getLanguage(), m.get(f).get(detail.getLanguage()) + 1); - total += 1; - } - } - for(String language: m.get(f).keySet()) { - m.get(f).put(language, m.get(f).get(language) / total); - } - } - - return m; - } - - public User[] getMissingUsers(RunDetail[] daysRuns) { - if(users == null) - return new User[0]; - List missingUsers = new ArrayList<>(); - for(User u: users) { - if(u != null && u.isActive()) { - boolean userFound = false; - for(RunDetail r: daysRuns) { - if(r != null && r.getUserid() == u.getId()) { - userFound = true; - break; - } - } - if(!userFound) - missingUsers.add(u); - } - } - return missingUsers.toArray(new User[missingUsers.size()]); - } - - public double round(double value, int places) { - if (places < 0) throw new IllegalArgumentException(); - - long factor = (long) Math.pow(10, places); - value = value * factor; - long tmp = Math.round(value); - return (double) tmp / factor; - } - - public void setRuns(RunDetail[] runs) { - this.runs = new LinkedHashMap<>(); - addRuns(runs); - statRuns = setStatRuns(); - } - - public TestLeakDetail[] getLeaks() { - List leaks = new ArrayList<>(); - for(RunDetail r: runs.values()) { - leaks.addAll(Arrays.asList(r.getLeaks())); - } - return leaks.toArray(new TestLeakDetail[leaks.size()]); - } - - public TestFailDetail[] getFailures() { - List fails = new ArrayList<>(); - for(RunDetail r: runs.values()) - fails.addAll(Arrays.asList(r.getFailures())); - return fails.toArray(new TestFailDetail[fails.size()]); - } - - public TestFailDetail[] getFailuresByName(String testName) { - List fails = new ArrayList<>(); - for(RunDetail r: runs.values()) - for(TestFailDetail f: r.getFailures()) - if(f.getTestName().equals(testName)) - fails.add(f); - return fails.toArray(new TestFailDetail[fails.size()]); - } - - public TestPassDetail[] getPasses() { - List passes = new ArrayList<>(); - for(RunDetail run : runs.values()) - passes.addAll(Arrays.asList(run.getPasses())); - return passes.toArray(new TestPassDetail[passes.size()]); - } - - private void addRuns(RunDetail[] runs) { - if(runs == null) - return; - for (RunDetail run : runs) { - // round all memory data - TestPassDetail[] passes = run.getPasses(); - if(passes != null && passes.length > 0 && passes[0] != null) { - for(int i = 0; i > getLeaksByDate(Date d, boolean isStatRun) { - Map> m = new TreeMap>(); - RunDetail[] dayRuns = getRunsByDate(d, true); - for(RunDetail r: dayRuns) { - if(isStatRun && !excludeRun(r.getId())) - { - for(TestLeakDetail l: r.getLeaks()) { - if(!m.containsKey(l.getTestName())) - m.put(l.getTestName(), new ArrayList()); - m.get(l.getTestName()).add(l); - } - } - } - return m; - } - - // returns a map of test name to list of TestFailDetails for a specified day - public Map> getFailedTestsByDate(Date d, boolean isStatRun) { - Map> m = new TreeMap>(); - RunDetail[] dayRuns = getRunsByDate(d, true); - for(RunDetail r: dayRuns) { - if(isStatRun && !excludeRun(r.getId())) - { - for(TestFailDetail f: r.getFailures()) { - if(!m.containsKey(f.getTestName())) - { - m.put(f.getTestName(), new ArrayList()); - } - m.get(f.getTestName()).add(f); - } - } - } - return m; - } - - public RunDetail[] getRunsByDate(Date day, boolean isStatRun) { - List runByDay = new ArrayList(); - Calendar cal = Calendar.getInstance(); - cal.setTime(day); - cal.set(Calendar.HOUR_OF_DAY, 8); - cal.set(Calendar.MINUTE, 1); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - day = cal.getTime(); - cal.add(Calendar.DATE, -1); - Date dateBefore1Day = cal.getTime(); - for(RunDetail runDetail:getStatRuns()) { - boolean isSameDay = (runDetail.getPostTime().getTime() < day.getTime() && runDetail.getPostTime().getTime() > dateBefore1Day.getTime()); - if(isSameDay) - runByDay.add(runDetail); - } - runByDay.sort(null); - RunDetail[] returnedRuns = runByDay.toArray(new RunDetail[runByDay.size()]); - return returnedRuns; - } - - /* - * returns trends of avg over time where the total time is the range of RunDetails we have in this object - * if there are no runs returns NULL - */ - public JSONObject getTrends() { - if(runs.size() == 0) - return null; - Map> dates = new TreeMap>(); - Calendar cal = Calendar.getInstance(); - for(RunDetail run: getStatRuns()) { - cal.setTime(run.getPostTime()); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - Date strippedDay = cal.getTime(); - List list = dates.get(strippedDay); - if (list == null) { - dates.put(strippedDay, list = new ArrayList<>()); - } - list.add(run); - } - - int size = dates.size(); - double[] avgDuration = new double[size]; - double[] avgTestRuns = new double[size]; - int[] avgMemory = new int[size]; - double[] avgFailures = new double[size]; - int i = 0; - for(Map.Entry> entry : dates.entrySet()) { - List runs = entry.getValue(); - int passTotal = 0; - int failTotal = 0; - int avgMemoryTotal = 0; - int durationTotal = 0; - for(RunDetail run: runs) { - passTotal += run.getPassedtests(); - failTotal += run.getFailedtests(); - durationTotal += run.getDuration(); - avgMemoryTotal += run.getAverageMemory(); - } - avgTestRuns[i] = round((double) passTotal/runs.size(),2); - avgFailures[i] = round(((double)failTotal)/runs.size(),2); - avgMemory[i] = avgMemoryTotal/runs.size(); - avgDuration[i] = round(((double)durationTotal)/runs.size(),2); - i++; - } - long[] milliSecondDates = new long[dates.size()]; - int j = 0; - for(Date d: dates.keySet()) { - milliSecondDates[j] = d.getTime(); - j++; - } - // create and populate the JSONObject that will be returned - JSONObject jo = new JSONObject(); - jo.put("avgDuration", avgDuration); - jo.put("avgMemory", avgMemory); - jo.put("avgFailures", avgFailures); - jo.put("avgTestRuns", avgTestRuns); - jo.put("dates", milliSecondDates); - - return jo; - } - - /* - * returns an aggregated JSON map of tests memory usage for all runs stored in the bean - * if isToday is set to true then it will only include runs from today - */ - public JSONObject getTodaysCompactMemoryJson(Date day) throws Exception - { - if(runs.size() == 0) - return null; - List selectedRuns = new ArrayList<>(); - Map points = new HashMap<>(); - Map memoryusagebyrun = new HashMap<>(); - Calendar cal = Calendar.getInstance(); - cal.setTime(day); - cal.set(Calendar.HOUR_OF_DAY, 8); - cal.set(Calendar.MINUTE, 1); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - day = cal.getTime(); - cal.add(Calendar.DATE, -1); - Date dateBefore1Day = cal.getTime(); - for (RunDetail run:runs.values()) { - boolean isSameDay = (run.getPostTime().getTime() < day.getTime() && run.getPostTime().getTime() > dateBefore1Day.getTime()); - if(isSameDay && !run.isFlagged()) { // filter by today's runs - if(run.getPointsummary() == null) - throw new Exception("null memory json for run id=" + run.getId()); - selectedRuns.add(run); - points.put(run.getUserName() + "(id." + run.getId() + ")", run.getPoints()); - } - } - - JSONObject json = new JSONObject(); - Map> allPassPointsMap = new HashMap<>(); - Map averagePassPointMap = new HashMap<>(); - - Map dataPoints = new HashMap<>(); - for (Map.Entry entry : points.entrySet()) { - if(entry.getValue().length ==0) - continue; - - Double[] encodedArray = entry.getValue(); - int index = Arrays.asList(encodedArray).indexOf(-1.0); - if(index == -1) - continue; - - Double[] passLocations = new Double[index]; - Double[] memoryusage = new Double[encodedArray.length-index-1]; // -1 because we no longer include the -1 flag - System.arraycopy(encodedArray, 0, passLocations, 0, index); - System.arraycopy(encodedArray, index+1, memoryusage, 0, encodedArray.length-index-1); - int[] memoryusageint = new int[memoryusage.length]; - for(int i = 0; i < memoryusage.length; i++) - memoryusageint[i] = memoryusage[i].intValue(); - - memoryusagebyrun.put(entry.getKey(), memoryusageint); - for(int i = 0; i < passLocations.length; i++) { - double location = passLocations[i]*TestResultsController.POINT_RATIO; - if(location == 0.0) - continue; - if(allPassPointsMap.get(i) == null) - allPassPointsMap.put(i, new ArrayList<>()); - allPassPointsMap.get(i).add(location); - } - } - StatsService service = StatsService.get(); - for (Map.Entry> entry : allPassPointsMap.entrySet()) - { - double total = 0; - List list = entry.getValue(); - - MathStat stats = service.getStats(ArrayUtils.toPrimitive(list.toArray(new Double[list.size()]))); - double median = stats.getMedian(); - int largestkey = averagePassPointMap.keySet().size() == 0 ? 0 : Collections.max(averagePassPointMap.keySet()); - int tolerance = 500; // pases must be at least 500 runs away from eachother - if(averagePassPointMap.size() == 0 || - (averagePassPointMap.get(largestkey) != null && median > tolerance + averagePassPointMap.get(largestkey))) { - averagePassPointMap.put(largestkey+1, median); - } - } - int i = averagePassPointMap.size(); - JSONObject jo = new JSONObject(); - jo.put("passes", averagePassPointMap.values()); - jo.put("runs", memoryusagebyrun); - - JSONObject mainObj = new JSONObject(); - mainObj.put("graphJSON", jo); - return jo; - } - - public Map> getUserToRunsMap(Date selectedDate) { - // treemap sorted by usernames - Map> map = new TreeMap<>(new Comparator() { - public int compare(User u1, User u2) { - return u1.getUsername().compareTo(u2.getUsername()); - } - }); - Calendar cal = Calendar.getInstance(); - cal.setTime(selectedDate); - cal.set(Calendar.HOUR_OF_DAY, 8); - cal.set(Calendar.MINUTE, 1); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - selectedDate = cal.getTime(); - cal.add(Calendar.DATE, -1); - Date dateBefore1Day = cal.getTime(); - - for(RunDetail run: getRuns()) { // Currently uses getPostTime() (time of post) for same day instead of getTimestamp() which is the actual timestamp run started - Date runDate = run.getPostTime(); - if(runDate == null) - runDate = run.getPostTime(); - boolean isSameDay = (runDate.getTime() < selectedDate.getTime() && runDate.getTime() > dateBefore1Day.getTime()); - if(!isSameDay) - continue; - if(map.get(getUserById(run.getUserid())) == null) - map.put(getUserById(run.getUserid()), new ArrayList<>()); - map.get(getUserById(run.getUserid())).add(run); - } - return map; - } - /* - * given a run @testRunId will return a JSON formatted map of memory data - * because of browser limitations one out of every ten points is chosen to be displayed - */ - public JSONObject getMemoryJson(int testRunId, boolean isStatRun) { - TestPassDetail[] mostRecent = getPassesByRunId(testRunId, isStatRun); - Arrays.sort(mostRecent); - List totalMemory = new ArrayList<>(); - List managedMemory = new ArrayList<>(); - List testName = new ArrayList<>(); - int index = 0; - for(TestPassDetail testPassDetails : mostRecent) { - if(index % 10 == 0) { - String thisTestName = testPassDetails.getTestName() + " Pass:" + testPassDetails.getPass(); - totalMemory.add(testPassDetails.getTotalMemory()); - managedMemory.add(testPassDetails.getManagedMemory()); - testName.add(thisTestName); - } - index++; - } - - JSONObject jo = new JSONObject(); - jo.put("totalMemory", totalMemory); - jo.put("managedMemory", managedMemory); - jo.put("testName", testName); - - JSONArray ja = new JSONArray(); - ja.put(jo); - - JSONObject mainObj = new JSONObject(); - mainObj.put("json", ja); - return mainObj; - } - - /* returns a map of (n)top leaks and their corresponding TestLeakDetail objects - *sorts by most frequent leak then avg leak size if frequencies are the same - * if 0 is passed in will return ALL leaks - */ - public Map> getTopLeaks(int n, boolean isStatRun) { - TestsDataBean runs = new TestsDataBean(getRuns()); - if(isStatRun) - runs = new TestsDataBean(getStatRuns()); - Map> m = new HashMap<>(); - TestLeakDetail[] leaks = runs.getLeaks(); - for(TestLeakDetail leak: leaks) { - List list = m.get(leak.getTestName()); - if (list == null) { - list = new ArrayList<>(); - m.put(leak.getTestName(), list); - } - list.add(leak); - } - - Map.Entry>[] entries = m.entrySet().toArray(new Map.Entry[0]); - Arrays.sort(entries, new Comparator>>() - { - @Override - public int compare(Map.Entry> o1, Map.Entry> o2) - { - if (o2.getValue().size() - o1.getValue().size() == 0) - { - double[] leak1bytes = new double[o1.getValue().size()]; - double[] leak2bytes = new double[o2.getValue().size()]; - for (int i = 0; i < o1.getValue().size(); i++) - { - leak1bytes[i] = o1.getValue().get(i).getBytes(); - } - for (int i = 0; i < o2.getValue().size(); i++) - { - leak2bytes[i] = o2.getValue().get(i).getBytes(); - } - StatsService service = StatsService.get(); - MathStat l1 = service.getStats(leak1bytes); - MathStat l2 = service.getStats(leak2bytes); - return (int) (l1.getMean() - l2.getMean()); - } - - return o2.getValue().size() - o1.getValue().size(); - } - }); - Map> newMap = new LinkedHashMap<>(); - if(n == 0) - n = entries.length; - for (int i = 0; i < n && i < entries.length; i++) { - newMap.put(entries[i].getKey(), entries[i].getValue()); - } - - return newMap; - } - - /* returns a map of (n)top failed tests and their corresponding TestFailDetail objects - * sorts by most frequent failure then failure name if frequencies are the same - * if 0 is passed in will return all failures - */ - public Map> getTopFailures(int n, boolean isStatRun) { - Map> m = new HashMap<>(); - TestsDataBean runs = new TestsDataBean(getRuns()); - if(isStatRun) - runs = new TestsDataBean(getStatRuns()); - TestFailDetail[] failures = runs.getFailures(); - for(TestFailDetail fail: failures) { - List list = m.get(fail.getTestName()); - if (list == null) { - list = new ArrayList<>(); - m.put(fail.getTestName(), list); - } - list.add(fail); - } - Map.Entry>[] entries = m.entrySet().toArray(new Map.Entry[0]); - Arrays.sort(entries, new Comparator>>() - { - @Override - public int compare(Map.Entry> o1, Map.Entry> o2) - { - if (o2.getValue().size() == o1.getValue().size()) - return o1.getKey().compareTo(o2.getKey()); - return o2.getValue().size() - o1.getValue().size(); - } - }); - if(n == 0) - n = entries.length; - Map> newMap = new LinkedHashMap<>(); - for (int i = 0; i < n && i < entries.length; i++) { - newMap.put(entries[i].getKey(), entries[i].getValue()); - } - - return newMap; - } - - /* - * returns a map of mean, min, and max values for Duration, Test Runs, Failures, and Leaks - */ - public Map getAvgMinMaxTableData(boolean isStatRun) { - Map map = new HashMap<>(); - StatsService service = StatsService.get(); - - // Calculate durations - TestsDataBean runsBean; - if(isStatRun) - runsBean = new TestsDataBean(getStatRuns()); - else - runsBean = new TestsDataBean(getRuns()); - RunDetail[] runs = runsBean.getRuns(); - double[] durations = new double[runs.length]; - for(int i = 0; i < runs.length; i++) - durations[i] = runs[i].getDuration(); - - MathStat duration = service.getStats(durations); - double[] toMap = {duration.getMean(), duration.getMinimum(), duration.getMaximum()}; - map.put("duration", toMap); - - // Calculate test passes, failures, and leak statistics - List passed = new ArrayList<>(); - List failed = new ArrayList<>(); - List leaked = new ArrayList<>(); - for(RunDetail run: runs) { - int passCount = run.getPasses() == null ? 0 : run.getPassedtests(); - int failCount = run.getFailures() == null ? 0 : run.getFailedtests(); - int leakCount = run.getLeaks() == null ? 0 : run.getLeakedtests(); - - passed.add((double)passCount); - failed.add((double)failCount); - leaked.add((double)leakCount); - } - Double[] passedTests = passed.toArray(new Double[passed.size()]); - Double[] failedTests = failed.toArray(new Double[failed.size()]); - Double[] leakedTests = leaked.toArray(new Double[leaked.size()]); - - MathStat passStats = service.getStats(ArrayUtils.toPrimitive(passedTests)); - double[] testRuns = {passStats.getMean(), passStats.getMinimum(), passStats.getMaximum()}; - map.put("testruns", testRuns); - - MathStat failStats = service.getStats(ArrayUtils.toPrimitive(failedTests)); - double[] testFailures = {failStats .getMean(), failStats.getMinimum(), failStats.getMaximum()}; - map.put("failures", testFailures); - - MathStat leakStats = service.getStats(ArrayUtils.toPrimitive(leakedTests)); - double[] testLeaks = {leakStats.getMean(), leakStats.getMinimum(), leakStats.getMaximum()}; - map.put("leaks", testLeaks); - - return map; - } - - public User[] getUsers() - { - return users; - } - - public void setUsers(User[] users) - { - this.users = users; - } - - // returns true if the run with id @runId can be used in a statistical analysis - // if a run has no test runs returns false - public boolean excludeRun(int runId) { - RunDetail run = runs.get(runId); - if(run == null) - return true; - if(run.isFlagged()) - return true; - return false; - } - - public void setNonAssociatedFailures(TestFailDetail[] nonAssociatedFailures) - { - this.nonAssociatedFailures = nonAssociatedFailures; - } - public JSONObject getRunsPerDayJson() { - Map m = new TreeMap<>(); - DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); - Calendar cal = Calendar.getInstance(); - for(RunDetail run : getRuns()) { - cal.setTime(run.getTimestamp()); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - long time = cal.getTimeInMillis(); - Date d = new Date(time); - String dateString = df.format(d); - if(!m.containsKey(dateString)) { - m.put(dateString, 0); - } - int count = m.get(dateString); - m.put(dateString, count+1); - } - return new JSONObject(m); - } - public JSONObject getFailuresJson() - { - Map>> m = new TreeMap<>(); - - Calendar cal = Calendar.getInstance(); - DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); - for (TestFailDetail fail: nonAssociatedFailures) - { - cal.setTime(fail.getTimestamp()); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - long time = cal.getTimeInMillis(); - Date d = new Date(time); - String dateString = df.format(d); - fail.setTimestamp(d); - - if(!m.containsKey(dateString)) { - m.put(dateString, new ArrayList<>()); - } - Map failDetails = new TreeMap<>(); - failDetails.put("testname", fail.getTestName()); - failDetails.put("language", fail.getLanguage()); - m.get(dateString).add(failDetails); - - } - JSONObject json = new JSONObject(m); - return json; - } -} diff --git a/testresults/src/org/labkey/testresults/model/BackgroundColor.java b/testresults/src/org/labkey/testresults/model/BackgroundColor.java new file mode 100644 index 00000000..763c9f8d --- /dev/null +++ b/testresults/src/org/labkey/testresults/model/BackgroundColor.java @@ -0,0 +1,19 @@ +package org.labkey.testresults.model; + +public enum BackgroundColor +{ + pass("#caff95"), + error("#ffcaca"), + warn("#ffffca"), + unknown("#cccccc"); + + private final String htmlText; + + BackgroundColor(String text) { + htmlText = text; + } + + public String toString() { + return htmlText; + } +} diff --git a/testresults/src/org/labkey/testresults/RunDetail.java b/testresults/src/org/labkey/testresults/model/RunDetail.java similarity index 92% rename from testresults/src/org/labkey/testresults/RunDetail.java rename to testresults/src/org/labkey/testresults/model/RunDetail.java index da1d94d6..547eb671 100644 --- a/testresults/src/org/labkey/testresults/RunDetail.java +++ b/testresults/src/org/labkey/testresults/model/RunDetail.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.labkey.testresults; +package org.labkey.testresults.model; import org.labkey.api.data.Container; import org.labkey.api.reader.Readers; @@ -47,7 +47,7 @@ public class RunDetail implements Comparable private boolean isTrainRun; private TestFailDetail[] failures; // all failures which resulted in this run private TestPassDetail[] passes; // all passes(successful tests runs) in this run - private TestLeakDetail[] leaks; // all leaks detected during this run + private TestMemoryLeakDetail[] testmemoryleaks; // all memory testmemoryleaks detected during this run private byte[] xml; // compressed xml @@ -75,7 +75,7 @@ public RunDetail(int userid, int duration, Date posttime, Date timestamp, String this.container = container; this.failures = new TestFailDetail[0]; this.passes = new TestPassDetail[0]; - this.leaks = new TestLeakDetail[0]; + this.testmemoryleaks = new TestMemoryLeakDetail[0]; this.flagged = flagged; this.timestamp = timestamp; this.xml = xml; @@ -136,14 +136,14 @@ public void setOs(String os) this.os = os; } - public TestLeakDetail[] getLeaks() + public TestMemoryLeakDetail[] getTestmemoryleaks() { - return leaks; + return testmemoryleaks; } - public void setLeaks(TestLeakDetail[] leaks) + public void setTestmemoryleaks(TestMemoryLeakDetail[] testmemoryleaks) { - this.leaks = leaks; + this.testmemoryleaks = testmemoryleaks; } public TestFailDetail[] getFailures() @@ -332,16 +332,6 @@ public Double[] getPoints() throws IOException { public int getAveragemem() { return averagemem; } - public String getUsername() - { - return username; - } - - public void setUsername(String username) - { - this.username = username; - } - public double getAverageMemory() { if(averagemem != 0) { return averagemem; diff --git a/testresults/src/org/labkey/testresults/TestFailDetail.java b/testresults/src/org/labkey/testresults/model/TestFailDetail.java similarity index 94% rename from testresults/src/org/labkey/testresults/TestFailDetail.java rename to testresults/src/org/labkey/testresults/model/TestFailDetail.java index 5c9d2b92..f68c988f 100644 --- a/testresults/src/org/labkey/testresults/TestFailDetail.java +++ b/testresults/src/org/labkey/testresults/model/TestFailDetail.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.labkey.testresults; +package org.labkey.testresults.model; import java.util.Date; diff --git a/testresults/src/org/labkey/testresults/model/TestHandleLeakDetail.java b/testresults/src/org/labkey/testresults/model/TestHandleLeakDetail.java new file mode 100644 index 00000000..d12afbe4 --- /dev/null +++ b/testresults/src/org/labkey/testresults/model/TestHandleLeakDetail.java @@ -0,0 +1,27 @@ +package org.labkey.testresults.model; + +public class TestHandleLeakDetail extends TestLeakDetail +{ + double handles; + + public TestHandleLeakDetail() { + + } + + public TestHandleLeakDetail(int testRunId, String name, String type, double handles) { + setTestRunId(testRunId); + setTestName(name); + setType(type); + this.handles = handles; + } + + public double getHandles() + { + return handles; + } + + public void setHandles(float handles) + { + this.handles = handles; + } +} diff --git a/testresults/src/org/labkey/testresults/TestLeakDetail.java b/testresults/src/org/labkey/testresults/model/TestLeakDetail.java similarity index 70% rename from testresults/src/org/labkey/testresults/TestLeakDetail.java rename to testresults/src/org/labkey/testresults/model/TestLeakDetail.java index c59cc8a0..a1d7ec69 100644 --- a/testresults/src/org/labkey/testresults/TestLeakDetail.java +++ b/testresults/src/org/labkey/testresults/model/TestLeakDetail.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.labkey.testresults; +package org.labkey.testresults.model; /** * User: Yuval Boss, yuval(at)uw.edu @@ -22,17 +22,17 @@ public class TestLeakDetail { private int testRunId; - private String testname; - private int bytes; // bytes leaked - + private String testName; + private String type; public TestLeakDetail() { } - public TestLeakDetail(int testRunId, String name, int bytes) { + + public TestLeakDetail(int testRunId, String name, String type) { this.testRunId = testRunId; - this.testname = name; - this.bytes = bytes; + this.testName = name; + this.type = type; } public int getTestRunId() @@ -45,23 +45,23 @@ public void setTestRunId(int testRunId) this.testRunId = testRunId; } - public int getBytes() + public String getTestName() { - return bytes; + return testName; } - public void setBytes(int bytes) + public void setTestName(String testname) { - this.bytes = bytes; + this.testName = testname; } - public String getTestName() + public String getType() { - return testname; + return type; } - public void setTestName(String testname) + public void setType(String type) { - this.testname = testname; + this.type = type; } } diff --git a/testresults/src/org/labkey/testresults/model/TestMemoryLeakDetail.java b/testresults/src/org/labkey/testresults/model/TestMemoryLeakDetail.java new file mode 100644 index 00000000..eff6edd0 --- /dev/null +++ b/testresults/src/org/labkey/testresults/model/TestMemoryLeakDetail.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015 LabKey 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 org.labkey.testresults.model; + +/** + * User: Yuval Boss, yuval(at)uw.edu + * Date: 1/14/2015 + */ +public class TestMemoryLeakDetail extends TestLeakDetail +{ + private int bytes; // bytes leaked + + public TestMemoryLeakDetail() { + + } + + public TestMemoryLeakDetail(int testRunId, String name, String type, int bytes) { + setTestRunId(testRunId); + setTestName(name); + setType(type); + this.bytes = bytes; + } + + public int getBytes() + { + return bytes; + } + + public void setBytes(int bytes) + { + this.bytes = bytes; + } + +} diff --git a/testresults/src/org/labkey/testresults/TestPassDetail.java b/testresults/src/org/labkey/testresults/model/TestPassDetail.java similarity index 72% rename from testresults/src/org/labkey/testresults/TestPassDetail.java rename to testresults/src/org/labkey/testresults/model/TestPassDetail.java index 6d2e952e..93a440d1 100644 --- a/testresults/src/org/labkey/testresults/TestPassDetail.java +++ b/testresults/src/org/labkey/testresults/model/TestPassDetail.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.labkey.testresults; +package org.labkey.testresults.model; import java.util.Date; @@ -32,13 +32,17 @@ public class TestPassDetail implements Comparable private int duration; private double managedMemory; private double totalMemory; + private double committedMemory; + private int userAndGDIHandles; + private int handles; private Date timestamp; public TestPassDetail() { } - public TestPassDetail(int testRunId, int pass, int testId, String testName, String language, int duration, double managedMemory, double totalMemory, Date timestamp) { + public TestPassDetail(int testRunId, int pass, int testId, String testName, String language, int duration, + double managedMemory, double totalMemory, double committedMemory, int userGdiHandles, int handles, Date timestamp) { this.testRunId = testRunId; this.pass = pass; this.testId = testId; @@ -47,6 +51,9 @@ public TestPassDetail(int testRunId, int pass, int testId, String testName, Stri this.duration = duration; this.managedMemory = managedMemory; this.totalMemory = totalMemory; + this.committedMemory = committedMemory; + this.userAndGDIHandles = userGdiHandles; + this.handles = handles; this.timestamp = timestamp; } @@ -90,8 +97,7 @@ public void setTestName(String testName) this.testName = testName; } - public String getLanguage() - { + public String getLanguage() { if(language == null) return "unknown"; // standard display value so that null values aren't ignored as that would skew data.. return language; @@ -144,8 +150,6 @@ public void setId(int id) public Date getTimestamp() { - if(timestamp == null) - return null; return timestamp; } @@ -154,6 +158,36 @@ public void setTimestamp(Date timestamp) this.timestamp = timestamp; } + public double getCommittedMemory() + { + return committedMemory; + } + + public void setCommittedMemory(double committedMemory) + { + this.committedMemory = committedMemory; + } + + public int getUserAndGDIHandles() + { + return userAndGDIHandles; + } + + public void setUserAndGDIHandles(int userAndGDIHandles) + { + this.userAndGDIHandles = userAndGDIHandles; + } + + public int getHandles() + { + return handles; + } + + public void setHandles(int handles) + { + this.handles = handles; + } + public int compareTo(TestPassDetail o2) { return this.getId() - o2.getId(); } diff --git a/testresults/src/org/labkey/testresults/User.java b/testresults/src/org/labkey/testresults/model/User.java similarity index 94% rename from testresults/src/org/labkey/testresults/User.java rename to testresults/src/org/labkey/testresults/model/User.java index c5f6674f..85776736 100644 --- a/testresults/src/org/labkey/testresults/User.java +++ b/testresults/src/org/labkey/testresults/model/User.java @@ -1,4 +1,4 @@ -package org.labkey.testresults; +package org.labkey.testresults.model; import org.labkey.api.data.Container; diff --git a/testresults/src/org/labkey/testresults/view/LongTermBean.java b/testresults/src/org/labkey/testresults/view/LongTermBean.java new file mode 100644 index 00000000..288b66e2 --- /dev/null +++ b/testresults/src/org/labkey/testresults/view/LongTermBean.java @@ -0,0 +1,90 @@ +package org.labkey.testresults.view; + + +import org.json.JSONObject; +import org.labkey.testresults.model.User; +import org.labkey.testresults.model.RunDetail; +import org.labkey.testresults.model.TestFailDetail; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class LongTermBean extends TestsDataBean +{ + private TestFailDetail[] nonAssociatedFailures; + + + public LongTermBean(RunDetail[] runs, User[] users) + { + super(runs, users); + } + + public LongTermBean(RunDetail[] runs, User[] users, String viewType, Date startDate, Date endDate) + { + super(runs, users, viewType, startDate, endDate); + } + + public void setNonAssociatedFailures(TestFailDetail[] nonAssociatedFailures) + { + this.nonAssociatedFailures = nonAssociatedFailures; + } + + public JSONObject getFailuresJson() + { + Map>> m = new TreeMap<>(); + + Calendar cal = Calendar.getInstance(); + DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); + for (TestFailDetail fail: nonAssociatedFailures) + { + cal.setTime(fail.getTimestamp()); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + long time = cal.getTimeInMillis(); + Date d = new Date(time); + String dateString = df.format(d); + fail.setTimestamp(d); + + if(!m.containsKey(dateString)) { + m.put(dateString, new ArrayList<>()); + } + Map failDetails = new TreeMap<>(); + failDetails.put("testname", fail.getTestName()); + failDetails.put("language", fail.getLanguage()); + m.get(dateString).add(failDetails); + + } + JSONObject json = new JSONObject(m); + return json; + } + + public JSONObject getRunsPerDayJson() { + Map m = new TreeMap<>(); + DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); + Calendar cal = Calendar.getInstance(); + for(RunDetail run : getRuns()) { + cal.setTime(run.getTimestamp()); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + long time = cal.getTimeInMillis(); + Date d = new Date(time); + String dateString = df.format(d); + if(!m.containsKey(dateString)) { + m.put(dateString, 0); + } + int count = m.get(dateString); + m.put(dateString, count+1); + } + return new JSONObject(m); + } +} diff --git a/testresults/src/org/labkey/testresults/view/RunDownBean.java b/testresults/src/org/labkey/testresults/view/RunDownBean.java new file mode 100644 index 00000000..ecefc96e --- /dev/null +++ b/testresults/src/org/labkey/testresults/view/RunDownBean.java @@ -0,0 +1,328 @@ +package org.labkey.testresults.view; + +import org.apache.commons.lang3.ArrayUtils; +import org.json.JSONObject; +import org.labkey.api.data.statistics.MathStat; +import org.labkey.api.data.statistics.StatsService; +import org.labkey.api.services.ServiceRegistry; +import org.labkey.testresults.TestResultsController; +import org.labkey.testresults.model.TestMemoryLeakDetail; +import org.labkey.testresults.model.User; +import org.labkey.testresults.model.RunDetail; +import org.labkey.testresults.model.TestFailDetail; +import org.labkey.testresults.model.TestMemoryLeakDetail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class RunDownBean extends TestsDataBean +{ + public RunDownBean(RunDetail[] runs, User[] users) + { + super(runs, users); + } + + public RunDownBean(RunDetail[] runs, User[] users, String viewType, Date startDate, Date endDate) + { + super(runs, users, viewType, startDate, endDate); + } + + public Map> getUserToRunsMap(Date selectedDate) { + // treemap sorted by usernames + Map> map = new TreeMap<>(new Comparator() { + public int compare(User u1, User u2) { + return u1.getUsername().compareTo(u2.getUsername()); + } + }); + Calendar cal = Calendar.getInstance(); + cal.setTime(selectedDate); + cal.set(Calendar.HOUR_OF_DAY, 8); + cal.set(Calendar.MINUTE, 1); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + selectedDate = cal.getTime(); + cal.add(Calendar.DATE, -1); + Date dateBefore1Day = cal.getTime(); + + for(RunDetail run: getRuns()) { // Currently uses getPostTime() (time of post) for same day instead of getTimestamp() which is the actual timestamp run started + Date runDate = run.getPostTime(); + if(runDate == null) + runDate = run.getPostTime(); + boolean isSameDay = (runDate.getTime() < selectedDate.getTime() && runDate.getTime() > dateBefore1Day.getTime()); + if(!isSameDay) + continue; + if(map.get(getUserById(run.getUserid())) == null) + map.put(getUserById(run.getUserid()), new ArrayList<>()); + map.get(getUserById(run.getUserid())).add(run); + } + return map; + } + + /* returns a map of (n)top leaks and their corresponding TestMemoryLeakDetail objects + *sorts by most frequent leak then avg leak size if frequencies are the same + * if 0 is passed in will return ALL leaks + */ + public Map> getTopLeaks(int n, boolean isStatRun) { + TestsDataBean runs = new TestsDataBean(getRuns(), new User[0]); + if(isStatRun) + runs = new TestsDataBean(getStatRuns(), new User[0]); + Map> m = new HashMap<>(); + TestMemoryLeakDetail[] leaks = runs.getLeaks(); + for(TestMemoryLeakDetail leak: leaks) { + List list = m.get(leak.getTestName()); + if (list == null) { + list = new ArrayList<>(); + m.put(leak.getTestName(), list); + } + list.add(leak); + } + + Map.Entry>[] entries = m.entrySet().toArray(new Map.Entry[0]); + Arrays.sort(entries, new Comparator>>() + { + @Override + public int compare(Map.Entry> o1, Map.Entry> o2) + { + if (o2.getValue().size() - o1.getValue().size() == 0) + { + double[] leak1bytes = new double[o1.getValue().size()]; + double[] leak2bytes = new double[o2.getValue().size()]; + for (int i = 0; i < o1.getValue().size(); i++) + { + leak1bytes[i] = o1.getValue().get(i).getBytes(); + } + for (int i = 0; i < o2.getValue().size(); i++) + { + leak2bytes[i] = o2.getValue().get(i).getBytes(); + } + StatsService service = ServiceRegistry.get().getService(StatsService.class); + MathStat l1 = service.getStats(leak1bytes); + MathStat l2 = service.getStats(leak2bytes); + return (int) (l1.getMean() - l2.getMean()); + } + + return o2.getValue().size() - o1.getValue().size(); + } + }); + Map> newMap = new LinkedHashMap<>(); + if(n == 0) + n = entries.length; + for (int i = 0; i < n && i < entries.length; i++) { + newMap.put(entries[i].getKey(), entries[i].getValue()); + } + + return newMap; + } + + public User[] getMissingUsers(RunDetail[] daysRuns) { + User[] users = getUsers(); + if(users == null) + return new User[0]; + List missingUsers = new ArrayList<>(); + for(User u: users) { + if(u != null && u.isActive()) { + boolean userFound = false; + for(RunDetail r: daysRuns) { + if(r != null && r.getUserid() == u.getId()) { + userFound = true; + break; + } + } + if(!userFound) + missingUsers.add(u); + } + } + return missingUsers.toArray(new User[missingUsers.size()]); + } + + /* returns a map of (n)top failed tests and their corresponding TestFailDetail objects + * sorts by most frequent failure then failure name if frequencies are the same + * if 0 is passed in will return all failures + */ + public Map> getTopFailures(int n, boolean isStatRun) { + Map> m = new HashMap<>(); + TestsDataBean runs = new TestsDataBean(getRuns(), new User[0]); + if(isStatRun) + runs = new TestsDataBean(getStatRuns(), new User[0]); + TestFailDetail[] failures = runs.getFailures(); + for(TestFailDetail fail: failures) { + List list = m.get(fail.getTestName()); + if (list == null) { + list = new ArrayList<>(); + m.put(fail.getTestName(), list); + } + list.add(fail); + } + Map.Entry>[] entries = m.entrySet().toArray(new Map.Entry[0]); + Arrays.sort(entries, new Comparator>>() + { + @Override + public int compare(Map.Entry> o1, Map.Entry> o2) + { + if (o2.getValue().size() == o1.getValue().size()) + return o1.getKey().compareTo(o2.getKey()); + return o2.getValue().size() - o1.getValue().size(); + } + }); + if(n == 0) + n = entries.length; + Map> newMap = new LinkedHashMap<>(); + for (int i = 0; i < n && i < entries.length; i++) { + newMap.put(entries[i].getKey(), entries[i].getValue()); + } + + return newMap; + } + + /* + * returns an aggregated JSON map of tests memory usage for all runs stored in the bean + * if isToday is set to true then it will only include runs from today + */ + public JSONObject getTodaysCompactMemoryJson(Date day) throws Exception + { + RunDetail[] runs = getRuns(); + if(runs.length == 0) + return null; + List selectedRuns = new ArrayList<>(); + Map points = new HashMap<>(); + Map memoryusagebyrun = new HashMap<>(); + Calendar cal = Calendar.getInstance(); + cal.setTime(day); + cal.set(Calendar.HOUR_OF_DAY, 8); + cal.set(Calendar.MINUTE, 1); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + day = cal.getTime(); + cal.add(Calendar.DATE, -1); + Date dateBefore1Day = cal.getTime(); + for (RunDetail run:runs) { + boolean isSameDay = (run.getPostTime().getTime() < day.getTime() && run.getPostTime().getTime() > dateBefore1Day.getTime()); + if(isSameDay && !run.isFlagged()) { // filter by today's runs + if(run.getPointsummary() == null) + throw new Exception("null memory json for run id=" + run.getId()); + selectedRuns.add(run); + points.put(run.getUserName() + "(id." + run.getId() + ")", run.getPoints()); + } + } + + Map> allPassPointsMap = new HashMap<>(); + Map averagePassPointMap = new HashMap<>(); + + for (Map.Entry entry : points.entrySet()) { + if(entry.getValue().length ==0) + continue; + + Double[] encodedArray = entry.getValue(); + int index = Arrays.asList(encodedArray).indexOf(-1.0); + if(index == -1) + continue; + + Double[] passLocations = new Double[index]; + Double[] memoryusage = new Double[encodedArray.length-index-1]; // -1 because we no longer include the -1 flag + System.arraycopy(encodedArray, 0, passLocations, 0, index); + System.arraycopy(encodedArray, index+1, memoryusage, 0, encodedArray.length-index-1); + int[] memoryusageint = new int[memoryusage.length]; + for(int i = 0; i < memoryusage.length; i++) + memoryusageint[i] = memoryusage[i].intValue(); + + memoryusagebyrun.put(entry.getKey(), memoryusageint); + for(int i = 0; i < passLocations.length; i++) { + double location = passLocations[i]* TestResultsController.POINT_RATIO; + if(location == 0.0) + continue; + if(allPassPointsMap.get(i) == null) + allPassPointsMap.put(i, new ArrayList<>()); + allPassPointsMap.get(i).add(location); + } + } + StatsService service = ServiceRegistry.get().getService(StatsService.class); + for (Map.Entry> entry : allPassPointsMap.entrySet()) + { + List list = entry.getValue(); + + MathStat stats = service.getStats(ArrayUtils.toPrimitive(list.toArray(new Double[list.size()]))); + double median = stats.getMedian(); + int largestkey = averagePassPointMap.keySet().size() == 0 ? 0 : Collections.max(averagePassPointMap.keySet()); + int tolerance = 500; // pases must be at least 500 runs away from eachother + if(averagePassPointMap.size() == 0 || + (averagePassPointMap.get(largestkey) != null && median > tolerance + averagePassPointMap.get(largestkey))) { + averagePassPointMap.put(largestkey+1, median); + } + } + int i = averagePassPointMap.size(); + JSONObject jo = new JSONObject(); + jo.put("passes", averagePassPointMap.values()); + jo.put("runs", memoryusagebyrun); + + JSONObject mainObj = new JSONObject(); + mainObj.put("graphJSON", jo); + return jo; + } + + // returns a map of test name to list of TestLeakDetails for a specified day + public Map> getLeaksByDate(Date d, boolean isStatRun) { + Map> m = new TreeMap>(); + RunDetail[] dayRuns = getRunsByDate(d, true); + for(RunDetail r: dayRuns) { + if(isStatRun && !excludeRun(r.getId())) + { + for(TestMemoryLeakDetail l: r.getTestmemoryleaks()) { + if(!m.containsKey(l.getTestName())) + m.put(l.getTestName(), new ArrayList()); + m.get(l.getTestName()).add(l); + } + } + } + return m; + } + + // returns a map of test name to list of TestFailDetails for a specified day + public Map> getFailedTestsByDate(Date d, boolean isStatRun) { + Map> m = new TreeMap>(); + RunDetail[] dayRuns = getRunsByDate(d, true); + for(RunDetail r: dayRuns) { + if(isStatRun && !excludeRun(r.getId())) + { + for(TestFailDetail f: r.getFailures()) { + if(!m.containsKey(f.getTestName())) + { + m.put(f.getTestName(), new ArrayList()); + } + m.get(f.getTestName()).add(f); + } + } + } + return m; + } + + public RunDetail[] getRunsByDate(Date day, boolean isStatRun) { + List runByDay = new ArrayList(); + Calendar cal = Calendar.getInstance(); + cal.setTime(day); + cal.set(Calendar.HOUR_OF_DAY, 8); + cal.set(Calendar.MINUTE, 1); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + day = cal.getTime(); + cal.add(Calendar.DATE, -1); + Date dateBefore1Day = cal.getTime(); + for(RunDetail runDetail:getStatRuns()) { + boolean isSameDay = (runDetail.getPostTime().getTime() < day.getTime() && runDetail.getPostTime().getTime() > dateBefore1Day.getTime()); + if(isSameDay) + runByDay.add(runDetail); + } + runByDay.sort(null); + RunDetail[] returnedRuns = runByDay.toArray(new RunDetail[runByDay.size()]); + return returnedRuns; + } + +} diff --git a/testresults/src/org/labkey/testresults/view/TestsDataBean.java b/testresults/src/org/labkey/testresults/view/TestsDataBean.java new file mode 100644 index 00000000..9d725424 --- /dev/null +++ b/testresults/src/org/labkey/testresults/view/TestsDataBean.java @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2015 LabKey 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 org.labkey.testresults.view; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.testresults.model.RunDetail; +import org.labkey.testresults.model.TestMemoryLeakDetail; +import org.labkey.testresults.model.User; +import org.labkey.testresults.model.TestFailDetail; +import org.labkey.testresults.model.TestPassDetail; + +import java.lang.String; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * User: Yuval Boss, yuval(at)uw.edu + * Date: 1/14/2015 + */ +public class TestsDataBean +{ + private Map runs; + private RunDetail[] statRuns; + private User[] users; + private String viewType; + private Date startDate; + private Date endDate; + + public TestsDataBean(RunDetail[] runs, User[] users) { + setRuns(runs); + statRuns = setStatRuns(); + setUsers(users); + } + + public TestsDataBean(RunDetail[] runs, User[] users, String viewType, Date startDate, Date endDate) { + this(runs, users); + setViewType(viewType); + setStartDate(startDate); + setEndDate(endDate); + } + + public Date getStartDate() + { + if(startDate == null) + startDate = new Date(); + return startDate; + } + + public void setStartDate(Date startDate) + { + this.startDate = startDate; + } + + public Date getEndDate() + { + if(endDate == null) + setEndDate(new Date()); + return endDate; + } + + public void setEndDate(Date day) + { + this.endDate = day; + } + + public String getViewType() + { + return viewType; + } + + public void setViewType(String viewType) + { + this.viewType = viewType; + } + + + + // Getters and Setters for fields + public RunDetail[] getRuns() { + RunDetail[] r = runs.values().toArray(new RunDetail[runs.size()]); + Arrays.sort(r); + return r; + } + public RunDetail[] setStatRuns() { + List statRuns = new ArrayList<>(); + for(RunDetail run: runs.values()) + if(!excludeRun(run.getId())) + statRuns.add(run); + Collections.sort(statRuns); + return statRuns.toArray(new RunDetail[statRuns.size()]); + } + + public RunDetail[] getStatRuns() { + return statRuns; + } + + public Map> getLanguageBreakdown(Map> topFailures){ + Map> m = new TreeMap>(); + for(String f: topFailures.keySet()) { + double total = 0.0; + if(!m.containsKey(f)) + m.put(f, new TreeMap()); + List l = topFailures.get(f); + for(TestFailDetail detail: l) { + if(detail != null) { + if(!m.get(f).containsKey(detail.getLanguage())) + m.get(f).put(detail.getLanguage(), 0.0); + m.get(f).put(detail.getLanguage(), m.get(f).get(detail.getLanguage()) + 1); + total += 1; + } + } + for(String language: m.get(f).keySet()) { + m.get(f).put(language, m.get(f).get(language) / total); + } + } + + return m; + } + + public double round(double value, int places) { + if (places < 0) throw new IllegalArgumentException(); + + long factor = (long) Math.pow(10, places); + value = value * factor; + long tmp = Math.round(value); + return (double) tmp / factor; + } + + public void setRuns(RunDetail[] runs) { + this.runs = new LinkedHashMap<>(); + addRuns(runs); + statRuns = setStatRuns(); + } + + public TestMemoryLeakDetail[] getLeaks() { + List leaks = new ArrayList<>(); + for(RunDetail r: runs.values()) { + leaks.addAll(Arrays.asList(r.getTestmemoryleaks())); + } + return leaks.toArray(new TestMemoryLeakDetail[leaks.size()]); + } + + public TestFailDetail[] getFailures() { + List fails = new ArrayList<>(); + for(RunDetail r: runs.values()) + fails.addAll(Arrays.asList(r.getFailures())); + return fails.toArray(new TestFailDetail[fails.size()]); + } + + public TestFailDetail[] getFailuresByName(String testName) { + List fails = new ArrayList<>(); + for(RunDetail r: runs.values()) + for(TestFailDetail f: r.getFailures()) + if(f.getTestName().equals(testName)) + fails.add(f); + return fails.toArray(new TestFailDetail[fails.size()]); + } + + public TestPassDetail[] getPasses() { + List passes = new ArrayList<>(); + for(RunDetail run : runs.values()) + passes.addAll(Arrays.asList(run.getPasses())); + return passes.toArray(new TestPassDetail[passes.size()]); + } + + private void addRuns(RunDetail[] runs) { + if(runs == null) + return; + for (RunDetail run : runs) { + // round all memory data + TestPassDetail[] passes = run.getPasses(); + if(passes != null && passes.length > 0 && passes[0] != null) { + for(int i = 0; i > dates = new TreeMap>(); + Calendar cal = Calendar.getInstance(); + for(RunDetail run: getStatRuns()) { + cal.setTime(run.getPostTime()); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + Date strippedDay = cal.getTime(); + List list = dates.get(strippedDay); + if (list == null) { + dates.put(strippedDay, list = new ArrayList<>()); + } + list.add(run); + } + + int size = dates.size(); + double[] avgDuration = new double[size]; + double[] avgTestRuns = new double[size]; + int[] avgMemory = new int[size]; + double[] avgFailures = new double[size]; + int i = 0; + for(Map.Entry> entry : dates.entrySet()) { + List runs = entry.getValue(); + int passTotal = 0; + int failTotal = 0; + int avgMemoryTotal = 0; + int durationTotal = 0; + for(RunDetail run: runs) { + passTotal += run.getPassedtests(); + failTotal += run.getFailedtests(); + durationTotal += run.getDuration(); + avgMemoryTotal += run.getAverageMemory(); + } + avgTestRuns[i] = round((double) passTotal/runs.size(),2); + avgFailures[i] = round(((double)failTotal)/runs.size(),2); + avgMemory[i] = avgMemoryTotal/runs.size(); + avgDuration[i] = round(((double)durationTotal)/runs.size(),2); + i++; + } + long[] milliSecondDates = new long[dates.size()]; + int j = 0; + for(Date d: dates.keySet()) { + milliSecondDates[j] = d.getTime(); + j++; + } + // create and populate the JSONObject that will be returned + JSONObject jo = new JSONObject(); + jo.put("avgDuration", avgDuration); + jo.put("avgMemory", avgMemory); + jo.put("avgFailures", avgFailures); + jo.put("avgTestRuns", avgTestRuns); + jo.put("dates", milliSecondDates); + + return jo; + } + + /* + * given a run @testRunId will return a JSON formatted map of memory data + * because of browser limitations one out of every ten points is chosen to be displayed + */ + public JSONObject getMemoryJson(int testRunId, boolean isStatRun) { + TestPassDetail[] mostRecent = getPassesByRunId(testRunId, isStatRun); + Arrays.sort(mostRecent); + List totalMemory = new ArrayList<>(); + List managedMemory = new ArrayList<>(); + List testName = new ArrayList<>(); + int index = 0; + for(TestPassDetail testPassDetails : mostRecent) { + if(index % 10 == 0) { + String thisTestName = testPassDetails.getTestName() + " Pass:" + testPassDetails.getPass(); + totalMemory.add(testPassDetails.getTotalMemory()); + managedMemory.add(testPassDetails.getManagedMemory()); + testName.add(thisTestName); + } + index++; + } + + JSONObject jo = new JSONObject(); + jo.put("totalMemory", totalMemory); + jo.put("managedMemory", managedMemory); + jo.put("testName", testName); + + JSONArray ja = new JSONArray(); + ja.put(jo); + + JSONObject mainObj = new JSONObject(); + mainObj.put("json", ja); + return mainObj; + } + + public User[] getUsers() + { + return users; + } + + public void setUsers(User[] users) + { + this.users = users; + } + + // returns true if the run with id @runId can be used in a statistical analysis + // if a run has no test runs returns false + public boolean excludeRun(int runId) { + RunDetail run = runs.get(runId); + if(run == null) + return true; + if(run.isFlagged()) + return true; + return false; + } +} diff --git a/testresults/src/org/labkey/testresults/view/errorFiles.jsp b/testresults/src/org/labkey/testresults/view/errorFiles.jsp new file mode 100644 index 00000000..bf420c03 --- /dev/null +++ b/testresults/src/org/labkey/testresults/view/errorFiles.jsp @@ -0,0 +1,101 @@ +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.settings.AppProps" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.testresults.TestResultsController" %> +<%@ page import="java.io.File" %> +<%@ page import="java.nio.file.Files" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> + +<% + /** + * User: Yuval Boss, yuval(at)uw.edu + * Date: 10/05/2015 + */ + JspView me = (JspView) HttpView.currentView(); + File[] errorFiles = (File[])me.getModelBean(); + final String contextPath = AppProps.getInstance().getContextPath(); + Container c = getContainer(); +%> + + + + +

All the files listed below at one point or another failed to post. When a run is successfully posted through this page it gets removed from the list.

+<%if(errorFiles.length == 0){ %> +

There are 0 failed xml files store.

+<%} else {%> + + +

+ + + <% + for(File f: errorFiles) { + if (f.getName().equals(".upload.log")) // LabKey system file + continue; %> + + + + + + <%}%> +
File NameDate Originally SavedMessage
<%=h(f.getName())%><%=h(Files.getLastModifiedTime(f.toPath()))%>

+<%}%> +

Files

+ \ No newline at end of file diff --git a/testresults/src/org/labkey/testresults/view/failureDetail.jsp b/testresults/src/org/labkey/testresults/view/failureDetail.jsp index c3f21683..f8e997d8 100644 --- a/testresults/src/org/labkey/testresults/view/failureDetail.jsp +++ b/testresults/src/org/labkey/testresults/view/failureDetail.jsp @@ -3,10 +3,10 @@ <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.testresults.RunDetail" %> -<%@ page import="org.labkey.testresults.TestFailDetail" %> +<%@ page import="org.labkey.testresults.model.RunDetail" %> +<%@ page import="org.labkey.testresults.model.TestFailDetail" %> <%@ page import="org.labkey.testresults.TestResultsController" %> -<%@ page import="org.labkey.testresults.TestsDataBean" %> +<%@ page import="org.labkey.testresults.view.TestsDataBean" %> <%@ page import="java.util.Arrays" %> <%@ page import="java.util.Collections" %> <%@ page import="java.util.Date" %> @@ -16,6 +16,8 @@ <%@ page import="java.util.ArrayList" %> <%@ page import="java.util.Comparator" %> <%@ page import="org.json.JSONObject" %> +<%@ page import="java.text.DateFormat" %> +<%@ page import="java.text.SimpleDateFormat" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% @@ -26,22 +28,26 @@ JspView me = (JspView) HttpView.currentView(); TestsDataBean data = (TestsDataBean)me.getModelBean(); final String contextPath = AppProps.getInstance().getContextPath(); - String viewType = getViewContext().getRequest().getParameter("viewType"); + String viewType = data.getViewType(); String failedTest = getViewContext().getRequest().getParameter("failedTest"); Container c = getContainer(); RunDetail[] runs = data.getStatRuns(); - Arrays.sort(runs, new Comparator() - { - @Override - public int compare(RunDetail o1, RunDetail o2) + if(runs.length > 1) { + Arrays.sort(runs, new Comparator() { - return o2.getRevision() - o1.getRevision(); - } - }); + @Override + public int compare(RunDetail o1, RunDetail o2) + { + return o2.getRevision() - o1.getRevision(); + } + }); + } Map> languageFailures = new TreeMap<>(); - languageFailures.put(failedTest, Arrays.asList(data.getFailuresByName(failedTest))); + languageFailures.put(failedTest, Arrays.asList(data.getFailures())); Map> languageBreakdown = data.getLanguageBreakdown(languageFailures); - List users = new ArrayList<>(); + + DateFormat df = new SimpleDateFormat("MM/dd/YYYY HH:mm"); + DateFormat dfEnd = new SimpleDateFormat("MM/dd/YYYY"); %> +
View Type: + +
@@ -83,47 +93,55 @@ -<%if(data != null && (viewType.equals("posttime") || viewType.equals("wk") || viewType.equals("mo") || viewType.equals("yr")|| viewType.equals("at"))) { %> +<%if(data.getStatRuns().length > 0) { %>

<%=h(failedTest)%>

-

Viewing data for: <%=h(viewType)%>

+

Viewing data for: <%=h(df.format(data.getStartDate()))%> - <%=h(df.format(data.getEndDate()))%>

Total failures: <%=h(runs.length)%>

-

Unique users: <%=h(users.size())%>


- - - - +
Sorted by revision
+ + + + + + + + + + - <% - Collections.reverse(Arrays.asList(runs)); // so most recent is on top - Map dates = new TreeMap(); // maps dates to count of failures per run - for(RunDetail run: runs) { %> + + + <% + Map dates = new TreeMap(); // maps dates to count of failures per run + for(RunDetail run: runs) { %> + + + + + + <%}%> +
UserPost TimeDurationOSRevTotal Run FailuresHangsStackTrace
-

- <%=h(run.getUsername())%>
- Duration: <%=h(run.getDuration())%>
- OS: <%=h(run.getOs())%>
- Post Time: <%=h(run.getPostTime())%>
- Rev: <%=h(run.getRevisionFull())%>
- Run Failures: <%=h(run.getFailures().length)%> -

+ + <%=h(run.getUserName())%> +
<%=h(df.format(run.getPostTime()))%><%=h(run.getDuration())%><%=h(run.getOs())%><%=h(run.getRevisionFull())%><%=h(run.getFailedtests())%><%=h(run.hasHang())%> <% int failCounter = 0; for(TestFailDetail fail: run.getFailures()) { if(fail.getTestName().equals(failedTest)) { failCounter++; %> -
Pass: <%=h(fail.getPass())%> Language: <%=h(fail.getLanguage())%> --- <%=h(fail.getStacktrace())%>
-                        
- <%} - }%> +
Pass: <%=h(fail.getPass())%> Language: <%=h(fail.getLanguage())%> --- <%=h(fail.getStacktrace())%>
+                            
+ <%} + }%> <% if(!dates.containsKey(run.getPostTime())) dates.put(run.getPostTime(), 0); @@ -133,8 +151,10 @@
+ <%}%> -<%}%> \ No newline at end of file +<%}%> + + \ No newline at end of file diff --git a/testresults/src/org/labkey/testresults/view/flagged.jsp b/testresults/src/org/labkey/testresults/view/flagged.jsp index b88507ff..8d941c4f 100644 --- a/testresults/src/org/labkey/testresults/view/flagged.jsp +++ b/testresults/src/org/labkey/testresults/view/flagged.jsp @@ -4,8 +4,8 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.testresults.TestResultsController" %> -<%@ page import="org.labkey.testresults.TestsDataBean" %> -<%@ page import="org.labkey.testresults.RunDetail" %> +<%@ page import="org.labkey.testresults.view.TestsDataBean" %> +<%@ page import="org.labkey.testresults.model.RunDetail" %> <%@ page import="java.util.Arrays" %> <%@ page import="java.util.Collections" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> @@ -29,6 +29,7 @@
  • -Long Term
  • -Flags
  • -Training Data
  • +
  • -Posting Errors
  • -Issues
  • diff --git a/testresults/src/org/labkey/testresults/view/longTerm.jsp b/testresults/src/org/labkey/testresults/view/longTerm.jsp index b92b97df..26fc6318 100644 --- a/testresults/src/org/labkey/testresults/view/longTerm.jsp +++ b/testresults/src/org/labkey/testresults/view/longTerm.jsp @@ -7,8 +7,8 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.testresults.TestResultsController" %> -<%@ page import="org.labkey.testresults.TestsDataBean" %> <%@ page import="static org.labkey.testresults.TestResultsModule.ViewType" %> +<%@ page import="org.labkey.testresults.view.LongTermBean" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% @@ -17,7 +17,7 @@ * Date: 1/14/2015 */ JspView me = (JspView) HttpView.currentView(); - TestsDataBean data = (TestsDataBean)me.getModelBean(); + LongTermBean data = (LongTermBean)me.getModelBean(); final String contextPath = AppProps.getInstance().getContextPath(); String viewType = data.getViewType(); Container c = getContainer(); @@ -30,6 +30,7 @@
  • -Long Term
  • -Flags
  • -Training Data
  • +
  • -Posting Errors
  • -Issues
  • diff --git a/testresults/src/org/labkey/testresults/view/multiFailureDetail.jsp b/testresults/src/org/labkey/testresults/view/multiFailureDetail.jsp new file mode 100644 index 00000000..abd11c34 --- /dev/null +++ b/testresults/src/org/labkey/testresults/view/multiFailureDetail.jsp @@ -0,0 +1,230 @@ +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.settings.AppProps" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.testresults.model.RunDetail" %> +<%@ page import="org.labkey.testresults.model.TestFailDetail" %> +<%@ page import="org.labkey.testresults.TestResultsController" %> +<%@ page import="org.labkey.testresults.view.TestsDataBean" %> +<%@ page import="java.util.Arrays" %> +<%@ page import="java.util.Collections" %> +<%@ page import="java.util.Date" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Map" %> +<%@ page import="java.util.TreeMap" %> +<%@ page import="java.util.ArrayList" %> +<%@ page import="java.util.Comparator" %> +<%@ page import="org.json.JSONObject" %> +<%@ page import="java.text.DateFormat" %> +<%@ page import="java.text.SimpleDateFormat" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> + +<% + /** + * User: Yuval Boss, yuval(at)uw.edu + * Date: 10/05/2015 + */ + JspView me = (JspView) HttpView.currentView(); + TestsDataBean data = (TestsDataBean)me.getModelBean(); + final String contextPath = AppProps.getInstance().getContextPath(); + String viewType = data.getViewType(); + Container c = getContainer(); + RunDetail[] runs = data.getStatRuns(); + if(runs.length > 1) { + Arrays.sort(runs, new Comparator() + { + @Override + public int compare(RunDetail o1, RunDetail o2) + { + return o2.getRevision() - o1.getRevision(); + } + }); + } + Map> languageFailures = new TreeMap<>(); + TestFailDetail[] fails = data.getFailures(); + for(TestFailDetail f: fails) { + if(!languageFailures.containsKey(f.getTestName())) + languageFailures.put(f.getTestName(), new ArrayList<>()); + languageFailures.get(f.getTestName()).add(f); + } + Map> languageBreakdown = data.getLanguageBreakdown(languageFailures); + List users = new ArrayList<>(); + + DateFormat df = new SimpleDateFormat("MM/dd HH:mm"); +%> + + + + + + +
    +
    + View Type: + +
    + + + + +<%if(data.getStatRuns().length > 0) { %> +
    +

    All Failures

    +

    Viewing data for: <%=h(df.format(data.getStartDate()))%> - <%=h(df.format(data.getEndDate()))%>

    +

    Total failures: <%=h(runs.length)%>

    +

    Unique users: <%=h(users.size())%>

    +
    +
    + +
    +
    + + + + + + <% + Collections.reverse(Arrays.asList(runs)); // so most recent is on top + Map dates = new TreeMap(); // maps dates to count of failures per run + for(RunDetail run: runs) { %> + + + + + <%}%> +
    Sorted by revision
    +

    + <%=h(run.getUserName())%>
    + Duration: <%=h(run.getDuration())%>
    + OS: <%=h(run.getOs())%>
    + Post Time: <%=h(run.getPostTime())%>
    + Rev: <%=h(run.getRevision())%>
    + Run Failures: <%=h(run.getFailures().length)%> +

    +
    + <% int failCounter = 0; + for(TestFailDetail fail: run.getFailures()) { + failCounter++; %> +
    Pass: <%=h(fail.getPass())%> Language: <%=h(fail.getLanguage())%> --- <%=h(fail.getStacktrace())%>
    +                        
    + <%}%> + <% + if(!dates.containsKey(run.getPostTime())) + dates.put(run.getPostTime(), 0); + int currentCount = dates.get(run.getPostTime()); + dates.put(run.getPostTime(),currentCount+failCounter); + %> +
    + + + + +<% + JSONObject failureTrends = new JSONObject(); + // populate json with failure count for each date + for(Map.Entry entry : dates.entrySet()) { + failureTrends.append("avgFailures", entry.getValue()); + failureTrends.append("dates", entry.getKey().getTime()); + } +%> +<% if(failureTrends != null && failureTrends.size() > 0) {%> + +<%}%> +<%}%> \ No newline at end of file diff --git a/testresults/src/org/labkey/testresults/view/runDetail.jsp b/testresults/src/org/labkey/testresults/view/runDetail.jsp index a43cf8e5..16e57f29 100644 --- a/testresults/src/org/labkey/testresults/view/runDetail.jsp +++ b/testresults/src/org/labkey/testresults/view/runDetail.jsp @@ -1,17 +1,18 @@ -<%@ page import="org.labkey.testresults.TestsDataBean" %> +<%@ page import="org.labkey.testresults.view.TestsDataBean" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.testresults.RunDetail" %> -<%@ page import="org.labkey.testresults.TestFailDetail" %> -<%@ page import="org.labkey.testresults.TestLeakDetail" %> +<%@ page import="org.labkey.testresults.model.RunDetail" %> +<%@ page import="org.labkey.testresults.model.TestFailDetail" %> +<%@ page import="org.labkey.testresults.model.TestMemoryLeakDetail" %> <%@ page import="java.text.DateFormat" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.testresults.TestResultsController" %> <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.settings.AppProps" %> -<%@ page import="org.labkey.testresults.TestPassDetail" %> +<%@ page import="org.labkey.testresults.model.TestPassDetail" %> <%@ page import="java.util.Arrays" %> +<%@ page import="org.labkey.testresults.model.TestMemoryLeakDetail" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% @@ -33,6 +34,7 @@
  • -Long Term
  • -Flags
  • -Training Data
  • +
  • -Posting Errors
  • -Issues
  • @@ -52,7 +54,7 @@ RunDetail run = data.getRuns()[0]; TestFailDetail[] failures = run.getFailures(); Arrays.sort(failures); // sorts by timestamp - TestLeakDetail[] leaks = run.getLeaks(); + TestMemoryLeakDetail[] testmemoryleaks = run.getTestmemoryleaks(); TestPassDetail[] passes = run.getPasses(); DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); DateFormat dfMDHM = new SimpleDateFormat("MM/dd HH:mm"); @@ -78,12 +80,12 @@   
    <%if(hasHang){%> (POSSIBLE HANG) <%}%>

    Run Id: <%=h(run.getId())%>
    - User : <%=h(run.getUserName())%>
    + User : <%=h(run.getUserName())%>
    OS: <%=h(run.getOs())%>
    Revision: <%=h(run.getRevisionFull())%>
    Passed Tests : <%=h(run.getPasses().length)%>
    Failures : <%=h(failures.length)%>
    - Leaks : <%=h(leaks.length)%>
    + Leaks : <%=h(testmemoryleaks.length)%>
    TimeStamp: <%=h((run.getTimestamp() == null) ? "N/A" : run.getTimestamp())%>
    <%=h((run.isTrainRun()) ? "Remove from training set" : "Add to training set")%> @@ -126,7 +128,7 @@ } }); $('#deleteRun').click(function(){ - var c = confirm("Press 'Ok' to delete this run and all associated passes, leaks, and test failures."); + var c = confirm("Press 'Ok' to delete this run and all associated passes, testmemoryleaks, and test failures."); if (c == true) { window.location.href = "<%=h(new ActionURL(TestResultsController.DeleteRunAction.class, c))%>" + "runId=<%=h(run.getId())%>"; } @@ -135,7 +137,7 @@

    - + @@ -153,7 +155,7 @@
    Failed Tests
    - <%for(TestLeakDetail l: leaks) {%> + <%for(TestMemoryLeakDetail l: testmemoryleaks) {%> diff --git a/testresults/src/org/labkey/testresults/view/rundown.jsp b/testresults/src/org/labkey/testresults/view/rundown.jsp index 54f00f47..b25f0b92 100644 --- a/testresults/src/org/labkey/testresults/view/rundown.jsp +++ b/testresults/src/org/labkey/testresults/view/rundown.jsp @@ -2,23 +2,23 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.data.statistics.MathStat" %> <%@ page import="org.labkey.api.data.statistics.StatsService" %> -<%@ page import="org.labkey.api.services.ServiceRegistry" %> <%@ page import="org.labkey.api.settings.AppProps" %> <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.testresults.RunDetail" %> -<%@ page import="org.labkey.testresults.TestFailDetail" %> -<%@ page import="org.labkey.testresults.TestLeakDetail" %> <%@ page import="org.labkey.testresults.TestResultsController" %> -<%@ page import="org.labkey.testresults.TestsDataBean" %> -<%@ page import="org.labkey.testresults.User" %> +<%@ page import="org.labkey.testresults.model.RunDetail" %> +<%@ page import="org.labkey.testresults.model.TestFailDetail" %> +<%@ page import="org.labkey.testresults.model.TestMemoryLeakDetail" %> +<%@ page import="org.labkey.testresults.model.User" %> +<%@ page import="org.labkey.testresults.view.RunDownBean" %> <%@ page import="java.text.DateFormat" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="java.util.Date" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> <%@ page import="static org.labkey.testresults.TestResultsModule.ViewType" %> +<%@ page import="org.labkey.testresults.model.BackgroundColor" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% @@ -27,7 +27,7 @@ * Date: 1/14/2015 */ JspView me = (JspView)HttpView.currentView(); - TestsDataBean data = (TestsDataBean)me.getModelBean(); + RunDownBean data = (RunDownBean)me.getModelBean(); final String contextPath = AppProps.getInstance().getContextPath(); DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); @@ -49,12 +49,12 @@ User[] missingUsers = data.getMissingUsers(data.getRunsByDate(selectedDate, false)); // Calculates Mean, Min, Max table Map> topFailures = data.getTopFailures(10, true); // top 10 failures - Map> topLeaks = data.getTopLeaks(10, true); // top 10 leaks + Map> topLeaks = data.getTopLeaks(10, true); // top 10 leaks StatsService service = StatsService.get(); Map> languageBreakdown = data.getLanguageBreakdown(topFailures); // test name mapped to language and percents Map> todaysFailures = data.getFailedTestsByDate(selectedDate, true); - Map> todaysLeaks = data.getLeaksByDate(selectedDate, true); + Map> todaysLeaks = data.getLeaksByDate(selectedDate, true); JSONObject memoryChartData = data.getTodaysCompactMemoryJson(selectedDate); JSONObject trendsJson = data.getTrends(); Container c = getViewContext().getContainer(); @@ -80,6 +80,7 @@
  • -Long Term
  • -Flags
  • -Training Data
  • +
  • -Posting Errors
  • -Issues
  • 0 runs to show @@ -125,50 +126,52 @@ <% int passes = run.getPassedtests(); int failures = run.getFailedtests(); - int leaks = run.getLeakedtests(); + int testmemoryleaks = run.getLeakedtests(); boolean isGoodRun = true; boolean highlightPasses = false; - String color="#00cc00;"; + String color= BackgroundColor.pass.toString(); String title= "Within 2 standard deviations of training data. "; - // ERROR: duration < 540, failures or leaks count > 0, more then 3 standard deviations away + // ERROR: duration < 540, failures or testmemoryleaks count > 0, more then 3 standard deviations away // WARNING: Between 2 and 3 standard deviations away // PASS: within 2 standard deviations away as well as not an ERROR or a WARNING int errorCount = errorRuns; - if(isGoodRun && (u.getMeanmemory() == 0d || u.getMeantestsrun() == 0d)) { // IF NO TRAINING DATA FOR USER - color="#cccccc;"; - title = "No training data for " + u.getUsername(); + if(isGoodRun && (u.getMeanmemory() == 0d || u.getMeantestsrun() == 0d || !u.isActive())) { // IF NO TRAINING DATA FOR USER or INACTIVE + color=BackgroundColor.unknown.toString(); + title = (u.getMeanmemory() == 0d || u.getMeantestsrun() == 0d) + ? "No training data for " + u.getUsername() + : "Deactivated " + u.getUsername(); isGoodRun = false; } if(isGoodRun && (!u.fitsMemoryTrainingData(run.getAverageMemory(), 3) || !u.fitsRunCountTrainingData(run.getPassedtests(), 3))) { - color="#ff0000;"; + color=BackgroundColor.error.toString(); isGoodRun = false; highlightPasses = true; title = "Outside 3 standard deviations of training data"; errorRuns++; } if(isGoodRun && (!u.fitsMemoryTrainingData(run.getAverageMemory(), 2) || !u.fitsRunCountTrainingData(run.getPassedtests(), 2))) { - color="#ffa500"; + color=BackgroundColor.warn.toString(); title="Between 2 and 3 standard deviations of training data"; isGoodRun = false; highlightPasses = true; warningRuns++; } - if(run.getDuration()< 540) { - color="#ff0000;"; + if(run.getDuration() < 539) { // 1 minute tolerance + color=BackgroundColor.error.toString(); isGoodRun = false; - title = "Duration not 540 minutes. "; + title = "Duration less than 540 minutes. "; if(errorCount == errorRuns) errorRuns++; } if(run.getFailures().length > 0) { - color="#ff0000;"; + color=BackgroundColor.error.toString(); isGoodRun = false; title += "One or more tests failed. "; if(errorCount == errorRuns) errorRuns++; } - if( run.getLeaks().length > 0) { - color="#ff0000;"; + if(run.getTestmemoryleaks().length > 0) { + color=BackgroundColor.error.toString(); isGoodRun = false; title += "Leaks were detected."; if(errorCount == errorRuns) @@ -185,13 +188,13 @@
    + <%if(run.getDuration()< 539) {%><%}%> - + <%}} @@ -199,16 +202,14 @@ <% for(User user: missingUsers) {%> - - - - - - - + + + <% for (int i = 0; i < 5; i++) { %> + + <% } %> - <%} - }%> + <% } + }%>
    LeaksBytes
    <%=h(l.getTestName())%> <%=h(l.getBytes()/1000 + "kb")%><%=h(dfMDHM.format(run.getPostTime()))%> <%=h(run.getDuration())%><%if(run.hasHang()){%> <%}%> - <%if(run.getDuration()< 540) {%><%}%> <%=h(passes)%> <%if(highlightPasses) {%><%}%> <%=h(failures)%> <%if(run.getFailures().length > 0) {%><%}%><%=h(leaks)%> - <%if(run.getLeaks().length > 0) {%><%}%><%=h(testmemoryleaks)%> + <%if(run.getTestmemoryleaks().length > 0) {%><%}%> " class="traindata"><%=h((run.isTrainRun()) ? "Untrain" : "Train")%>
    <%=h(user.getUsername())%>MISSING RUN<%=h(user.getUsername())%>MISSING RUN
    @@ -218,7 +219,7 @@ Fail: | Leak: <%for(User user: statRunUserMap.keySet()){ for(RunDetail run: statRunUserMap.get(user)) { - if(run.getFailures().length == 0 && run.getLeaks().length ==0) continue;%> + if(run.getFailures().length == 0 && run.getTestmemoryleaks().length ==0) continue;%> <%=h(user.getUsername()) + "("+run.getId()+")"%> <%} }%> @@ -228,10 +229,10 @@ <%for(Map.Entry> entry : todaysFailures.entrySet()) {%> - <%=h(entry.getKey())%> + <%=h(entry.getKey())%> <%for(User user: statRunUserMap.keySet()){ for(RunDetail run: statRunUserMap.get(user)){ - if(run.getFailures().length == 0 && run.getLeaks().length ==0) continue;%> + if(run.getFailures().length == 0 && run.getTestmemoryleaks().length ==0) continue;%> <% TestFailDetail matchingFail = null; @@ -251,16 +252,16 @@ <%}%> - <%for(Map.Entry> entry : todaysLeaks.entrySet()) {%> + <%for(Map.Entry> entry : todaysLeaks.entrySet()) {%> <%=h(entry.getKey())%> <%for(User user: statRunUserMap.keySet()){ for(RunDetail run: statRunUserMap.get(user)){ - if(run.getFailures().length == 0 && run.getLeaks().length ==0) continue;%> + if(run.getFailures().length == 0 && run.getTestmemoryleaks().length ==0) continue;%> <% - TestLeakDetail matchingLeak = null; + TestMemoryLeakDetail matchingLeak = null; - for(TestLeakDetail leak: entry.getValue()){ + for(TestMemoryLeakDetail leak: entry.getValue()){ if(leak.getTestRunId() ==run.getId()) { matchingLeak = leak; } } @@ -297,7 +298,7 @@ @@ -311,7 +312,7 @@

    - +
    <%if(topFailures.size() > 0){%> @@ -326,7 +327,7 @@ for(String key: topFailures.keySet()) { %> - <%=h(key)%> + <%=h(key)%> <%=h(topFailures.get(key).size())%>
    diff --git a/testresults/src/org/labkey/testresults/view/trainingdata.jsp b/testresults/src/org/labkey/testresults/view/trainingdata.jsp index 3448fe4b..892ec6e5 100644 --- a/testresults/src/org/labkey/testresults/view/trainingdata.jsp +++ b/testresults/src/org/labkey/testresults/view/trainingdata.jsp @@ -1,16 +1,17 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.testresults.TestsDataBean" %> +<%@ page import="org.labkey.testresults.view.TestsDataBean" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.settings.AppProps" %> -<%@ page import="org.labkey.testresults.User" %> +<%@ page import="org.labkey.testresults.model.User" %> -<%@ page import="org.labkey.testresults.RunDetail" %> +<%@ page import="org.labkey.testresults.model.RunDetail" %> <%@ page import="org.labkey.testresults.TestResultsController" %> <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.util.List" %> <%@ page import="org.labkey.testresults.SendTestResultsEmail" %> +<%@ page import="org.labkey.testresults.model.BackgroundColor" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% /** @@ -40,6 +41,7 @@
  • -Long Term
  • -Flags
  • -Training Data
  • +
  • -Posting Errors
  • -Issues
  • @@ -91,9 +93,9 @@ noRunsForUser.add(user); continue; } - String color = "red"; + String color = BackgroundColor.unknown.toString(); if(user.isActive()) - color = "green"; + color = BackgroundColor.pass.toString(); %> diff --git a/testresults/src/org/labkey/testresults/view/user.jsp b/testresults/src/org/labkey/testresults/view/user.jsp index 26ea6fb0..14258da8 100644 --- a/testresults/src/org/labkey/testresults/view/user.jsp +++ b/testresults/src/org/labkey/testresults/view/user.jsp @@ -7,11 +7,11 @@ <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.testresults.RunDetail" %> +<%@ page import="org.labkey.testresults.model.RunDetail" %> <%@ page import="org.labkey.testresults.TestResultsController" %> <%@ page import="org.labkey.testresults.TestResultsSchema" %> -<%@ page import="org.labkey.testresults.TestsDataBean" %> -<%@ page import="org.labkey.testresults.User" %> +<%@ page import="org.labkey.testresults.view.TestsDataBean" %> +<%@ page import="org.labkey.testresults.model.User" %> <%@ page import="java.text.DateFormat" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="java.util.ArrayList" %> @@ -73,6 +73,7 @@
  • -Long Term
  • -Flags
  • -Training Data
  • +
  • -Posting Errors
  • -Issues
  • @@ -162,7 +163,7 @@ - <%if(!showSingleUser){%><%}%> @@ -185,7 +186,7 @@ %> - <%if(!showSingleUser){%><%}%> + <%if(!showSingleUser){%><%}%> diff --git a/testresults/webapp/TestResults/css/style.css b/testresults/webapp/TestResults/css/style.css index b40a24b9..e4cc7754 100644 --- a/testresults/webapp/TestResults/css/style.css +++ b/testresults/webapp/TestResults/css/style.css @@ -109,8 +109,6 @@ background-color: #b0c4de; } .tablesorter td, .tablesorter th { - max-width:60px; - min-width:60px; overflow:hidden; padding-right:12px; } @@ -130,6 +128,8 @@ text-align:center; padding-right:12px; } + + /* tell the SVG path to be a thin blue line without any area fill */ path { stroke-width: 1; @@ -285,7 +285,7 @@ path { #menu { width: 100%; - height:30px; + height:40px; background-color: #4b2e83; margin: 0 !important; padding: 0 !important; diff --git a/testresults/webapp/TestResults/css/tablesorter-default.css b/testresults/webapp/TestResults/css/tablesorter-default.css new file mode 100644 index 00000000..6b602b47 --- /dev/null +++ b/testresults/webapp/TestResults/css/tablesorter-default.css @@ -0,0 +1,194 @@ +/************* +Default Theme +*************/ +/* overall */ +.tablesorter-default { + width: 100%; + font: 12px/18px Arial, Sans-serif; + color: #333; + background-color: #fff; + border-spacing: 0; + margin: 10px 0 15px; + text-align: left; +} + +/* header */ +.tablesorter-default th, +.tablesorter-default thead td { + font-weight: bold; + color: #000; + background-color: #fff; + border-collapse: collapse; + border-bottom: #ccc 2px solid; + padding: 0; +} +.tablesorter-default tfoot th, +.tablesorter-default tfoot td { + border: 0; +} +.tablesorter-default .header, +.tablesorter-default .tablesorter-header { + background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); + background-position: center right; + background-repeat: no-repeat; + cursor: pointer; + white-space: normal; + padding: 4px 20px 4px 4px; +} +.tablesorter-default thead .headerSortUp, +.tablesorter-default thead .tablesorter-headerSortUp, +.tablesorter-default thead .tablesorter-headerAsc { + background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); + border-bottom: #000 2px solid; +} +.tablesorter-default thead .headerSortDown, +.tablesorter-default thead .tablesorter-headerSortDown, +.tablesorter-default thead .tablesorter-headerDesc { + background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); + border-bottom: #000 2px solid; +} +.tablesorter-default thead .sorter-false { + background-image: none; + cursor: default; + padding: 4px; +} + +/* tfoot */ +.tablesorter-default tfoot .tablesorter-headerSortUp, +.tablesorter-default tfoot .tablesorter-headerSortDown, +.tablesorter-default tfoot .tablesorter-headerAsc, +.tablesorter-default tfoot .tablesorter-headerDesc { + border-top: #000 2px solid; +} + +/* tbody */ +.tablesorter-default td { + background-color: #fff; + border-bottom: #ccc 1px solid; + padding: 4px; + vertical-align: top; +} + +/* hovered row colors */ +.tablesorter-default tbody > tr.hover > td, +.tablesorter-default tbody > tr:hover > td, +.tablesorter-default tbody > tr.even:hover > td, +.tablesorter-default tbody > tr.odd:hover > td { + background-color: #fff; + color: #000; +} + +/* table processing indicator */ +.tablesorter-default .tablesorter-processing { + background-position: center center !important; + background-repeat: no-repeat !important; + /* background-image: url(images/loading.gif) !important; */ + background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; +} + +/* Zebra Widget - row alternating colors */ +.tablesorter-default tr.odd > td { + background-color: #dfdfdf; +} +.tablesorter-default tr.even > td { + background-color: #efefef; +} + +/* Column Widget - column sort colors */ +.tablesorter-default tr.odd td.primary { + background-color: #bfbfbf; +} +.tablesorter-default td.primary, +.tablesorter-default tr.even td.primary { + background-color: #d9d9d9; +} +.tablesorter-default tr.odd td.secondary { + background-color: #d9d9d9; +} +.tablesorter-default td.secondary, +.tablesorter-default tr.even td.secondary { + background-color: #e6e6e6; +} +.tablesorter-default tr.odd td.tertiary { + background-color: #e6e6e6; +} +.tablesorter-default td.tertiary, +.tablesorter-default tr.even td.tertiary { + background-color: #f2f2f2; +} + +/* caption */ +.tablesorter-default > caption { + background-color: #fff; +} + +/* filter widget */ +.tablesorter-default .tablesorter-filter-row { + background-color: #eee; +} +.tablesorter-default .tablesorter-filter-row td { + background-color: #eee; + border-bottom: #ccc 1px solid; + line-height: normal; + text-align: center; /* center the input */ + -webkit-transition: line-height 0.1s ease; + -moz-transition: line-height 0.1s ease; + -o-transition: line-height 0.1s ease; + transition: line-height 0.1s ease; +} +/* optional disabled input styling */ +.tablesorter-default .tablesorter-filter-row .disabled { + opacity: 0.5; + filter: alpha(opacity=50); + cursor: not-allowed; +} +/* hidden filter row */ +.tablesorter-default .tablesorter-filter-row.hideme td { + /*** *********************************************** ***/ + /*** change this padding to modify the thickness ***/ + /*** of the closed filter row (height = padding x 2) ***/ + padding: 2px; + /*** *********************************************** ***/ + margin: 0; + line-height: 0; + cursor: pointer; +} +.tablesorter-default .tablesorter-filter-row.hideme * { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} +/* filters */ +.tablesorter-default input.tablesorter-filter, +.tablesorter-default select.tablesorter-filter { + width: 95%; + height: auto; + margin: 4px auto; + padding: 4px; + background-color: #fff; + border: 1px solid #bbb; + color: #333; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: height 0.1s ease; + -moz-transition: height 0.1s ease; + -o-transition: height 0.1s ease; + transition: height 0.1s ease; +} +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + text-align: center; + cursor: pointer; + background-color: #e6bf99; +}
    User<%}%> + <%if(!showSingleUser){%>UserDate Duration Passes
    run details<%=h(run.getUsername())%><%=h(run.getUserName())%><%=h(df.format(run.getPostTime()))%> <%=h(run.getDuration())%> <%=h(run.getPassedtests())%>