From 2d1e90c85e878755af2e954d31e2d77ab3ce8af6 Mon Sep 17 00:00:00 2001 From: Thomas Bitonti Date: Fri, 15 Mar 2024 16:20:23 -0400 Subject: [PATCH] Rewrite TestSchemaGen: Improve output; capture stderr; refactor common operations; display key test values. --- .../schemagen/internal/SchemaGenTest.java | 463 ++++++++++++------ 1 file changed, 302 insertions(+), 161 deletions(-) diff --git a/dev/com.ibm.ws.config.schemagen/test/com/ibm/ws/config/schemagen/internal/SchemaGenTest.java b/dev/com.ibm.ws.config.schemagen/test/com/ibm/ws/config/schemagen/internal/SchemaGenTest.java index 89f9f54f6b3..796f721df94 100644 --- a/dev/com.ibm.ws.config.schemagen/test/com/ibm/ws/config/schemagen/internal/SchemaGenTest.java +++ b/dev/com.ibm.ws.config.schemagen/test/com/ibm/ws/config/schemagen/internal/SchemaGenTest.java @@ -1,6 +1,6 @@ package com.ibm.ws.config.schemagen.internal; /******************************************************************************* - * Copyright (c) 2022 IBM Corporation and others. + * Copyright (c) 2022,2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -12,32 +12,30 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ - +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.File; -import java.io.IOException; import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; import org.junit.Test; /** - * Tests the schemaGen command that exists in the wlp/bin directory. + * Test wlp/bin/schemaGen. + * + * */ public class SchemaGenTest { - public static final String WLP_BIN_DIR = "../build.image/wlp/bin"; - public static final String OUTPUT_FILE = "schemaGenOutput.xsd"; - public static final String HELP_OPTION = "-help"; - public static final String SCHEMAGEN_BAT = "./schemaGen.bat"; - public static final String SCHEMAGEN_LINUX_SCRIPT = "./schemaGen"; - public static final long TIMEOUT = 30_000_000_000L; // 30-second timeout - public static final int MAX_OUTPUT_LINES = 500; // Reasonable limit to output to standard output by schemaGen - public static final boolean IS_WINDOWS = isWindows(); - public static boolean isWindows() { String os = System.getProperty("os.name"); if (os.startsWith("Win")) { @@ -46,194 +44,337 @@ public static boolean isWindows() { return false; } + public static final boolean IS_WINDOWS = isWindows(); + + // Make sure to give the process builder paths with the + // correct slash for the test environment! + + // 'schemaGen' is a script in the server bin directory. + public static final String WLP_BIN_DIR_WIN = "..\\build.image\\wlp\\bin"; + public static final String WLP_BIN_DIR_UNIX = "../build.image/wlp/bin"; + public static final String WLP_BIN_DIR = (IS_WINDOWS ? WLP_BIN_DIR_WIN : WLP_BIN_DIR_UNIX); + public static File WLP_BIN = new File(WLP_BIN_DIR); + + // The script has a windows format (batch script) and a unix format (shell script). + public static final String SCHEMAGEN_SCRIPT_WIN = ".\\schemaGen.bat"; + public static final String SCHEMAGEN_SCRIPT_UNIX = "./schemaGen"; + public static final String SCHEMAGEN_SCRIPT = (IS_WINDOWS ? SCHEMAGEN_SCRIPT_WIN : SCHEMAGEN_SCRIPT_UNIX); + + public static final File SCHEMAGEN = new File(WLP_BIN, SCHEMAGEN_SCRIPT); + + // The option used to request help from schemaGen. + public static final String HELP_OPTION = "-help"; + + // Output file used by the test. + public static final String OUTPUT_FILE = "schemaGenOutput.xsd"; + public static final File OUTPUT = new File(WLP_BIN, OUTPUT_FILE); + public static final String OUTPUT_PATH = OUTPUT.getAbsolutePath(); + + // Parameters used to bound the script output and the script running time. + public static final int MAX_OUTPUT_LINES = 500; // Reasonable maximum count of output lines. + public static final long TIMEOUT_NS = 30_000_000_000L; // Reasonable maximum time to run (30s). + + public static void displayEnv() { + System.out.println("Environment:"); + System.out.println("OS [ os.name ] [ " + System.getProperty("os.name") + " ] isWindows [ " + IS_WINDOWS + " ]"); + System.out.println("WLP Bin [ " + WLP_BIN_DIR + " ] [ " + WLP_BIN.getAbsolutePath() + " ] Exists [ " + WLP_BIN.exists() + " ]"); + System.out.println(" Script [ " + SCHEMAGEN_SCRIPT + " ] [ " + SCHEMAGEN.getAbsolutePath() + " ] Exists [ " + SCHEMAGEN.exists() + " ]"); + System.out.println(" Output [ " + OUTPUT_FILE + " ] [ " + OUTPUT_PATH + " ] Exists [ " + OUTPUT.exists() + " ]"); + } + + public static ProcessBuilder schemaGen() { + return schemaGen(null); + } + + public static ProcessBuilder schemaGen(String option) { + ProcessBuilder pb; + if (IS_WINDOWS) { + if ( option == null ) { + pb = new ProcessBuilder("cmd", "/c", SCHEMAGEN_SCRIPT_WIN); + } else { + pb = new ProcessBuilder("cmd", "/c", SCHEMAGEN_SCRIPT_WIN, option); + } + } else { + if ( option == null ) { + pb = new ProcessBuilder(SCHEMAGEN_SCRIPT_UNIX); + } else { + pb = new ProcessBuilder(SCHEMAGEN_SCRIPT_UNIX, option); + } + } + + pb.directory(WLP_BIN); + + // Only one stream is being captured. If this redirect + // is not enabled, error output will not be captured. + pb.redirectErrorStream(true); + + return pb; + } + /** - * Test that when no parameters are passed, only basic usage info is displayed - * @throws IOException + * Process line processor. */ - @Test - public void testSchemaGenNoParms() throws IOException { - System.out.println("==== testSchemaGenNoParms ===="); + public static class ProcessActor { + public ProcessActor() { + this.numLines = 0; + } + + /** + * Process a single line. + * + * Increment the count of lines. + * + * @param line A line which is to be processed. + * + * @return The count of lines. (Subclasses are expected + * to redefine the return value.) + */ + int process(String line) { + numLines++; + + return numLines; + } - ProcessBuilder pb; + private int numLines; + + /** + * Tell how many lines were processed. + * + * @return The count of line which were processed. + */ + int getLines() { + return numLines; + } + } + + /** + * Launch a process and apply a process actor to its lines. + * + * The process is bounded. No more than {@link #MAX_OUTPUT_LINES} + * will be accept, and the process will not be allowed to run longer + * than {@link #TIMEOUT_NS} nano-seconds. An except is thrown if + * either bound is exceeded. + * + * @param pBuild A process builder. + * @param pAct A actor to apply to the lines from the process. + * @return The return code from the process. + * + * @return The process return code. + * + * @throws Exception Thrown if the process did not launch, or if process + * lines could not be read, or if a process bound was exceeded. An + * exception is not thrown if the process generates a failure return code. + */ + public static int apply(ProcessBuilder pBuild, ProcessActor pAct) throws Exception { + System.out.println("Running [ " + pBuild + " ]:"); + + long startTime = System.nanoTime(); + int lines = 0; + Process p = null; - try { - - if (IS_WINDOWS) { - pb = new ProcessBuilder("cmd", "/c", SCHEMAGEN_BAT); - } else { - pb = new ProcessBuilder(SCHEMAGEN_LINUX_SCRIPT); - } - - File dir = new File(WLP_BIN_DIR); - pb.directory(dir); - p = pb.start(); + p = pBuild.start(); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line; - - boolean usageAppears = false; - boolean encodingAppears = false; - - long startTime = System.nanoTime(); - int lineCounter = 0; + String line; while ((line = br.readLine()) != null) { + lines++; System.out.println(line); - - if (line.indexOf("Usage") != -1) { - usageAppears = true; - } - - if (line.indexOf("--encoding") != -1) { - encodingAppears = true; - } - // Exit loop if we are getting hung - if (lineCounter++ > MAX_OUTPUT_LINES) { - System.out.println("schemaGen usage info exceeded [ " + MAX_OUTPUT_LINES + " ] lines"); - break; - } else if (System.nanoTime() - startTime > TIMEOUT) { - System.out.println("SchemaGen exceeded [ " + TIMEOUT + " ] ns when displaying usage Info"); - break; + if (lines > MAX_OUTPUT_LINES) { + throw new Exception("Exceeded maximum output [ " + MAX_OUTPUT_LINES + " ] (lines)"); + } else if (System.nanoTime() - startTime > TIMEOUT_NS) { + throw new Exception("Exceeded maximum process time [ " + TIMEOUT_NS + " ] (ns)"); } + + pAct.process(line); } - - assertTrue("'Usage' should appear in command output", usageAppears); - assertFalse("'--encoding' should NOT appear in command output when no arguments passed.", encodingAppears); - System.out.println("PASSED"); - + } finally { - if ( p!= null) { - p.destroy(); + if (p != null) { + if ( !p.waitFor(30, TimeUnit.SECONDS) ) { + p.destroyForcibly(); + fail("Process ran longer than 30 seconds"); + } } } + + int rc = ((p == null) ? -1 : p.exitValue()); + System.out.println("Return code [ " + rc + " ]"); + return rc; } - + /** - * Test that when -help parameter is passed that help and usage information is displayed. - * @throws IOException + * A process actor used to detect specific values on lines. + */ + public static class TagActor extends ProcessActor { + /** + * Create a tag actor for specified tags. + * + * See {@link #process(String)} for processing details. + * + * @param tags Values which are to be detected. + */ + public TagActor(String... tags) { + super(); + + Map useHits = new HashMap<>(tags.length); + + for ( String tag : tags ) { + useHits.put(tag, new int[] { 0 }); + } + + this.tagHits = useHits; + } + + private final Map tagHits; + + /** + * Tell how many hits were recorded for a specified + * tag. Answer zero if the tag is not being detected + * by this actor. + * + * @param tag A tag value. + * + * @return The number of hits which were recorded for the tag. + */ + public int getHits(String tag) { + int[] hits = tagHits.get(tag); + return ((hits == null) ? 0 : hits[0]); + } + + private int totalHits; + + /** + * Answer the total number of hits for all tags. + * + * @return The total number of hits for all tags. + */ + public int getHits() { + return totalHits; + } + + /** + * Subclass extension: Record the tags which appear on the + * line. Each tag is detected at most once. Multiple tags + * will be detected on the same line. + * + * Add the number of new hits to the total number of hits. + * + * @param line A line which is to be processed. + * + * @return The number of keys which were detected. + */ + @Override + public int process(String line) { + super.process(line); // Increment the number of lines + + int hits = 0; + for ( String key : tagHits.keySet() ) { + if ( line.indexOf(key) != -1 ) { + (tagHits.get(key))[0]++; + hits++; + } + } + + totalHits += hits; + + return hits; + } + } + + /** + * Test that when no parameters are passed, only basic usage info is displayed. */ @Test - public void testSchemaGenHelp() throws IOException { - System.out.println("==== testSchemaGenHelp ===="); + public void testSchemaGenNoParms() throws Exception { + System.out.println("==== testSchemaGenNoParms ===="); + displayEnv(); + + TagActor pActor = new TagActor("Usage", "--encoding"); + int rc = apply( schemaGen(), pActor ); - ProcessBuilder pb; - Process p = null; - try { + assertEquals("Unexpected return code", 0, rc); + + int usageCount = pActor.getHits("Usage"); + int encodingCount = pActor.getHits("--encoding"); - if (IS_WINDOWS) { - pb = new ProcessBuilder("cmd", "/c", SCHEMAGEN_BAT, HELP_OPTION); - } else { - pb = new ProcessBuilder(SCHEMAGEN_LINUX_SCRIPT, HELP_OPTION); - } + assertTrue("'Usage' should appear", (usageCount > 0)); + System.out.println("Detected 'Usage'"); - File dir = new File(WLP_BIN_DIR); - pb.directory(dir); - p = pb.start(); + assertTrue("'--encoding' should NOT appear", (encodingCount == 0)); + System.out.println("Did not detect detect '--encoding'"); - BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line; - - boolean usageAppears = false; - boolean encodingAppears = false; + System.out.println("PASSED"); + System.out.println(); + } - long startTime = System.nanoTime(); - int lineCounter = 0; - - while ((line = br.readLine()) != null) { - System.out.println(line); - - if (line.indexOf("Usage") != -1) { - usageAppears = true; - } - - if (line.indexOf("--encoding") != -1) { - encodingAppears = true; - } + /** + * Test that when help parameters are passed, help info is displayed. + */ + @Test + public void testSchemaGenHelp() throws Exception { + System.out.println("==== testSchemaGenHelp ===="); + displayEnv(); + + TagActor pActor = new TagActor("Usage", "--encoding"); + int rc = apply( schemaGen(HELP_OPTION), pActor ); - // Exit loop if we are getting hung - if (lineCounter++ > MAX_OUTPUT_LINES) { - System.out.println("schemaGen help exceeded [ " + MAX_OUTPUT_LINES + " ] lines"); - break; - } else if (System.nanoTime() - startTime > TIMEOUT) { - System.out.println("schemaGen exceeded [ " + TIMEOUT + " ] ns when displaying help"); - break; - } - } - - assertTrue("'Usage' should appear in command output", usageAppears); - assertTrue("'--encoding' should appear in command output when " + HELP_OPTION + " is passed.", encodingAppears); - System.out.println("PASSED"); - - } finally { - if ( p!= null) { - p.destroy(); - } - } + assertEquals("Unexpected return code", 0, rc); + + int usageCount = pActor.getHits("Usage"); + int encodingCount = pActor.getHits("--encoding"); + + assertTrue("'Usage' should appear", (usageCount > 0)); + System.out.println("Detected 'Usage'"); + + assertTrue("'--encoding' should appear", (encodingCount > 0)); + System.out.println("Detected '--encoding'"); + + System.out.println("PASSED"); + System.out.println(); } /** * Test that when an output file is specified as parameter that the output file is created, * and that CWWKG0109I "success" message is created. - * - * @throws IOException */ @Test - public void testSchemaGenOutput() throws IOException { + public void testSchemaGenOutput() throws Exception { System.out.println("==== testSchemaGenOutput ===="); + displayEnv(); - ProcessBuilder pb; - Process p = null; - try { - if (IS_WINDOWS) { - pb = new ProcessBuilder("cmd", "/c", SCHEMAGEN_BAT, OUTPUT_FILE); - } else { - pb = new ProcessBuilder(SCHEMAGEN_LINUX_SCRIPT, OUTPUT_FILE); - } + // Make sure there isn't stale output from another test. + OUTPUT.delete(); + assertFalse("Output [" + OUTPUT_PATH + "] does not exist", OUTPUT.exists()); - File dir = new File(WLP_BIN_DIR); - pb.directory(dir); - p = pb.start(); - - BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line; - - boolean successMsgAppears = false; + try { + TagActor pActor = new TagActor("CWWKG0109I"); - long startTime = System.nanoTime(); - int lineCounter = 0; - - while ((line = br.readLine()) != null) { - System.out.println(line); - - if (line.indexOf("CWWKG0109I") != -1) { - successMsgAppears = true; - } + int rc = apply( schemaGen(OUTPUT_FILE), pActor); - // Exit loop if we are getting hung - if (lineCounter++ > MAX_OUTPUT_LINES) { - System.out.println("schemaGen help exceeded [ " + MAX_OUTPUT_LINES + " ] lines"); - break; - } else if (System.nanoTime() - startTime > TIMEOUT) { - System.out.println("schemaGen exceeded [ " + TIMEOUT + " ] ns when displaying help"); - break; - } - } - - assertTrue("'CWWKG0109I' should appear in command output", successMsgAppears); - assertTrue("Should only output 1 message in successful case.", lineCounter==1); + assertEquals("Unexpected return code", 0, rc); + + int msgCount = pActor.getHits("CWWKG0109I"); + int numLines = pActor.getLines(); + + assertTrue("'CWWKG0109I' should appear once", (msgCount == 1)); + System.out.println("Detected 'CWWKG0109I'"); - // It should, however, generate the output file. - File outputFile = new File(WLP_BIN_DIR + "/" + OUTPUT_FILE); - assertTrue("File [" + outputFile.getName() + "] should exist.", outputFile.exists()); - outputFile.delete(); // clean up - System.out.println("PASSED"); + assertTrue("Should have exactly one output line", (numLines == 1)); + assertTrue("Output [ " + OUTPUT_PATH + " ] exists", OUTPUT.exists()); + System.out.println("Detected output [" + OUTPUT_PATH + " ]"); } finally { - if ( p!= null) { - p.destroy(); - } + // TODO: Would be nice to capture this output file in the build folder. + + // Don't leave the output behind. + OUTPUT.delete(); + assertFalse("Output [" + OUTPUT_PATH + "] does not exist", OUTPUT.exists()); } + + System.out.println("PASSED"); + System.out.println(); } }