Path: blob/master/test/functional/cmdline_options_tester/src/TestConfigParser.java
6004 views
/*******************************************************************************1* Copyright (c) 2004, 2020 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122import java.util.*;23import java.util.regex.Pattern;2425import com.oti.j9.exclude.*;2627class TestConfigParser {28private ExcludeList _excludes;29private String[] _platforms;30private TestSuite _suite;31private boolean _verbose;32private int _outputLimit;33private boolean _debugCmdOnTimeout = false;34private String _modeHints;3536static String archName = System.getProperty("os.arch");37static boolean isRiscv = archName.toLowerCase().contains("riscv");3839/**40* If true, the test suite will print out the full output for each test case, regardless of whether41* it passed or failed42*/43void setVerbose( boolean verbose ) {44_verbose = verbose;45if (_suite != null) {46_suite.setVerbose( _verbose );47}48}4950void setOutputLimit ( int limit ) {51_outputLimit = limit;52if (_suite != null) {53_suite.setOutputLimit( _outputLimit );54}55}56/**57* Takes the given configuration file, exclude list, and optional platforms, and generates a test suite58* complete with test cases and expected outputs.59*60* @param configFile - The file from which to read the test case information. It should be formatted in61* accordance with the cmdlinetester.dtd file (see j:\j9\testing\cmdlinetester\)62* @param excludes - The exclude list, which specifies test cases to exclude on certain platforms. See63* j:\j9\testing\excludes\ for more information on the structure of this file. If null,64* no tests are excluded65* @param platforms - A comma-delimited set of strings identifying which platform-dependent output strings66* in the configuration file should be used.67* @param modeHints - List of hints specified for the mode being used to run this test suite.68* @return The test suite object from which the tests can be run, and which will output the pass/fail stats69* of the test cases.70*/71TestSuite runTests( String configFile, ExcludeList excludes, String platforms, boolean debugCmdOnTimeout, String modeHints ) {72_excludes = excludes;73_platforms = doSplit( platforms, "," );74_debugCmdOnTimeout = debugCmdOnTimeout;75_modeHints = modeHints;76XMLParser xmlp = new XMLParser();77xmlp.parse( configFile, new TestConfigDocumentHandler() );78return _suite;79}8081/**82* Basic string-splitting function, roughly equivalent to perl's split( /delim/, s ).83*84* @param s The string to be split85* @param delim The delimiter between the different pieces in the string. This must be non-null86* @return The array of pieces in the string. This will always be non-null, although may be of size zero87* if <code>s</code> is null or of length zero.88*/89private static String[] doSplit( String s, String delim ) {90if (s == null || s.length() == 0) {91return new String[0];92}93StringTokenizer st = new StringTokenizer( s, delim );94String[] pieces = new String[ st.countTokens() ];95for (int i = 0; i < pieces.length; i++) {96pieces[i] = st.nextToken();97}98return pieces;99}100101/**102* The class that is used as the document handler when SAX-ing the configuration file. This103* class is responsible for reading information from the configuration file, converting it to104* the relevant Java objects and adding them to the test suite.105*/106class TestConfigDocumentHandler implements IXMLDocumentHandler {107/* Test currently being processed */108private Test _currentTest;109/* Output case currently being processed */110private Output _currentOutput;111/* exec case currently being processed */112private CommandExecuter _currentExec;113/* The relevant non-element character data being spit out by the SAX parser */114private StringBuffer _data = new StringBuffer();115/* This is a single-iteration TestIterator which will run all the things given to it exactly once */116private TestIterator _iterator;117// if this is a new-style command where the executable is specified on its own, that is stored here (otherwise, this variable must be null)118private String _commandExecutable;119// this flag allows us to continue to use this parser implementation for the more complicated use of the command element without having to re-write it as a sort of delegating PDA120private boolean _isInNewCommandStanza;121// used to collect the contents of the in-order "arg" tags under the new-style command stanza (which are then passed as arguments to the underlying process)122private Vector _commandArgs;123// used to collect the contents of the in-order "input" tags under the new-style command stanza (which are then fed into the stdin of the underlying process as new-line terminated strings)124private Vector _commandInputLines;125126/**127* Empty implementation128*/129public void xmlStartDocument() {130}131132/**133* Empty implementation134*/135public void xmlEndDocument() {136}137138/**139* @see com.oti.j9.exclude.IXMLDocumentHandler#xmlStartElement(String,Hashtable)140*/141public void xmlStartElement(String elementName, Hashtable attributes) {142if (elementName.equalsIgnoreCase("suite")) {143System.out.println("*** Starting test suite: " + attributes.get("id") + " ***");144long timeout = getTimeout( attributes.get("timeout"), 10000 );145_suite = new TestSuite( _excludes, timeout, _verbose, _outputLimit );146_iterator = new TestIterator( _suite );147148} else if (elementName.equalsIgnoreCase("loop")) {149String index = (String)attributes.get("index");150String from = (String)attributes.get("from");151String until = (String)attributes.get("until");152String inc = (String)attributes.get("inc");153_iterator.addSubIterator( new TestIterator( _suite, index, from, until, inc ) );154155} else if (elementName.equalsIgnoreCase("test")) {156if (hasAllowedPlatform((String)attributes.get("platforms"))) {157String id = (String)attributes.get("id");158String modeHints = (String)attributes.get("modeHints");159if (hasAllowedHints(modeHints)) {160String timeout = (String)attributes.get("timeout");161162/* Set 16 hours (57600 secs) for timeout on RISC-V due to the lack of JIT */163timeout = (isRiscv) ? "57600" : timeout;164165_currentTest = new Test( id, timeout, _debugCmdOnTimeout );166} else {167if ((_modeHints == null) || (_modeHints.matches("\\s*"))) {168_modeHints = "empty";169}170System.out171.println("Test Skipped: "172+ id173+ " : Hints specified for this test { "174+ modeHints175+ " } do not match with the hints for current mode { "176+ _modeHints + " }\n");177_currentTest = null;178}179} else {180_currentTest = null;181}182} else if (elementName.equalsIgnoreCase("output")) {183if (hasAllowedPlatform((String)attributes.get("platforms"))) {184String regex = (String)attributes.get("regex");185String javaUtilPattern = "no";186if (null != attributes.get("javaUtilPattern")) {187javaUtilPattern = (String)attributes.get("javaUtilPattern");188}189String showRegexMatch = "no";190if (null != attributes.get("showMatch")) {191showRegexMatch = (String)attributes.get("showMatch");192}193String caseSensitive = (String)attributes.get("caseSensitive");194String type = (String)attributes.get("type");195_currentOutput = new Output( regex, javaUtilPattern, showRegexMatch, caseSensitive, type );196} else {197_currentOutput = null;198}199200} else if (elementName.equalsIgnoreCase("saveoutput")) {201if (hasAllowedPlatform((String)attributes.get("platforms"))) {202String regex = (String)attributes.get("regex");203String javaUtilPattern = "no";204String showRegexMatch = "no";205if (null != attributes.get("showMatch")) {206showRegexMatch = (String)attributes.get("showMatch");207}208String caseSensitive = (String)attributes.get("caseSensitive");209String saveName = (String)attributes.get("saveName");210String splitIndex = (String)attributes.get("splitIndex");211String splitBy = (String)attributes.get("splitBy");212String type = (String)attributes.get("type");213_currentOutput = new SaveOutput( regex, javaUtilPattern, showRegexMatch, caseSensitive, type, saveName, splitIndex, splitBy);214} else {215_currentOutput = null;216}217218} else if (elementName.equalsIgnoreCase("return")) {219if ((_currentTest != null) && (hasAllowedPlatform((String)attributes.get("platforms")))) {220String value = (String)attributes.get("value");221String type = (String)attributes.get("type");222_currentTest.addTestCondition( new ReturnValue( value, type ) );223}224225} else if (elementName.equalsIgnoreCase("variable")) {226if (hasAllowedPlatform((String)attributes.get("platforms"))) {227String name = (String)attributes.get("name");228String value = (String)attributes.get("value");229_iterator.addCommand( new VariableAdder( name, value ) );230}231232} else if (elementName.equalsIgnoreCase("exec")) {233if (hasAllowedPlatform((String)attributes.get("platforms"))) {234String command = (String)attributes.get("command");235String background = (String)attributes.get("background");236String captureVarStdout = (String)attributes.get("capture");237String captureVarStderr = (String)attributes.get("captureStderr");238String returnVar = (String)attributes.get("return");239_currentExec = new CommandExecuter(command, background, captureVarStdout, captureVarStderr, returnVar);240}241} else if (elementName.equalsIgnoreCase("delay")) {242_iterator.addCommand( new Delay( (String)attributes.get("length") ) );243244} else if (elementName.equalsIgnoreCase("echo")) {245_iterator.addCommand( new Echo( (String)attributes.get("value") ) );246} else if (elementName.equalsIgnoreCase("envvar")) {247// to get envvars to work properly in loops, changed EnvVar class to Command type248_iterator.addCommand( new EnvVar( (String)attributes.get("name"), (String)attributes.get("value") ) );249} else if (elementName.equalsIgnoreCase("command")) {250//the command tag can be structured multiple ways. It can just be simple like <command>echo This is what I am echoing</command> or251// it can be more complicated like:252// <command command="someCommand"><arg>one argument</arg><arg>arg two</arg><input>one line of stdin</input><input>second line of stdin</input></command>253// (note that the use of this "command" attribute looks odd but is to be consistent with "exec")254// so we will determine which one is which in this start tag since we would need a "command" attribute to be using the new style255String commandExecutable = (String)attributes.get("command");256if (null != commandExecutable)257{258//this is the "new-style" command tag so get the executable name259_commandExecutable = commandExecutable;260//also set the flag to know that we are in a command context and create the arrays for the arguments and inputs which may appear in that stanza261_isInNewCommandStanza = true;262_commandArgs = new Vector();263_commandInputLines = new Vector();264}265} else if (elementName.equalsIgnoreCase("if")) {266_iterator.addCommand( new IfTest( (String)attributes.get("testVariable"), (String)attributes.get("testValue"),267(String)attributes.get("resultVariable"), (String)attributes.get("resultValue") ) );268}269270// clear buffer to read in stuff within this tag271_data.setLength(0);272}273274/**275* @see com.oti.j9.exclude.IXMLDocumentHandler#xmlEndElement(String)276*/277public void xmlEndElement(String elementName) {278String readData = _data.toString();279280if (elementName.equalsIgnoreCase("loop")) {281// </loop> - close off the most-nested loop. if _iterator doesn't have any282// nested loops, it will return false, so we close off _iterator by setting it283// to null284_iterator.closeInnerLoop();285286} else if (elementName.equalsIgnoreCase("command")) {287// </command>288if (_currentTest != null) {289if (_isInNewCommandStanza) {290_currentTest.setSplitCommand(_commandExecutable, _commandArgs, _commandInputLines);291} else {292_currentTest.setCommand(readData);293}294_isInNewCommandStanza = false;295}296} else if (elementName.equalsIgnoreCase("output") || elementName.equalsIgnoreCase("saveoutput")) {297// </output> - check to make sure _currentOutput is not null298// before proceeding - this may happen if the output tag was thrown out299// due to platform-dependency-incompatibilities300if ((_currentTest != null) && (_currentOutput != null)) {301_currentOutput.setOutput(readData);302_currentTest.addTestCondition( _currentOutput );303}304305} else if (elementName.equalsIgnoreCase("test")) {306// </test> - pass it off to the iterator to run307if (_currentTest != null) {308_iterator.addTest( _currentTest );309}310} else if (elementName.equalsIgnoreCase("exec")) {311if (_currentExec != null) {312_iterator.addCommand( _currentExec );313}314_currentExec = null;315} else if (elementName.equalsIgnoreCase("arg")) {316if (_isInNewCommandStanza) {317_commandArgs.add(readData);318} else {319if (_currentExec != null) {320_currentExec.addArg(readData);321}322}323} else if (elementName.equalsIgnoreCase("input")) {324if (_isInNewCommandStanza) {325_commandInputLines.add(readData);326}327}328_data.setLength( 0 ); // any data in here has outlived it's usefulness329}330331/**332* @see com.oti.j9.exclude.IXMLDocumentHandler#xmlCharacters(String)333*/334public void xmlCharacters(String chars) {335_data.append( chars );336}337338private boolean hasAllowedPlatform( String platforms ) {339// check the platform-dependencies. if there are no340// listed platforms or if any one of the listed platforms matches any one of341// the platforms specified on the runtime commandline, then we return true342String evaluatePlatforms = TestSuite.evaluateVariables(platforms);343String[] requiredPlatforms = doSplit( evaluatePlatforms, "," );344boolean allow = (requiredPlatforms.length == 0);345for (int i = 0; i < _platforms.length; i++) {346for (int j = 0; j < requiredPlatforms.length; j++) {347if (Pattern.matches(requiredPlatforms[j], _platforms[i])) {348allow = true;349// like a double break350j = requiredPlatforms.length;351i = _platforms.length;352}353}354}355return allow;356}357358private boolean hasAllowedHints(String modeHints) {359String[] hintsSet = doSplit(modeHints, ",");360if (hintsSet.length == 0) {361return true;362} else if (_modeHints == null) {363return false;364} else {365for (int i = 0; i < hintsSet.length; i++) {366boolean allHintsFound = true;367String[] hints = doSplit(hintsSet[i], " ");368for (int j = 0; j < hints.length; j++) {369if (_modeHints.indexOf(hints[i]) == -1) {370allHintsFound = false;371break;372}373}374if (allHintsFound == true) {375return true;376}377}378}379return false;380}381382private long getTimeout( Object attribute, long defaultValue ) {383long timeout = defaultValue;384try {385timeout = 1000 * Integer.parseInt( ((String)attribute).trim() );386} catch (Exception e) { }387return timeout;388}389}390}391392393