From a5ce1b53bf9304c08bb51639b48bb77085cd62b3 Mon Sep 17 00:00:00 2001
From: davidely <davidely@localhost>
Date: Sun, 02 Sep 2007 04:00:42 +0000
Subject: [PATCH] There are several improvements to the unit test framework in this commit.
---
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 352 insertions(+), 25 deletions(-)
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
index 53d7357..fa6fa10 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -37,6 +37,9 @@
import java.util.Enumeration;
import java.util.Map;
import java.util.LinkedHashMap;
+import java.util.TreeMap;
+import java.util.LinkedHashSet;
+import java.util.Collections;
import java.util.logging.Logger;
import java.util.logging.Handler;
import java.util.logging.LogManager;
@@ -50,10 +53,11 @@
import org.opends.server.backends.jeb.BackendImpl;
import org.opends.server.backends.jeb.EntryContainer;
import org.opends.server.backends.jeb.RootContainer;
+import org.opends.server.backends.jeb.DatabaseContainer;
+import org.opends.server.backends.jeb.Index;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DirectoryServer;
-import org.opends.server.core.LockFileManager;
import org.opends.server.extensions.ConfigFileHandler;
import org.opends.server.loggers.TextErrorLogPublisher;
import org.opends.server.loggers.TextAccessLogPublisher;
@@ -75,14 +79,17 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.OperatingSystem;
import org.opends.server.types.ResultCode;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
import org.opends.server.util.EmbeddedUtils;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
-import org.opends.server.tasks.TaskUtils;
import org.opends.server.api.WorkQueue;
+import org.opends.server.api.Backend;
import org.opends.messages.Message;
/**
@@ -105,12 +112,40 @@
"org.opends.server.LdapPort";
/**
+ * If this System property is set to true, then the classes/ directory
+ * will be copied into the server package setup for the tests. This allows
+ * the server tools (e.g. ldapsearch) to be used on a live server, but it
+ * takes a while to copy all of the files, so we don't do it by default.
+ */
+ public static final String PROPERTY_COPY_CLASSES_TO_TEST_PKG =
+ "org.opends.test.copyClassesToTestPackage";
+
+ /**
* The string representation of the DN that will be used as the base entry for
* the test backend. This must not be changed, as there are a number of test
* cases that depend on this specific value of "o=test".
*/
public static final String TEST_ROOT_DN_STRING = "o=test";
+
+ /**
+ * The test text writer for the Debug Logger
+ */
+ public static TestTextWriter DEBUG_TEXT_WRITER =
+ new TestTextWriter();
+
+ /**
+ * The test text writer for the Debug Logger
+ */
+ public static TestTextWriter ERROR_TEXT_WRITER =
+ new TestTextWriter();
+
+ /**
+ * The test text writer for the Debug Logger
+ */
+ public static TestTextWriter ACCESS_TEXT_WRITER =
+ new TestTextWriter();
+
/**
* Indicates whether the server has already been started. The value of this
* constant must not be altered by anything outside the
@@ -139,6 +174,11 @@
private static int serverLdapsPort;
/**
+ * Incremented by one each time the server has restarted.
+ */
+ private static int serverRestarts = 0;
+
+ /**
* Starts the Directory Server so that it will be available for use while
* running the unit tests. This will only actually start the server once, so
* subsequent attempts to start it will be ignored because it will already be
@@ -203,8 +243,11 @@
File testLibDir = new File(testRoot, "lib");
File testBinDir = new File(testRoot, "bin");
- copyDirectory(serverClassesDir, testClassesDir);
- copyDirectory(unitClassesDir, testClassesDir);
+ if (Boolean.getBoolean(PROPERTY_COPY_CLASSES_TO_TEST_PKG)) {
+ copyDirectory(serverClassesDir, testClassesDir);
+ copyDirectory(unitClassesDir, testClassesDir);
+ }
+
copyDirectory(libDir, testLibDir);
copyDirectory(new File(resourceDir, "bin"), testBinDir);
copyDirectory(new File(resourceDir, "config"), testConfigDir);
@@ -225,7 +268,6 @@
copyFile(new File(testResourceDir, "client-cert.p12"),
new File(testConfigDir, "client-cert.p12"));
-
for (File f : testBinDir.listFiles())
{
try
@@ -316,26 +358,135 @@
config.addAccessLogger(
TextAccessLogPublisher.getStartupTextAccessPublisher(
- TestListener.ACCESS_TEXT_WRITER, false));
+ ACCESS_TEXT_WRITER, false));
config.addErrorLogger(
TextErrorLogPublisher.getStartupTextErrorPublisher(
- TestListener.ERROR_TEXT_WRITER));
+ ERROR_TEXT_WRITER));
config.addDebugLogger(
TextDebugLogPublisher.getStartupTextDebugPublisher(
- TestListener.DEBUG_TEXT_WRITER));
+ DEBUG_TEXT_WRITER));
EmbeddedUtils.startServer(config);
assertTrue(InvocationCounterPlugin.startupCalled());
+ // Save config.ldif for when we restart the server
+ backupServerConfigLdif();
+
SERVER_STARTED = true;
initializeTestBackend(true);
}
/**
+ * Similar to startServer, but it will restart the server each time it is
+ * called. Since this is somewhat expensive, it should be called under
+ * two circumstances. Either in an @AfterClass method for a test that
+ * makes lots of configuration changes to the server, or in a @BeforeClass
+ * method for a test that is very sensitive to running in a clean server.
+ *
+ * @throws IOException If a problem occurs while interacting with the
+ * filesystem to prepare the test package root.
+ *
+ * @throws InitializationException If a problem occurs while starting the
+ * server.
+ *
+ * @throws ConfigException If there is a problem with the server
+ * configuration.
+ */
+ public static synchronized void restartServer()
+ throws IOException, InitializationException, ConfigException,
+ DirectoryException, Exception
+ {
+ if (!SERVER_STARTED) {
+ startServer();
+ return;
+ }
+
+ long startMs = System.currentTimeMillis();
+
+ clearLoggersContents();
+
+ clearJEBackends();
+ restoreServerConfigLdif();
+ memoryBackend = null; // We need it to be recreated and reregistered
+
+ EmbeddedUtils.restartServer(null, null, DirectoryServer.getEnvironmentConfig());
+ initializeTestBackend(true);
+
+ // This generates too much noise, so it's disabled by default.
+ // outputLogContentsIfError("Potential problem during in-core restart. You be the judge.");
+
+ // Keep track of these so we can report how long they took in the test summary
+ long durationMs = System.currentTimeMillis() - startMs;
+ restartTimesMs.add(durationMs);
+
+ serverRestarts++;
+ }
+
+ public static List<Long> restartTimesMs = new ArrayList<Long>();
+ public static List<Long> getRestartTimesMs() {
+ return Collections.unmodifiableList(restartTimesMs);
+ }
+
+ private static void outputLogContentsIfError(String prefix) {
+ StringBuilder logContents = new StringBuilder(prefix + EOL);
+ appendLogsContents(logContents);
+
+ if (logContents.indexOf("ERROR") != -1) {
+ originalSystemErr.println(logContents);
+ }
+ }
+
+ private static void clearJEBackends() throws Exception
+ {
+ for (Backend backend: DirectoryServer.getBackends().values()) {
+ if (backend instanceof BackendImpl) {
+ TestCaseUtils.clearJEBackend(false, backend.getBackendID(), null);
+ }
+ }
+ }
+
+ public static void clearDataBackends() throws Exception
+ {
+ clearJEBackends();
+ memoryBackend.clearMemoryBackend();
+ }
+
+ private static File getTestConfigDir()
+ {
+ String buildRoot = System.getProperty(PROPERTY_BUILD_ROOT);
+ File buildDir = new File(buildRoot, "build");
+ File unitRoot = new File(buildDir, "unit-tests");
+ File testRoot = new File(unitRoot, "package");
+ return new File(testRoot, "config");
+ }
+
+ private static void backupServerConfigLdif() throws IOException
+ {
+ File testConfigDir = getTestConfigDir();
+ copyFile(new File(testConfigDir, "config.ldif"),
+ new File(testConfigDir, "config.ldif.for-restart"));
+ }
+
+ private static void restoreServerConfigLdif() throws IOException {
+ File testConfigDir = getTestConfigDir();
+ File from = new File(testConfigDir, "config.ldif.for-restart");
+ File to = new File(testConfigDir, "config.ldif");
+
+ // Sometimes this fails because config.ldif is in use, so we wait
+ // and try it again.
+ try {
+ copyFile(from, to);
+ } catch (IOException e) {
+ sleep(1000);
+ copyFile(from, to);
+ }
+ }
+
+ /**
* Bring the server to a quiescent state. This includes waiting for all
* operations to complete. This can be used in a @BeforeMethod setup method
* to make sure that the server has finished processing all operations
@@ -352,11 +503,16 @@
*/
private static void waitForOpsToComplete()
{
- WorkQueue workQueue = DirectoryServer.getWorkQueue();
- final long NO_TIMEOUT = -1;
- workQueue.waitUntilIdle(NO_TIMEOUT);
+ try {
+ WorkQueue workQueue = DirectoryServer.getWorkQueue();
+ final long NO_TIMEOUT = -1;
+ workQueue.waitUntilIdle(NO_TIMEOUT);
+ } catch (Exception e) {
+ // Ignore it, maybe the server hasn't been started.
+ }
}
+
/**
* Binds to the given socket port on the local host.
* @return the bounded Server socket.
@@ -472,19 +628,51 @@
{
BackendImpl backend = (BackendImpl)DirectoryServer.getBackend(beID);
RootContainer rootContainer = backend.getRootContainer();
- for (EntryContainer ec : rootContainer.getEntryContainers())
- {
- ec.clear();
- assertEquals(ec.getHighestEntryID().longValue(), 0L);
- }
- rootContainer.resetNextEntryID();
+ if (rootContainer != null) {
+ for (EntryContainer ec : rootContainer.getEntryContainers())
+ {
+ ec.clear();
+ assertEquals(ec.getHighestEntryID().longValue(), 0L);
+ }
+ rootContainer.resetNextEntryID();
- if (createBaseEntry)
- {
- DN baseDN = DN.decode(dn);
- Entry e = createEntry(baseDN);
- backend = (BackendImpl)DirectoryServer.getBackend(beID);
- backend.addEntry(e, null);
+ if (createBaseEntry)
+ {
+ DN baseDN = DN.decode(dn);
+ Entry e = createEntry(baseDN);
+ backend = (BackendImpl)DirectoryServer.getBackend(beID);
+ backend.addEntry(e, null);
+ }
+ }
+ }
+
+ /**
+ * This was used to track down which test was trashing the indexes.
+ * We left it here because it might be useful again.
+ */
+ public static void printUntrustedIndexes()
+ {
+ try {
+ BackendImpl backend = (BackendImpl)DirectoryServer.getBackend("userRoot");
+ if (backend == null) {
+ return;
+ }
+ RootContainer rootContainer = backend.getRootContainer();
+ for (EntryContainer ec : rootContainer.getEntryContainers())
+ {
+ List<DatabaseContainer> databases = new ArrayList<DatabaseContainer>();
+ ec.listDatabases(databases);
+ for (DatabaseContainer dbContainer: databases) {
+ if (dbContainer instanceof Index) {
+ Index index = (Index)dbContainer;
+ if (!index.isTrusted()) {
+ originalSystemErr.println("ERROR: The index " + index.toString() + " is no longer trusted.");
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace(originalSystemErr);
}
}
@@ -606,8 +794,8 @@
}
/**
- * Get teh LDAPS port the test environment Directory Server instance is
- * running on
+ * Get the LDAPS port the test environment Directory Server instance is
+ * running on.
*
* @return The port number.
*/
@@ -617,6 +805,17 @@
}
/**
+ * Get the number of times the server has done an incore restart during
+ * the unit tests.
+ *
+ * @return the number of server restarts.
+ */
+ public static int getNumServerRestarts()
+ {
+ return serverRestarts;
+ }
+
+ /**
* Method for getting a file from the test resources directory.
*
* @return The directory as a File
@@ -1060,6 +1259,73 @@
redirectedSystemErr.reset();
}
+ /**
+ * clear everything written to the Access, Error, or Debug loggers
+ */
+ public synchronized static void clearLoggersContents() {
+ ACCESS_TEXT_WRITER.clear();
+ ERROR_TEXT_WRITER.clear();
+ DEBUG_TEXT_WRITER.clear();
+ clearSystemOutContents();
+ clearSystemErrContents();
+ }
+
+ /**
+ * Append the contents of the Access Log, Error Log, Debug Loggers,
+ * System.out, System.err to the specified buffer.
+ */
+ public static void appendLogsContents(StringBuilder logsContents)
+ {
+ List<String> messages = TestCaseUtils.ACCESS_TEXT_WRITER.getMessages();
+ if (! messages.isEmpty())
+ {
+ logsContents.append(EOL);
+ logsContents.append("Access Log Messages:");
+ logsContents.append(EOL);
+ for (String message : messages)
+ {
+ logsContents.append(message);
+ logsContents.append(EOL);
+ }
+ }
+
+ messages = TestCaseUtils.ERROR_TEXT_WRITER.getMessages();
+ if (! messages.isEmpty())
+ {
+ logsContents.append(EOL);
+ logsContents.append("Error Log Messages:");
+ logsContents.append(EOL);
+ for (String message : messages)
+ {
+ logsContents.append(message);
+ logsContents.append(EOL);
+ }
+ }
+
+ messages = TestCaseUtils.DEBUG_TEXT_WRITER.getMessages();
+ if(! messages.isEmpty())
+ {
+ logsContents.append(EOL);
+ logsContents.append("Debug Log Messages:");
+ logsContents.append(EOL);
+ for (String message : messages)
+ {
+ logsContents.append(message);
+ logsContents.append(EOL);
+ }
+ }
+
+ String systemOut = TestCaseUtils.getSystemOutContents();
+ if (systemOut.length() > 0) {
+ logsContents.append(EOL + "System.out contents:" + EOL + systemOut);
+ }
+
+ String systemErr = TestCaseUtils.getSystemErrContents();
+ if (systemErr.length() > 0) {
+ logsContents.append(EOL + "System.err contents:" + EOL + systemErr);
+ }
+ }
+
public synchronized static void unsupressOutput() {
System.setOut(originalSystemOut);
System.setErr(originalSystemErr);
@@ -1226,5 +1492,66 @@
assertEquals(DSConfig.main(fullArgs, false, System.out, System.err), 0);
}
+
+
+ /**
+ * Return a String representation of all of the current threads.
+ * @return a dump of all Threads on the server
+ */
+ public static String threadStacksToString()
+ {
+ Map<Thread,StackTraceElement[]> threadStacks = Thread.getAllStackTraces();
+
+
+ // Re-arrange all of the elements by thread ID so that there is some logical
+ // order.
+ TreeMap<Long,Map.Entry<Thread,StackTraceElement[]>> orderedStacks =
+ new TreeMap<Long,Map.Entry<Thread,StackTraceElement[]>>();
+ for (Map.Entry<Thread,StackTraceElement[]> e : threadStacks.entrySet())
+ {
+ orderedStacks.put(e.getKey().getId(), e);
+ }
+
+ final StringBuilder buffer = new StringBuilder();
+ for (Map.Entry<Thread,StackTraceElement[]> e : orderedStacks.values())
+ {
+ Thread t = e.getKey();
+ StackTraceElement[] stackElements = e.getValue();
+
+ long id = t.getId();
+
+ buffer.append("id=");
+ buffer.append(id);
+ buffer.append(" ---------- ");
+ buffer.append(t.getName());
+ buffer.append(" ----------");
+ buffer.append(EOL);
+
+ if (stackElements != null)
+ {
+ for (int j=0; j < stackElements.length; j++)
+ {
+ buffer.append(" ").append(stackElements[j].getClassName());
+ buffer.append(".");
+ buffer.append(stackElements[j].getMethodName());
+ buffer.append("(");
+ buffer.append(stackElements[j].getFileName());
+ buffer.append(":");
+ if (stackElements[j].isNativeMethod())
+ {
+ buffer.append("native");
+ }
+ else
+ {
+ buffer.append(stackElements[j].getLineNumber());
+ }
+ buffer.append(")").append(EOL);
+ }
+ }
+ buffer.append(EOL);
+ }
+
+ return buffer.toString();
+ }
}
--
Gitblit v1.10.0