Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,6 @@ project.tasks.named("uiTests").configure {
project.parent.parent.tasks.named("ijConfigure").configure {
dependsOn(initPropertiesTask)
}
project.tasks.withType(RunTestSuite).configureEach { it ->
scanForTestClasses = true // Required for Gradle 9.3
}

if (project.hasProperty('doPublishing'))
{
Expand Down
31 changes: 31 additions & 0 deletions src/org/labkey/remoteapi/admin/ClearCachesCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.labkey.remoteapi.admin;

import org.labkey.remoteapi.CommandResponse;
import org.labkey.remoteapi.PostCommand;

import java.util.HashMap;
import java.util.Map;

public class ClearCachesCommand extends PostCommand<CommandResponse>
{
private final boolean _clearCaches;
private final boolean _gc;

public ClearCachesCommand(boolean clearCaches, boolean gc)
{
super("admin", "clearCaches");
_clearCaches = clearCaches;
_gc = gc;
}

@Override
protected Map<String, Object> createParameterMap()
{
Map<String, Object> params = new HashMap<>();
if (_clearCaches)
params.put("clearCaches", true);
if (_gc)
params.put("gc", true);
return params;
}
}
28 changes: 20 additions & 8 deletions src/org/labkey/test/BaseWebDriverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.labkey.remoteapi.CommandException;
import org.labkey.remoteapi.CommandResponse;
import org.labkey.remoteapi.Connection;
import org.labkey.remoteapi.admin.ClearCachesCommand;
import org.labkey.remoteapi.SimpleGetCommand;
import org.labkey.remoteapi.SimplePostCommand;
import org.labkey.remoteapi.collections.CaseInsensitiveHashMap;
Expand Down Expand Up @@ -99,7 +100,6 @@
import org.labkey.test.util.UIPermissionsHelper;
import org.labkey.test.util.core.webdav.WebDavUploadHelper;
import org.labkey.test.util.ext4cmp.Ext4FieldRef;
import org.labkey.test.util.query.QueryUtils;
import org.labkey.test.util.selenium.WebDriverUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.ElementClickInterceptedException;
Expand Down Expand Up @@ -1174,6 +1174,7 @@ public void dumpHeap()

// Use dumpHeapAction rather that touching file so that we can get file name and publish artifact.
beginAt(WebTestHelper.buildURL("admin", "dumpHeap"));
clickButton("OK");
String dumpMsg = Locators.bodyPanel().childTag("div").findElement(getDriver()).getText();
String filePrefix = "Heap dumped to ";
int prefixIndex = dumpMsg.indexOf(filePrefix);
Expand Down Expand Up @@ -1389,6 +1390,22 @@ public static File getDownloadDir()
return SingletonWebDriver.getInstance().getDownloadDir();
}

/**
* Clears all server caches and runs garbage collection via the admin ClearCachesAction.
* Leaves the browser on the MemTracker page.
*/
protected void clearCaches()
{
try
{
new ClearCachesCommand(true, true).execute(createDefaultConnection(), "/");
}
catch (IOException | CommandException e)
{
throw new RuntimeException("Failed to clear caches", e);
}
}

protected void checkLeaks()
{
if (isLeakCheckSkipped())
Expand All @@ -1415,7 +1432,8 @@ protected void checkLeaks()
}
}
msSinceTestStart = System.currentTimeMillis() - previousLeakCheck;
beginAt(WebTestHelper.buildURL("admin", "memTracker", Map.of("gc", 1, "clearCaches", 1)), 120000);
clearCaches();
beginAt(WebTestHelper.buildURL("admin", "memTracker"));
if (!isTextPresent("In-Use Objects"))
throw new IllegalStateException("Asserts must be enabled to track memory leaks; add -ea to your server VM params and restart or add -DmemCheck=false to your test VM params.");
leakCount = getImageWithAltTextCount("expand/collapse");
Expand Down Expand Up @@ -2379,12 +2397,6 @@ public void validateQueries(boolean validateSubfolders)
validateQueries(validateSubfolders, 120000);
}

@Deprecated
public void deleteAllRows(String projectName, String schema, String table) throws IOException, CommandException
{
QueryUtils.truncateTable(projectName, schema, table);
}

// This class makes it easier to start a specimen import early in a test and wait for completion later.
public class SpecimenImporter
{
Expand Down
119 changes: 86 additions & 33 deletions src/org/labkey/test/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,24 @@ private void updateRemainingTests(Test test, boolean failed, boolean errored)
Class<?> testClass = getTestClass(test);
_remainingTests.remove(testClass);
if (failed)
_failedTests.add(test.toString());
_failedTests.add(getTestName(test));
else if (errored)
_erroredTests.add(test.toString());
_erroredTests.add(getTestName(test));
else
_passedTests.add(test.toString());
_passedTests.add(getTestName(test));
}

