From 9f22e33ebd4a1e2933472d52d7ce1b903c0c5f6a Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 18 Oct 2012 15:54:29 -0400 Subject: [PATCH] Verifying that the heap size of a SuiteResult (from Surefire) is linear in the sizes of TEST-*.xml + *-output.txt even when there is lots of stdio. --- test/pom.xml | 10 ++ .../org/jvnet/hudson/test/MemoryAssert.java | 63 +++++++++++ .../hudson/tasks/junit/SuiteResultTest.java | 105 ++++++++++++++++++ .../jvnet/hudson/test/MemoryAssertTest.java | 48 ++++++++ 4 files changed, 226 insertions(+) create mode 100644 test/src/main/java/org/jvnet/hudson/test/MemoryAssert.java create mode 100644 test/src/test/java/hudson/tasks/junit/SuiteResultTest.java create mode 100644 test/src/test/java/org/jvnet/hudson/test/MemoryAssertTest.java diff --git a/test/pom.xml b/test/pom.xml index 0f45e3203b..11b2a5d0b0 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -140,6 +140,16 @@ THE SOFTWARE. mockito-core test + + org.netbeans.modules + org-netbeans-insane + RELEASE72 + + + com.github.stephenc.findbugs + findbugs-annotations + 1.3.9-1 + diff --git a/test/src/main/java/org/jvnet/hudson/test/MemoryAssert.java b/test/src/main/java/org/jvnet/hudson/test/MemoryAssert.java new file mode 100644 index 0000000000..ef5246cc6a --- /dev/null +++ b/test/src/main/java/org/jvnet/hudson/test/MemoryAssert.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright 2012 Jesse Glick. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.hudson.test; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import static org.junit.Assert.*; +import org.netbeans.insane.scanner.CountingVisitor; +import org.netbeans.insane.scanner.ScannerUtils; + +/** + * Static utility methods for verifying heap memory usage. + * Uses the INSANE library + * to traverse the heap from within your test. + *

Object sizes are in an idealized JVM in which pointers are 4 bytes + * (realistic even for modern 64-bit JVMs in which {@code -XX:+UseCompressedOops} is the default) + * but objects are aligned on 8-byte boundaries (so dropping an {@code int} field does not always save memory). + *

{@code import static org.jvnet.hudson.test.MemoryAssert.*;} to use. + */ +public class MemoryAssert { + + private MemoryAssert() {} + + /** + * Verifies that an object and its transitive reference graph occupy at most a predetermined amount of memory. + * The referents of {@link WeakReference} and the like are ignored. + *

To use, run your test for the first time with {@code max} of {@code 0}; + * when it fails, use the reported actual size as your assertion maximum. + * When improving memory usage, run again with {@code 0} and tighten the test to both demonstrate + * your improvement quantitatively and prevent regressions. + * @param o the object to measure + * @param max the maximum desired memory usage (in bytes) + */ + public static void assertHeapUsage(Object o, int max) throws Exception { + CountingVisitor v = new CountingVisitor(); + ScannerUtils.scan(ScannerUtils.skipNonStrongReferencesFilter(), v, Collections.singleton(o), false); + int memoryUsage = v.getTotalSize(); + assertTrue(o + " consumes " + memoryUsage + " bytes of heap, " + (memoryUsage - max) + " over the limit of " + max, memoryUsage <= max); + } + +} diff --git a/test/src/test/java/hudson/tasks/junit/SuiteResultTest.java b/test/src/test/java/hudson/tasks/junit/SuiteResultTest.java new file mode 100644 index 0000000000..198b28e451 --- /dev/null +++ b/test/src/test/java/hudson/tasks/junit/SuiteResultTest.java @@ -0,0 +1,105 @@ +/* + * The MIT License + * + * Copyright 2012 Jesse Glick. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.tasks.junit; + +import edu.umd.cs.findbugs.annotations.SuppressWarnings; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.List; +import static org.jvnet.hudson.test.MemoryAssert.*; +import static org.junit.Assert.*; +import org.junit.Test; + +public class SuiteResultTest { + + @SuppressWarnings({"DM_DEFAULT_ENCODING", "OS_OPEN_STREAM", "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE"}) + @Test public void sizeSurefire() throws Exception { + File data = File.createTempFile("TEST-", ".xml"); + try { + Writer w = new FileWriter(data); + try { + PrintWriter pw = new PrintWriter(w); + pw.println(""); + pw.println(""); + // Simulating Surefire 2.12.4 with redirectTestOutputToFile=true: + for (int i = 0; i < 10; i++) { // these pass and SF omits stdio + pw.println(""); + } + for (int i = 10; i < 20; i++) { // these fail and SF includes per-test stdio + pw.println(""); + pw.println("some stack trace"); + pw.print(""); + for (int j = 0; j < 1000; j++) { + pw.println("t" + i + " out #" + j); + } + pw.println(""); + pw.print(""); + for (int j = 0; j < 1000; j++) { + pw.println("t" + i + " err #" + j); + } + pw.println(""); + pw.println(""); + } + pw.println(""); + pw.println(""); + pw.flush(); + } finally { + w.close(); + } + File data2 = new File(data.getParentFile(), data.getName().replaceFirst("^TEST-(.+)[.]xml$", "$1-output.txt")); + try { + w = new FileWriter(data2); + try { + PrintWriter pw = new PrintWriter(w); + for (int i = 0; i < 20; i++) { // stdio for complete suite (incl. passing tests) + for (int j = 0; j < 1000; j++) { + pw.println("t" + i + " out #" + j); + pw.println("t" + i + " err #" + j); + } + } + pw.flush(); + } finally { + w.close(); + } + SuiteResult sr = parseOne(data); + assertHeapUsage(sr, 242 + /* Unicode overhead */2 * (int) (/*259946*/data.length() + /*495600*/data2.length() + /* SuiteResult.file */data.getAbsolutePath().length())); + // XXX serialize using TestResultAction.XSTREAM and verify that round-tripped object has same size + } finally { + data2.delete(); + } + } finally { + data.delete(); + } + } + + private SuiteResult parseOne(File file) throws Exception { + List results = SuiteResult.parse(file, false); + assertEquals(1,results.size()); + return results.get(0); + } + +} diff --git a/test/src/test/java/org/jvnet/hudson/test/MemoryAssertTest.java b/test/src/test/java/org/jvnet/hudson/test/MemoryAssertTest.java new file mode 100644 index 0000000000..8b259799f8 --- /dev/null +++ b/test/src/test/java/org/jvnet/hudson/test/MemoryAssertTest.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright 2012 Jesse Glick. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.hudson.test; + +import java.lang.ref.WeakReference; +import static org.jvnet.hudson.test.MemoryAssert.*; +import static org.junit.Assert.*; +import org.junit.Test; + +public class MemoryAssertTest { + + @Test public void heapUsage() throws Exception { + Object[] biggie = new Object[1000]; + assertHeapUsage(biggie, 4016); + assertHeapUsage(new WeakReference(biggie), 56); + AssertionError e = null; + try { + assertHeapUsage(biggie, 1016); + } catch (AssertionError _e) { + e = _e; + } + assertNotNull(e); + assertTrue(e.toString(), e.getMessage().contains("3000")); + } + +} -- GitLab