From 10431c521eaa96815b6863379338d18289be920a Mon Sep 17 00:00:00 2001 From: Braids Date: Fri, 12 Aug 2011 14:47:37 +0000 Subject: [PATCH] - Added programmatic access to BuildID and Version through methods on FModel.getBuildInfo(). - Added class forge.model.BuildInfo with unit test. - Added unit test for forge.model.FModel. - Added exception class forge.model.MultipleForgeJarsFoundError. - Updated forge.view.swing.Main with some preliminary code for progress monitoring. - Reduced CheckStyle, FindBugs, and PMD violations in these files and in net.slightlymagic.braids.util.testng.BraidsAssertFunctions. - Added missing package-info in net.slightlymagic.braids.util.testng. --- .gitattributes | 5 + src/main/java/forge/model/BuildInfo.java | 234 ++++++++++++++++++ src/main/java/forge/model/FModel.java | 78 +++++- .../model/MultipleForgeJarsFoundError.java | 23 ++ src/main/java/forge/view/swing/Main.java | 2 +- src/test/java/forge/model/BuildInfoTest.java | 180 ++++++++++++++ src/test/java/forge/model/FModelTest.java | 58 +++++ .../util/testng/BraidsAssertFunctions.java | 63 ++--- .../braids/util/testng/package-info.java | 3 + 9 files changed, 609 insertions(+), 37 deletions(-) create mode 100644 src/main/java/forge/model/BuildInfo.java create mode 100644 src/main/java/forge/model/MultipleForgeJarsFoundError.java create mode 100644 src/test/java/forge/model/BuildInfoTest.java create mode 100644 src/test/java/forge/model/FModelTest.java create mode 100644 src/test/java/net/slightlymagic/braids/util/testng/package-info.java diff --git a/.gitattributes b/.gitattributes index bfa9fea1aff..116facfb73d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9665,7 +9665,9 @@ src/main/java/forge/gui/input/Input_PayManaCostUtil.java svneol=native#text/plai src/main/java/forge/gui/input/Input_PayManaCost_Ability.java svneol=native#text/plain src/main/java/forge/gui/input/package-info.java svneol=native#text/plain src/main/java/forge/gui/package-info.java svneol=native#text/plain +src/main/java/forge/model/BuildInfo.java -text src/main/java/forge/model/FModel.java svneol=native#text/plain +src/main/java/forge/model/MultipleForgeJarsFoundError.java -text src/main/java/forge/model/package-info.java svneol=native#text/plain src/main/java/forge/package-info.java svneol=native#text/plain src/main/java/forge/properties/ForgePreferences.java svneol=native#text/plain @@ -9780,5 +9782,8 @@ src/test/java/forge/deck/generate/GenerateConstructedDeckTest.java svneol=native src/test/java/forge/deck/generate/GenerateConstructedMultiColorDeckTest.java svneol=native#text/plain src/test/java/forge/gui/ListChooserTest.java svneol=native#text/plain src/test/java/forge/gui/game/CardDetailPanelTest.java svneol=native#text/plain +src/test/java/forge/model/BuildInfoTest.java -text +src/test/java/forge/model/FModelTest.java -text src/test/java/net/slightlymagic/braids/util/testng/BraidsAssertFunctions.java svneol=native#text/plain src/test/java/net/slightlymagic/braids/util/testng/LICENSE.txt svneol=native#text/plain +src/test/java/net/slightlymagic/braids/util/testng/package-info.java -text diff --git a/src/main/java/forge/model/BuildInfo.java b/src/main/java/forge/model/BuildInfo.java new file mode 100644 index 00000000000..9607afeecff --- /dev/null +++ b/src/main/java/forge/model/BuildInfo.java @@ -0,0 +1,234 @@ +package forge.model; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Provides access to information about the current version and build ID. + */ +public class BuildInfo { + /** Exception-free means of getting the ASCII Charset. */ + public static final Charset US_ASCII_CHARSET = Charset.forName("US-ASCII"); + + /** Convenience for file.separator. */ + private static final String FILE_SEP = System.getProperty("file.separator"); + + /** Convenience for path.separator. */ + private static final String PATH_SEP = System.getProperty("path.separator"); + + private static final Pattern FORGE_JAR_REGEX_2G = // NOPMD by Braids on 8/12/11 10:18 AM + Pattern.compile("^(.*" + Pattern.quote(FILE_SEP) + ")?" + + Pattern.quote("forge-") + + "([^" + Pattern.quote(FILE_SEP) + Pattern.quote(PATH_SEP) + "]*)" + + Pattern.quote("-with-dependencies.jar") + "$", + Pattern.CASE_INSENSITIVE); + + + private transient String pathToForgeJar; + + /** + * Construct a standard BuildInfo object. + * + * Package access is intentional for unit testing. + * + * @see FModel.getBuildInfo() + */ + BuildInfo() { + // empty + } + + /** + * Unit-testable constructor which allows a specific jar file to be set. + * + * Dependency injection! Relax, this won't hurt a bit. + * + * @param pathToMockJarFile where to find the mock Forge jar + */ + public BuildInfo(final String pathToMockJarFile) { + pathToForgeJar = pathToMockJarFile; + } + + /** + * Get the current build ID for Forge. + * + * @return a String representing the build identifier, or null if we could + * not determine the value. + */ + public final String getBuildID() { + String manifestResult; + String result; + try { + manifestResult = getManifestAttribute("Implementation-Build"); + } catch (IOException exn1) { + manifestResult = null; // NOPMD by Braids on 8/12/11 10:21 AM + } + + if (manifestResult == null) { + // Try getting the SVN version number by running the svnversion + // command. This is a long shot, but it works on some developers' + // systems. + + Process proc = null; + BufferedReader reader = null; + try { + proc = Runtime.getRuntime().exec("svnversion"); + final InputStream procStdoutStream = proc.getInputStream(); + final Reader procReader = new InputStreamReader(procStdoutStream, US_ASCII_CHARSET); + reader = new BufferedReader(procReader); + + result = reader.readLine(); // may be null + + } catch (IOException exn2) { + result = null; // NOPMD by Braids on 8/12/11 10:21 AM + } finally { + try { + reader.close(); + } catch (Throwable exn3) { // NOPMD by Braids on 8/12/11 10:21 AM + // ignored + } + + try { + proc.destroy(); + } catch (Throwable exn4) { // NOPMD by Braids on 8/12/11 10:21 AM + // ignored + } + } + } + else { + result = manifestResult; + } + + return result; + } + + /** + * Get the current version of Forge. + * + * @return a String representing the version specifier, or "SVN" if + * unknown. + */ + public final String getVersion() { + String manifestResult; + String result; + + try { + manifestResult = getManifestAttribute("Implementation-Version"); + } catch (IOException exn) { + manifestResult = null; // NOPMD by Braids on 8/12/11 10:21 AM + } + + if (manifestResult == null) { + result = "SVN"; + } + else { + result = manifestResult; + } + + return result; + } + + /** + * Fetch an attribute from the Forge main jar's manifest. + * + * @param manifestAttrName the name of the attribute you want from the manifest + * @return the attribute's value, which may be empty or null + * @throws IOException if a (unique) Forge jar could not be found + */ + protected final String getManifestAttribute(final String manifestAttrName) throws IOException { + String result = null; + JarFile jar = null; + InputStream manifestStream = null; + + try { + if (pathToForgeJar == null) { + // We're definitely not unit testing. Try to load from the + // currently running jar. + + manifestStream = ClassLoader.getSystemResourceAsStream("META-INF/MANIFEST.MF"); + final Manifest manifest = new Manifest(manifestStream); + + result = getMainManifestAttribute(manifest, manifestAttrName); + } + + if (result == null && pathToForgeJar == null) { + + // Try to find a unique Forge jar in the class path. + + final String classPath = System.getProperty("java.class.path"); + final String[] paths = classPath.split(PATH_SEP); + + for (String path : paths) { + final Matcher matcher = FORGE_JAR_REGEX_2G.matcher(path); + + if (matcher.matches()) { + if (pathToForgeJar == null) { + pathToForgeJar = path; + } + else { + // Error: we found more than one. + pathToForgeJar = null; // NOPMD by Braids on 8/12/11 10:21 AM + + throw new MultipleForgeJarsFoundError( + "Classpath = " + System.getProperty("java.class.path")); + } + } + } + } + + if (result == null && pathToForgeJar == null) { + throw new FileNotFoundException( + "There is nothing matching forge-*-with-dependencies.jar in the class path."); + } + + if (result == null) { + jar = new JarFile(pathToForgeJar); + final Manifest manifest = jar.getManifest(); + + if (manifest == null) { + throw new IOException("Forge jar at <<" + pathToForgeJar + ">> has no manifest."); + } + + result = getMainManifestAttribute(manifest, manifestAttrName); + } + } + finally { + try { + manifestStream.close(); + } catch (Throwable ignored) { // NOPMD by Braids on 8/12/11 10:21 AM + // ignored + } + + try { + jar.close(); + } catch (Throwable ignored) { // NOPMD by Braids on 8/12/11 10:21 AM + // ignored + } + } + + return result; + } + + /** + * Convience method for fetching an attribute from the main section of a + * jar's manifest. + * + * @param manifest the manifest that provides attributes + * @param manifestAttrName the name of the attribute to fetch + * @return the value of the attribute, or null if not set + */ + protected final String getMainManifestAttribute(final Manifest manifest, final String manifestAttrName) { + final Attributes atts = manifest.getMainAttributes(); + return atts.getValue(manifestAttrName); + } + +} diff --git a/src/main/java/forge/model/FModel.java b/src/main/java/forge/model/FModel.java index b7639db839b..ecfbca9b99b 100644 --- a/src/main/java/forge/model/FModel.java +++ b/src/main/java/forge/model/FModel.java @@ -3,39 +3,107 @@ package forge.model; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +//import net.slightlymagic.braids.util.progress_monitor.BaseProgressMonitor; +import net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor; import arcane.util.MultiplexOutputStream; /** * The default Model implementation for Forge. * - * This used to be an interface, but it seems unlikely that we will ever use a + * This used to be an interface, but it seems unlikely that we will ever use a * different model. * * In case we need to convert it into an interface in the future, all fields of * this class must be either private or public static final. */ public class FModel { + //private static final int NUM_INIT_PHASES = 1; + + private final transient OutputStream logFileStream; + private final transient PrintStream oldSystemOut; + private final transient PrintStream oldSystemErr; + private BuildInfo buildInfo; + /** * Constructor. * + * @param theMonitor a progress monitor (from the View) that shows the + * progress of the model's initialization. + * * @throws FileNotFoundException if we could not find or write to the log file. */ - public FModel() throws FileNotFoundException { + public FModel(final BraidsProgressMonitor theMonitor) throws FileNotFoundException { + /* To be implemented later. -Braids + BraidsProgressMonitor monitor; + if (theMonitor == null) { + monitor = new BaseProgressMonitor(NUM_INIT_PHASES, 1); + } + else { + monitor = theMonitor; + } + */ + final File logFile = new File("forge.log"); final boolean deleteSucceeded = logFile.delete(); - if (logFile.exists() && !deleteSucceeded) { + if (logFile.exists() && !deleteSucceeded && logFile.length() != 0) { throw new IllegalStateException("Could not delete existing logFile:" + logFile.getAbsolutePath()); } - // This used to be a BufferedOutputStream, but that seems inappropriate for a log. --Braids - final OutputStream logFileStream = new FileOutputStream(logFile); + logFileStream = new FileOutputStream(logFile); + oldSystemOut = System.out; System.setOut(new PrintStream(new MultiplexOutputStream(System.out, logFileStream), true)); + oldSystemErr = System.err; System.setErr(new PrintStream(new MultiplexOutputStream(System.err, logFileStream), true)); + setBuildInfo(new BuildInfo()); } + + /** + * Destructor for FModel. + * @throws Throwable indirectly + */ + @Override + protected final void finalize() throws Throwable { + close(); + super.finalize(); + } + + /** + * Opposite of constructor; resets all system resources and closes the + * log file. + */ + public final void close() { + System.setOut(oldSystemOut); + System.setErr(oldSystemErr); + try { + logFileStream.close(); + } catch (IOException e) { // NOPMD by Braids on 8/12/11 10:25 AM + // ignored + } + } + + /** + * Getter for buildInfo. + * + * @return the buildInfo + */ + public final BuildInfo getBuildInfo() { + return buildInfo; + } + + /** + * Setter for buildInfo. + * + * @param neoBuildInfo the buildInfo to set + */ + protected final void setBuildInfo(final BuildInfo neoBuildInfo) { + this.buildInfo = neoBuildInfo; + } + } diff --git a/src/main/java/forge/model/MultipleForgeJarsFoundError.java b/src/main/java/forge/model/MultipleForgeJarsFoundError.java new file mode 100644 index 00000000000..e1b206bcaaf --- /dev/null +++ b/src/main/java/forge/model/MultipleForgeJarsFoundError.java @@ -0,0 +1,23 @@ +package forge.model; + +//import java.io.IOException; + +/** + * Exception thrown by model when it is trying to find a single forge jar, but + * it finds more than one. + */ +public class MultipleForgeJarsFoundError extends RuntimeException { + /** Automatically generated. */ + private static final long serialVersionUID = 8899307272033517172L; + + + /** + * Create an exception with a message. + * + * @param message the message, which could be the System's class path. + */ + public MultipleForgeJarsFoundError(final String message) { + super(message); + } + +} diff --git a/src/main/java/forge/view/swing/Main.java b/src/main/java/forge/view/swing/Main.java index 6a9367ee458..885c12aa118 100644 --- a/src/main/java/forge/view/swing/Main.java +++ b/src/main/java/forge/view/swing/Main.java @@ -42,7 +42,7 @@ public final class Main { public static void main(final String[] args) { ExceptionHandler.registerErrorHandling(); try { - final FModel model = new FModel(); + final FModel model = new FModel(null); Singletons.setModel(model); final FView view = new ApplicationView(); Singletons.setView(view); diff --git a/src/test/java/forge/model/BuildInfoTest.java b/src/test/java/forge/model/BuildInfoTest.java new file mode 100644 index 00000000000..7ae2e39fe17 --- /dev/null +++ b/src/test/java/forge/model/BuildInfoTest.java @@ -0,0 +1,180 @@ +package forge.model; + +import static net.slightlymagic.braids.util.testng.BraidsAssertFunctions.assertThrowsException; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import net.slightlymagic.braids.util.ClumsyRunnable; + +import org.testng.annotations.Test; + +/** + * Test the BuildInfo class. + */ +public class BuildInfoTest { + + /** System property name for the class path. */ + private static final String JAVA_CLASS_PATH = "java.class.path"; + /** Manifest attribute name for the Build ID. */ + private static final String MF_ATTR_NAME_BUILD = "Implementation-Build"; // NOPMD by Braids on 8/12/11 10:29 AM + /** Manifest attribute name for the version string. */ + private static final String MF_ATTR_NAME_VERSION = "Implementation-Version"; // NOPMD by Braids on 8/12/11 10:29 AM + + /** + * Test BuildInfo using a mock jar file. + * @throws IOException rarely + */ + @Test + public final void test_BuildInfo_mockJar() throws IOException { // NOPMD by Braids on 8/12/11 10:26 AM + File jarAsFile = null; + try { + jarAsFile = makeTmpJarWithManifest("BuildInfoTest-", ".jar", + new String[] {MF_ATTR_NAME_VERSION, "1.2.42", MF_ATTR_NAME_BUILD, "4200"}); + + final BuildInfo info = new BuildInfo(jarAsFile.getAbsolutePath()); + + final String actualVersion = info.getVersion(); + assertEquals(actualVersion, "1.2.42", "versions match"); + + final String actualBuildID = info.getBuildID(); + assertEquals(actualBuildID, "4200", "build IDs match"); + + } finally { + assertTrue(jarAsFile.delete(), "attempting to delete temporary jar file"); + } + } + + /** + * Test BuildInfo with one mock forge jar placed in the class path. + * @throws IOException indirectly + */ + @Test + public final void test_BuildInfo_oneJarInCP() throws IOException { // NOPMD by Braids on 8/12/11 10:26 AM + final String origClassPath = System.getProperty(JAVA_CLASS_PATH); + File jarAsFile = null; + + try { + jarAsFile = makeTmpJarWithManifest("forge-BuildInfoTest-", "-with-dependencies.jar", + new String[] {MF_ATTR_NAME_VERSION, "1.2.43", MF_ATTR_NAME_BUILD, "4201"}); + + System.setProperty(JAVA_CLASS_PATH, + jarAsFile.getAbsolutePath() + System.getProperty("path.separator") + origClassPath); + + + final BuildInfo info = new BuildInfo(); + + final String actualVersion = info.getVersion(); + assertEquals(actualVersion, "1.2.43", "versions match"); + + final String actualBuildID = info.getBuildID(); + assertEquals(actualBuildID, "4201", "build IDs match"); + + } finally { + assertTrue(jarAsFile.delete(), "attempting to delete temporary jar file"); + System.setProperty(JAVA_CLASS_PATH, origClassPath); + } + } + + /** + * Test BuildInfo with two mock forge jars placed in the class path; this + * is an error condition. + * + * @throws IOException indirectly + */ + @Test + public final void test_BuildInfo_twoJarsInCP() throws IOException { // NOPMD by Braids on 8/12/11 10:26 AM + final String origClassPath = System.getProperty(JAVA_CLASS_PATH); + File jarAsFile1 = null; + File jarAsFile2 = null; + + try { + jarAsFile1 = makeTmpJarWithManifest("forge-BuildInfoTest-1-", "-with-dependencies.jar", + new String[] {MF_ATTR_NAME_VERSION, "1.1.1", MF_ATTR_NAME_BUILD, "1111"}); + + jarAsFile2 = makeTmpJarWithManifest("forge-BuildInfoTest-2-", "-with-dependencies.jar", + new String[] {MF_ATTR_NAME_VERSION, "2.2.2", MF_ATTR_NAME_BUILD, "2222"}); + + final String pathSep = System.getProperty("path.separator"); + + System.setProperty(JAVA_CLASS_PATH, + jarAsFile1.getAbsolutePath() + pathSep + + jarAsFile2.getAbsolutePath() + pathSep + + origClassPath); + + final BuildInfo info = new BuildInfo(); + + assertThrowsException(MultipleForgeJarsFoundError.class, + new ClumsyRunnable() { + public void run() throws Exception { // NOPMD by Braids on 8/12/11 10:29 AM + info.getBuildID(); + } + }); + + } finally { + assertTrue(jarAsFile1.delete(), "attempting to delete 1st temporary jar"); + assertTrue(jarAsFile2.delete(), "attempting to delete 2nd temporary jar"); + System.setProperty(JAVA_CLASS_PATH, origClassPath); + } + } + + + /** + * Helper method to create jar files at specific locations with specific + * name-value pairs in their manifests. + * + * @param jarLocation where to create the jar file + * @param nameValuePairs has the form {"name1", "value1", "name2", "value2", ...} + */ + private File makeTmpJarWithManifest(final String fileNamePrefix, final String fileNameSuffix, + final String[] nameValuePairs) + throws IOException + { + if (nameValuePairs.length % 2 != 0) { + throw new IllegalArgumentException("nameValuePairs must contain an even number of elements."); + } + + File result = null; + FileOutputStream fileOut = null; + ZipOutputStream zipOut = null; + try { + result = File.createTempFile(fileNamePrefix, fileNameSuffix); + fileOut = new FileOutputStream(result); + zipOut = new ZipOutputStream(fileOut); + + final ZipEntry zipEntry = new ZipEntry("META-INF/MANIFEST.MF"); + zipOut.putNextEntry(zipEntry); + + zipOut.write(toASCII("Manifest-Version: 1.3\n")); + + for (int ix = 0; ix < nameValuePairs.length; ix += 2) { + zipOut.write(toASCII(nameValuePairs[ix])); + zipOut.write(toASCII(": ")); + zipOut.write(toASCII(nameValuePairs[ix + 1])); + zipOut.write(toASCII("\n")); + } + + zipOut.write(toASCII("\n")); + + zipOut.closeEntry(); + } finally { + if (zipOut != null) { + zipOut.close(); + } + if (fileOut != null) { + fileOut.close(); + } + } + + return result; + } + + private byte[] toASCII(final String str) { + return str.getBytes(BuildInfo.US_ASCII_CHARSET); + } +} diff --git a/src/test/java/forge/model/FModelTest.java b/src/test/java/forge/model/FModelTest.java new file mode 100644 index 00000000000..6d9a83302c7 --- /dev/null +++ b/src/test/java/forge/model/FModelTest.java @@ -0,0 +1,58 @@ +package forge.model; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +import java.io.FileNotFoundException; + +import org.testng.annotations.Test; + +/** + * Tests FModel. + */ +public class FModelTest { + + + /** + * Test constructor, close, and construct again. + * @throws FileNotFoundException if something is really wrong + */ + @Test + public final void test_ctor_close_ctor() throws FileNotFoundException { // NOPMD by Braids on 8/12/11 10:36 AM + final FModel modelA = new FModel(null); + assertNotNull(modelA, "modelA is not null"); + modelA.close(); + + System.err.println("log test"); // NOPMD by Braids on 8/12/11 10:36 AM + + final FModel modelB = new FModel(null); + assertNotNull(modelB, "modelB is not null"); + } + + /** + * Test getVersion. + * @throws FileNotFoundException if something is really wrong + */ + @Test + public final void test_getVersion() throws FileNotFoundException { // NOPMD by Braids on 8/12/11 10:36 AM + final FModel model = new FModel(null); + final String version = model.getBuildInfo().getVersion(); + model.close(); + + assertEquals(version, "SVN", "version is default"); + } + + /** + * Test getBuildID. + * @throws FileNotFoundException if something is really wrong + */ + @Test + public final void test_getBuildID() throws FileNotFoundException { // NOPMD by Braids on 8/12/11 10:36 AM + final FModel model = new FModel(null); + + // Just test for an unexpected exception. + model.getBuildInfo().getBuildID(); + + model.close(); + } +} diff --git a/src/test/java/net/slightlymagic/braids/util/testng/BraidsAssertFunctions.java b/src/test/java/net/slightlymagic/braids/util/testng/BraidsAssertFunctions.java index 9a37b91665e..e7d480e6745 100644 --- a/src/test/java/net/slightlymagic/braids/util/testng/BraidsAssertFunctions.java +++ b/src/test/java/net/slightlymagic/braids/util/testng/BraidsAssertFunctions.java @@ -1,41 +1,42 @@ package net.slightlymagic.braids.util.testng; import net.slightlymagic.braids.util.ClumsyRunnable; + import org.testng.Assert; -import org.testng.annotations.Test; /** * A collection of assert functions that go with TestNG. */ -@Test(groups = {"UnitTest"}) -public class BraidsAssertFunctions { +public final class BraidsAssertFunctions { - /** Do not instantiate.*/ - private BraidsAssertFunctions() {;} - - /** - * Assert that a function (ClumsyRunnable) throws the given exception, or - * a subclass of it. - * - * @param exnClass the exception we expect - * @param withScissors the code to run - */ - public static void assertThrowsException( - @SuppressWarnings("rawtypes") Class exnClass, - ClumsyRunnable withScissors) - { - try { - withScissors.run(); - } - catch (Exception exn) { - if (!exnClass.isInstance(exn)) { - Assert.fail("caught exception " + exn.getClass().getName() + - ", but expected " + exnClass.getName()); - } - - return; //success - } - - Assert.fail("expected exception " + exnClass.getName() + ", but none was thrown"); - } + /** Do not instantiate.*/ + private BraidsAssertFunctions() { + //empty + } + + /** + * Assert that a function (ClumsyRunnable) throws the given exception, or + * a subclass of it. + * + * @param exnClass the exception we expect + * @param withScissors the code to run + */ + public static void assertThrowsException( + @SuppressWarnings("rawtypes") final Class exnClass, + final ClumsyRunnable withScissors) + { + try { + withScissors.run(); + } + catch (Exception exn) { + if (!exnClass.isInstance(exn)) { + Assert.fail("caught exception " + exn.getClass().getName() + + ", but expected " + exnClass.getName()); + } + + return; //success + } + + Assert.fail("expected exception " + exnClass.getName() + ", but none was thrown"); + } } diff --git a/src/test/java/net/slightlymagic/braids/util/testng/package-info.java b/src/test/java/net/slightlymagic/braids/util/testng/package-info.java new file mode 100644 index 00000000000..3694b76b21e --- /dev/null +++ b/src/test/java/net/slightlymagic/braids/util/testng/package-info.java @@ -0,0 +1,3 @@ +/** Utilities for TestNG tests. Licensed under both Apache 2.0 and GNU Public Licenses. */ +package net.slightlymagic.braids.util.testng; +