private static void writeRemainingTests()
{
ArrayList<String> failedAndRemaining = new ArrayList<>();
failedAndRemaining.addAll(_failedTests);
failedAndRemaining.addAll(_erroredTests);
for (Class<?> clazz : _remainingTests)
failedAndRemaining.add(clazz.getName());
writeClasses(failedAndRemaining, getRemainingTestsFile());
if (!TestProperties.isTestRunningOnTeamCity()) // Not useful on TeamCity
{
ArrayList<String> failedAndRemaining = new ArrayList<>();
failedAndRemaining.addAll(_failedTests);
failedAndRemaining.addAll(_erroredTests);
for (Class<?> clazz : _remainingTests)
failedAndRemaining.add(clazz.getName());
writeClasses(failedAndRemaining, getRemainingTestsFile());
}
}

private static void writeClasses(List<String> tests, File file)
Expand Down Expand Up @@ -203,7 +206,7 @@ private static Class<?>[] readClasses(File recentlyFailedTestsFile, List<Class<?
filteredRecentlyFailedTests.add(item);
}
}

return filteredRecentlyFailedTests.toArray(new Class[0]);
}

Expand Down Expand Up @@ -273,8 +276,7 @@ else if (test instanceof JUnit4TestAdapter adapter)

if (_failedTests.size() + _erroredTests.size() < _maxTestFailures || _maxTestFailures <= 0)
{
final Class<?> currentTestClass = getTestClass(test);
final String currentTestName = currentTestClass.getSimpleName();
final String currentTestName = getTestName(test);

TestListener classFailListener = new TestListener()
{
Expand All @@ -298,13 +300,16 @@ public void endTest(Test _test) { }
};
testResult.addListener(classFailListener);

logToServer("=== Starting " + currentTestName + getProgress() + " ===");
LOG.info("=============== Starting " + currentTestName + getProgress() + " =================");
if (!(test instanceof JUnitTest.RemoteTest))
{
logToServer("=== Starting " + currentTestName + getProgress() + " ===");
LOG.info("=============== Starting " + currentTestName + getProgress() + " =================");
}

// This stub matches the failure generated by JUnit when it fails during static setup/teardown (e.g. @BeforeClass)
// Without this TeamCity has no way of knowing when a setup/teardown failure has been resolved
final Test loggingStub = test instanceof JUnit4TestAdapter ?
new TestSuite(currentTestClass) :
final Test loggingStub = test instanceof JUnit4TestAdapter jUnit4TestAdapter ?
new TestSuite(jUnit4TestAdapter.getTestClass()) :
null;

if (loggingStub != null)
Expand All @@ -317,10 +322,13 @@ public void endTest(Test _test) { }

testResult.removeListener(classFailListener);

String result = failed.booleanValue() || errored.booleanValue() ? "Failed " : "Completed ";
TestLogger.resetLogger();
LOG.info("=============== " + result + currentTestName + getProgress() + " =================");
logToServer("=== " + result + currentTestName + getProgress() + " ===");
if (!(test instanceof JUnitTest.RemoteTest))
{
String result = failed.booleanValue() || errored.booleanValue() ? "Failed " : "Completed ";
TestLogger.resetLogger();
LOG.info("=============== " + result + currentTestName + getProgress() + " =================");
logToServer("=== " + result + currentTestName + getProgress() + " ===");
}

}
else
Expand Down Expand Up @@ -547,7 +555,7 @@ public String describe()
}
else if (test.countTestCases() > 0)
{
suite.addTest(test);
flattenSuiteInto(suite, (TestSuite) test);
foundServerSideTest = true;
}
}
Expand All @@ -570,7 +578,7 @@ else if (specifiedSuite.startsWith("?") && specifiedSuite.length() > 1)
}
TestSuite dynamicSuite = JUnitTest.dynamicSuite(requestedSuites, excludedSuites);
if (dynamicSuite.countTestCases() > 0)
suite.addTest(dynamicSuite);
flattenSuiteInto(suite, dynamicSuite);
}
}

Expand Down Expand Up @@ -603,9 +611,9 @@ public void run(TestResult testResult)

private static void writeTimeReport()
{
int width = 60;
int width = 64;
long total = 0;
LOG.info("======================= Time Report ========================");
LOG.info(getCenteredText("Time Report", '=', width));

Duration totalCrawlTime = Duration.ZERO;
int totalUniquePages = 0;
Expand All @@ -619,7 +627,7 @@ private static void writeTimeReport()
}
for (Map.Entry<Test, Long> entry : _testStats.entrySet())
{
String testName = entry.getKey().toString();
String testName = getTestName(entry.getKey());
long duration = entry.getValue();

long percent = Math.round(100.0 * (duration / (double) total));
Expand All @@ -630,7 +638,6 @@ private static void writeTimeReport()
(_erroredTests.contains(testName) ? "ERROR" : "not run"))) +
" - " +
formatDuration(duration) + " " + percentStr;
testName = testName.substring(testName.lastIndexOf('.') + 1);

