001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb.test;
032:
033: import java.io.BufferedInputStream;
034: import java.io.File;
035: import java.io.FileInputStream;
036: import java.io.FileNotFoundException;
037: import java.io.IOException;
038: import java.util.regex.Matcher;
039: import java.util.regex.Pattern;
040:
041: // $Id: SqlToolHarness.java,v 1.13 2005/10/23 19:25:13 fredt Exp $
042:
043: /**
044: * Runs SqlTool tests based upon metacommands embedded in comments in SQL
045: * files.
046: */
047: public class SqlToolHarness {
048:
049: private static final int MAX_SQLFILE_LEN = 10240;
050: private static final String SYNTAX_MSG = "SYNTAX: java org.hsqldb.test.SqlToolHarness file1.sql [file2.sq...]";
051:
052: /**
053: * To test the SqlToolHarness class itself.
054: * (Basically, a sanity check).
055: *
056: * @param sa Each argument is a SQL file to process.
057: * @returns Exits with 0 or 1 depending on whether the last
058: * SqlToolHarness.execute() returned true or false (correspondingly).
059: */
060: public static void main(String[] sa) throws IOException,
061: InterruptedException {
062:
063: if (sa.length > 0 && sa[0].equals("-v")) {
064: sa = ExecHarness.shift(sa);
065:
066: System.setProperty("VERBOSE", "true");
067: }
068:
069: if (sa.length < 1) {
070: System.err.println(SYNTAX_MSG);
071: System.exit(1);
072: }
073:
074: SqlToolHarness harness = new SqlToolHarness();
075: boolean result = true;
076:
077: for (int i = 0; i < sa.length; i++) {
078: result = harness.execute(new File(sa[i]));
079:
080: System.err.println(sa[i] + " ==> " + result);
081: }
082:
083: System.exit(result ? 0 : 1);
084: }
085:
086: ExecHarness execHarness;
087:
088: public SqlToolHarness() {
089:
090: execHarness = new ExecHarness("java");
091:
092: String tmp = System.getProperty("VERBOSE");
093:
094: Verbose = (tmp != null) && (tmp.trim().length() > 0);
095: }
096:
097: private boolean Verbose = false;
098:
099: /**
100: * Run SqlTool according to metacommands embedded in given SQL file.
101: *
102: * @param file SQL file
103: */
104: public boolean execute(File file) throws IOException,
105: InterruptedException {
106:
107: Metadata md = new Metadata(file);
108:
109: if (Verbose) {
110: System.err.println("HARNESS METADATA:\n" + md);
111: }
112:
113: execHarness.clear();
114:
115: String[] args = new String[md.jvmargs.length + 1
116: + md.toolargs.length + (md.inputAsFile ? 1 : 0)];
117: int argIndex = 0;
118:
119: for (int i = 0; i < md.jvmargs.length; i++) {
120: args[argIndex++] = md.jvmargs[i];
121: }
122:
123: args[argIndex++] = org.hsqldb.util.SqlTool.class.getName();
124:
125: for (int i = 0; i < md.toolargs.length; i++) {
126: args[argIndex++] = md.toolargs[i];
127: }
128:
129: if (md.inputAsFile) {
130: args[argIndex++] = file.toString();
131: } else {
132: execHarness.setInput(file);
133: }
134:
135: if (Verbose) {
136: System.err.println("ALL ARGS: "
137: + ExecHarness.stringArrayToString(args));
138: }
139:
140: execHarness.setArgs(args);
141: execHarness.exec();
142:
143: if (Verbose) {
144: System.err
145: .println("STDOUT ******************************************");
146: System.out.print(execHarness.getStdout());
147: System.err
148: .println("ERROUT ******************************************");
149: System.err.print(execHarness.getErrout());
150: System.err
151: .println("*************************************************");
152: }
153:
154: if (md.exitValue != null) {
155: if (md.exitValue.intValue() != execHarness.getExitValue()) {
156: if (Verbose) {
157: System.err.println("Failed exit value test");
158: }
159:
160: return false;
161: }
162: }
163:
164: String stdout = execHarness.getStdout();
165: String errout = execHarness.getErrout();
166:
167: for (int i = 0; i < md.rejectErroutPatterns.length; i++) {
168: if (md.rejectErroutPatterns[i].matcher(errout).find()) {
169: if (Verbose) {
170: System.err.println("Failed rejectErrOut regex '"
171: + md.rejectErroutPatterns[i].pattern()
172: + "'");
173: }
174:
175: return false;
176: }
177: }
178:
179: for (int i = 0; i < md.rejectStdoutPatterns.length; i++) {
180: if (md.rejectStdoutPatterns[i].matcher(stdout).find()) {
181: if (Verbose) {
182: System.err.println("Failed rejectStdout regex '"
183: + md.rejectStdoutPatterns[i].pattern()
184: + "'");
185: }
186:
187: return false;
188: }
189: }
190:
191: for (int i = 0; i < md.requireErroutPatterns.length; i++) {
192: if (!md.requireErroutPatterns[i].matcher(errout).find()) {
193: if (Verbose) {
194: System.err.println("Failed requireErrorOut regex '"
195: + md.requireErroutPatterns[i].pattern()
196: + "'");
197: }
198:
199: return false;
200: }
201: }
202:
203: for (int i = 0; i < md.requireStdoutPatterns.length; i++) {
204: if (!md.requireStdoutPatterns[i].matcher(stdout).find()) {
205: if (Verbose) {
206: System.err.println("Failed requireStdOut regex '"
207: + md.requireStdoutPatterns[i].pattern()
208: + "'");
209: }
210:
211: return false;
212: }
213: }
214:
215: return true;
216: }
217:
218: private static String[] mtString = {};
219: private static Pattern[] mtPattern = {};
220:
221: private class Metadata {
222:
223: private byte[] ba = new byte[MAX_SQLFILE_LEN + 1];
224:
225: public Metadata(File inFile) throws FileNotFoundException,
226: IOException {
227:
228: String name, val;
229: String metaBlock = getHarnessMetaBlock(inFile);
230: /* This really only needed for debugging this class itself
231: * (SqlToolHarness).
232: if (Verbose) {
233: System.err.println("METABLOCK {\n" + metaBlock + "}");
234: }
235: */
236: Pattern directivePattern = Pattern
237: .compile("(?m)^\\s*(\\w+)\\s+(.*\\S+)?");
238: Matcher directiveMatcher = directivePattern
239: .matcher(metaBlock);
240:
241: while (directiveMatcher.find()) {
242: if (directiveMatcher.groupCount() != 2) {
243: throw new RuntimeException("Pattern '"
244: + directivePattern + " matched "
245: + directiveMatcher.groupCount()
246: + " groups.");
247: }
248:
249: name = directiveMatcher.group(1);
250: val = ((directiveMatcher.groupCount() == 2) ? directiveMatcher
251: .group(2)
252: : null);
253:
254: if (name.equals("arg")) {
255: toolargs = ExecHarness.push(val, toolargs);
256: } else if (name.equals("jvmarg")) {
257: jvmargs = ExecHarness.push(val, jvmargs);
258: } else if (name.equals("requireStdoutRegex")) {
259: requireStdoutPatterns = push(Pattern.compile(val),
260: requireStdoutPatterns);
261: } else if (name.equals("rejectStdoutRegex")) {
262: rejectStdoutPatterns = push(Pattern.compile(val),
263: rejectStdoutPatterns);
264: } else if (name.equals("requireErroutRegex")) {
265: requireErroutPatterns = push(Pattern.compile(val),
266: requireErroutPatterns);
267: } else if (name.equals("rejectErroutRegex")) {
268: rejectErroutPatterns = push(Pattern.compile(val),
269: rejectErroutPatterns);
270: } else if (name.equals("inputAsFile")) {
271: inputAsFile = Boolean.valueOf(val).booleanValue();
272: } else if (name.equals("exitValue")) {
273: exitValue = ((val == null) ? null : Integer
274: .valueOf(val));
275: } else {
276:
277: // TODO: Use a custom Exception class.
278: throw new IOException(
279: "Unknown Metadata directive: " + name);
280: }
281: }
282: }
283:
284: private String[] toolargs = mtString;
285: private String[] jvmargs = mtString;
286: private Pattern[] requireStdoutPatterns = mtPattern;
287: private Pattern[] rejectStdoutPatterns = mtPattern;
288: private Pattern[] requireErroutPatterns = mtPattern;
289: private Pattern[] rejectErroutPatterns = mtPattern;
290: private boolean inputAsFile = false;
291: private Integer exitValue = new Integer(0);
292:
293: public String toString() {
294:
295: StringBuffer sb = new StringBuffer();
296:
297: sb.append(" TOOLARGS: "
298: + ExecHarness.stringArrayToString(toolargs) + '\n');
299: sb.append(" JVMARGS: "
300: + ExecHarness.stringArrayToString(jvmargs) + '\n');
301: sb.append(" REQUIRE_STDOUT_PATTERNS: "
302: + patternArrayToString(requireStdoutPatterns)
303: + '\n');
304: sb
305: .append(" REJECT_STDOUT_PATTERNS: "
306: + patternArrayToString(rejectStdoutPatterns)
307: + '\n');
308: sb.append(" REQUIRE_ERROUT_PATTERNS: "
309: + patternArrayToString(requireErroutPatterns)
310: + '\n');
311: sb
312: .append(" REJECT_ERROUT_PATTERNS: "
313: + patternArrayToString(rejectErroutPatterns)
314: + '\n');
315: sb.append(" INPUTASFILE: " + inputAsFile + '\n');
316: sb.append(" EXITVALUE: " + exitValue + '\n');
317:
318: return sb.toString();
319: }
320:
321: private String getHarnessMetaBlock(File inFile)
322: throws FileNotFoundException, IOException {
323:
324: // The extra 1 is so we can request 1 more byte than we want.
325: // If that read is satisfied, we know that we read > MAX_SQLFILE_LEN
326: int i;
327: int writePointer = 0;
328: BufferedInputStream bis = new BufferedInputStream(
329: new FileInputStream(inFile));
330:
331: while ((i = bis.read(ba, writePointer, ba.length
332: - writePointer)) > 0) {
333: writePointer += i;
334: }
335:
336: if (i > -1) {
337: throw new IOException(inFile.toString()
338: + " is larger than " + (ba.length - 1)
339: + " bytes.");
340: }
341:
342: StringBuffer sb1 = new StringBuffer();
343: StringBuffer sb2 = new StringBuffer();
344: Pattern commentPattern = Pattern
345: .compile("(?s)(?<=/\\*).*?(?=\\*/)");
346: Pattern mdPattern = Pattern
347: .compile("(?m)(^\\s*HARNESS_METADATA\\s+BEGIN\\s*^)(?s)(.*?$)(?-s)"
348: + "(\\s*HARNESS_METADATA\\s+END\\s*$)");
349: Matcher commentMatcher = commentPattern.matcher(new String(
350: ba, 0, writePointer));
351:
352: while (commentMatcher.find()) {
353: sb1.append(commentMatcher.group() + '\n');
354: }
355:
356: Matcher mdMatcher = mdPattern.matcher(sb1);
357:
358: while (mdMatcher.find()) {
359: if (mdMatcher.groupCount() != 3) {
360: continue;
361: }
362:
363: sb2.append(mdMatcher.group(2) + '\n');
364: }
365:
366: return sb2.toString();
367: }
368: }
369:
370: public static Pattern[] push(Pattern newTail, Pattern[] pataIn) {
371:
372: Pattern[] pataOut = new Pattern[pataIn.length + 1];
373:
374: for (int i = 0; i < pataIn.length; i++) {
375: pataOut[i] = pataIn[i];
376: }
377:
378: pataOut[pataOut.length - 1] = newTail;
379:
380: return pataOut;
381: }
382:
383: public static String patternArrayToString(Pattern[] pata) {
384:
385: StringBuffer sb = new StringBuffer("{");
386:
387: for (int i = 0; i < pata.length; i++) {
388: if (i > 0) {
389: sb.append(',');
390: }
391:
392: sb.append(pata[i].pattern());
393: }
394:
395: return sb.toString() + '}';
396: }
397: }
|