LOG.info(getFixedWidthString(testName, durationAndPercent, width));

Expand Down Expand Up @@ -686,7 +693,7 @@ private static void writeTimeReport()
}
if (!TeamCityUtils.getBuildStatistics().isEmpty())
{
LOG.info("--------------------- Build Statistics ---------------------");
LOG.info(getCenteredText("Build Statistics", '-', width));
for (String stat : TeamCityUtils.getBuildStatistics().keySet())
{
List<Number> values = TeamCityUtils.getBuildStatistics().get(stat);
Expand All @@ -702,7 +709,7 @@ private static void writeTimeReport()
Map<String, Collection<String>> actionWarnings = WebDriverWrapper.getActionWarnings();
if (!actionWarnings.isEmpty())
{
LOG.info("---------------------- Test Warnings -----------------------");
LOG.info(getCenteredText("Test Warnings", '-', width));
for (String warning : actionWarnings.keySet())
{
LOG.info(" " + warning + ":");
Expand All @@ -714,7 +721,7 @@ private static void writeTimeReport()
}
}
}
LOG.info("------------------------------------------------------------");
LOG.info("-".repeat(width));
LOG.info(getFixedWidthString("Total duration:", formatDuration(total), width) + "\n");
LOG.info("Completed " + FastDateFormat.getInstance("yyyy-MM-dd HH:mm").format(new Date()));
}
Expand Down Expand Up @@ -744,10 +751,23 @@ private static String getRowString(String[] list, int columnWidth)
private static String getFixedWidthString(String prefix, String suffix, int length)
{
int contentLength = prefix.length() + suffix.length();
int padding = Math.max(0, length - contentLength);
int padding = Math.max(1, length - contentLength);
return prefix + " ".repeat(padding) + suffix;
}

private static String getCenteredText(String text, char paddingChar, int width)
{
text = StringUtils.trimToEmpty(text);
if (text.length() > width)
return text;

text = text.isEmpty() ? "" : " " + text + " ";
int paddingLength = width - text.length();
int leftPadding = paddingLength / 2;
int rightPadding = paddingLength - leftPadding;
return Character.toString(paddingChar).repeat(leftPadding) + text + Character.toString(paddingChar).repeat(rightPadding);
}

private static TestSet getCompositeTestSet(List<String> suitesColl)
{
if (suitesColl.isEmpty())
Expand Down Expand Up @@ -1021,7 +1041,7 @@ else if (testNames.isEmpty())
Test test = e.nextElement();
Class<?> testClass = getTestClass(test);
_remainingTests.add(testClass);
LOG.info(" " + testClass.getSimpleName());
LOG.info(" " + getTestName(test));
for (String testMethod : specifiedTestMethods.getOrDefault(testClass, Collections.emptyList()))
{
LOG.info(" ." + testMethod);
Expand Down Expand Up @@ -1126,10 +1146,43 @@ private static String getModuleNameFromPath(File path)
return null;
}

private static void flattenSuiteInto(TestSuite destination, TestSuite source)
{
if (TestProperties.isTestRunningOnTeamCity())
{
// Flatten test suites so that TeamCity will correlate with results from before Gradle 9.3
Enumeration<Test> tests = source.tests();
while (tests.hasMoreElements())
{
Test t = tests.nextElement();
if (t instanceof TestSuite nested)
flattenSuiteInto(destination, nested);
else
destination.addTest(t);
}
}
else
{
destination.addTest(source);
}
}

private static String getTestName(Test test)
{
if (test instanceof JUnit4TestAdapter testAdapter)
return testAdapter.getTestClass().getSimpleName();
else if (test instanceof JUnitTest.RemoteTest remoteTest)
return remoteTest.getName();
else
return test.getClass().getSimpleName();
}

private static Class<?> getTestClass(Test test)
{
if (test instanceof JUnit4TestAdapter)
return ((JUnit4TestAdapter) test).getTestClass();
if (test instanceof JUnit4TestAdapter testAdapter)
return testAdapter.getTestClass();
else if (test instanceof JUnitTest.RemoteTest)
return JUnitTest.class;
else
return test.getClass();
}
Expand Down
6 changes: 6 additions & 0 deletions src/org/labkey/test/pages/LabkeyErrorPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ public String getErrorImage()
{
return elementCache().errorImage.getAttribute("src");
}

public boolean isShowDetailsPresent()
{
return !Locator.button("View Details").findElements(getDriver()).isEmpty();
}

@Override
protected ElementCache newElementCache()
{
Expand Down
Loading
Loading