1 /* 2 * $Id$ 3 * 4 * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.finder; 28 29 import java.io.File; 30 import java.util.HashMap; 31 import java.util.Iterator; 32 import java.util.Map; 33 34 import com.sun.javatest.TestEnvironment; 35 import com.sun.javatest.util.I18NResourceBundle; 36 import com.sun.javatest.util.StringArray; 37 38 /** 39 * This class allows a new tag "@expand" which allows a single test 40 * description to be expanded into multiple test descriptions using 41 * variable substitution. Variables are declared in the .jte file. 42 * 43 * The list of valid entries is identical to those found for JCK. 44 * There is no list of pre-defined keywords. If keyword checking 45 * is needed, then the list of allowed keywords must be 46 * provided via the <code>-allowKeyword</code> option. 47 * 48 * @see TagTestFinder 49 */ 50 public class ExpandTestFinder extends TagTestFinder 51 { 52 //-------------------------------------------------------------------------- 53 54 // This code is only needed to ensure that this test finder 55 // behaves the same as JCKTagTestFinder. If that finder ever 56 // derives from this one, it may be removed. 57 58 public ExpandTestFinder() { 59 validEntries = initTable(stdValidEntries); 60 validEntries = addTableItem(validEntries, "test", TRUE); 61 validKeywords = initTable(stdValidKeywords); 62 validKeywords = initTable(new String[] { }); 63 addExtension(".jasm", JavaCommentStream.class); 64 addExtension(".jcod", JavaCommentStream.class); 65 } // ExpandTestFinder() 66 67 protected int decodeArg(String[] args, int i) throws Fault { 68 if (args[i].equals("-verify")) { 69 verify = true; 70 return 1; 71 } 72 else if (args[i].equals("-allowEntry")) { 73 String e = args[i+1]; 74 validEntries.put(e.toLowerCase(), e); 75 return 2; 76 } 77 else if (args[i].equals("-allowKeyword")) { 78 String k = args[i+1]; 79 validKeywords.put(k.toLowerCase(), k); 80 return 2; 81 } 82 else 83 return super.decodeArg(args, i); 84 } // decodeArg() 85 86 private Map<String, String> initTable(String[] entries) { 87 Map<String, String> map = new HashMap<>(); 88 for (int i = 0; i < entries.length; i++) 89 map.put(entries[i].toLowerCase(), entries[i]); 90 return map; 91 } // initTable() 92 93 private Map<String, String> addTableItem(Map<String, String> entries, String name, String value) { 94 entries.put(name, value); 95 return entries; 96 } // addTableItem() 97 98 private boolean verify; 99 private Map<String, String> validEntries; 100 private Map<String, String> validKeywords; 101 102 private static final String TESTSUITE_HTML = "testsuite.html"; 103 // end of code for JCKTagTestFinder emulation 104 105 //-------------------------------------------------------------------------- 106 107 public void init(String [] args, File testSuiteRoot, TestEnvironment env) throws Fault { 108 // grab all environment variables open for expansion 109 if (expandVars == null) { 110 expandVars = new HashMap<>(3); 111 expandVarLen = new HashMap<>(3); 112 113 for (Iterator i = env.keys().iterator(); i.hasNext(); ) { 114 try { 115 String n = (String) (i.next()); 116 117 if (! n.startsWith("expand.")) 118 continue; 119 String[] v = env.lookup(n); 120 String fqvName = n.substring("expand.".length()); 121 // add to hashtable of fully-qualified varNames 122 expandVars.put(fqvName, v); 123 124 int pos; 125 if ((pos = fqvName.indexOf(".")) == -1) 126 continue; 127 String stem = fqvName.substring(0, pos); 128 129 // checking lengths of co-joined variables 130 Integer len = expandVarLen.get(stem); 131 if (len == null) { 132 // add to hashtable of valid stems 133 expandVarLen.put(stem, new Integer(v.length)); 134 } else { 135 if (v.length != len.intValue()) { 136 error(i18n, "expand.lengthMismatch", 137 new Object[] {stem, fqvName.substring(pos+1)} ); 138 } 139 } 140 } catch (TestEnvironment.Fault f) { 141 error(i18n, "expand.noDefn", f.getMessage()); 142 } 143 } 144 } 145 146 // This part of this method is only needed to ensure that this 147 // test finder behaves the same as JCKTagTestFinder. If that 148 // finder ever derives from this one, it may be removed. 149 if (testSuiteRoot.isDirectory()) { 150 File f = new File(testSuiteRoot, TESTSUITE_HTML); 151 if (!(f.exists() && !f.isDirectory() && f.canRead())) { 152 throw new Fault(i18n, "expand.badRootDir", 153 new Object[] {TESTSUITE_HTML, testSuiteRoot.getPath()}); 154 } 155 } 156 else { 157 String name = testSuiteRoot.getName(); 158 if (!name.equals(TESTSUITE_HTML)) { 159 throw new Fault(i18n, "expand.badRootFile"); 160 } 161 // force the test suite root to be the directory 162 testSuiteRoot = new File(testSuiteRoot.getParent()); 163 } 164 // end of code for JCKTagTestFinder emulation 165 166 super.init(args, testSuiteRoot, env); 167 } // init() 168 169 protected void foundTestDescription(Map<String, String> entries, File file, int line) { 170 // cross-product and loop call up 171 String origId = entries.get("id"); 172 if (origId == null) 173 origId = ""; 174 Map<String, Integer> loopVars = new HashMap<>(3); 175 foundTestDescription_1(entries, file, line, loopVars, origId); 176 } // foundTestDescription() 177 178 private void foundTestDescription_1(Map<String, String> entries, File file, int line, Map<String, Integer> loopVars, String id) { 179 for (Iterator<String> iter = entries.keySet().iterator(); iter.hasNext(); ) { 180 String name = iter.next(); 181 String value = entries.get(name); 182 // System.out.println("------ NAME: " + name + " VALUE: " + value); 183 184 if (name.equals("title") || name.equals("test") || name.equals("id")) 185 // don't tokenize 186 continue; 187 188 String [] words = StringArray.split(value); 189 for (int i = 0; i < words.length; i++) { 190 if (words[i].startsWith("$")) { 191 String varName = words[i].substring(1); 192 193 // strip out {}'s 194 if (varName.startsWith("{")) { 195 if (! varName.endsWith("}")) { 196 error(i18n, "expand.missingCloseCurly", words[i]); 197 continue; 198 } 199 varName = varName.substring(1, varName.length()-1); 200 } 201 202 // separate foo.bar into stem=foo and qualifier=bar 203 String stem; 204 String qualifier; 205 int pos; 206 if ((pos = varName.indexOf(".")) == -1) { 207 stem = varName; 208 qualifier = ""; 209 } else { 210 stem = varName.substring(0, pos); 211 qualifier = varName.substring(pos+1); 212 } 213 214 // System.out.println("stem: " + stem + " qual: " + qualifier); 215 216 if (testStems.get(stem) == null) 217 // this is something we shouldn't expand 218 continue; 219 220 String [] valueList; 221 if ((valueList = expandVars.get(varName)) == null) { 222 //String [] msgs = {"unable to find `expand' definition in environment", 223 //words[i]}; 224 //error(msgs); 225 continue; 226 } 227 228 String saveId = id; 229 Integer idx = loopVars.get(stem); 230 if (idx != null){ 231 int j = idx.intValue(); 232 words[i] = valueList[j]; 233 234 id = saveId + "_" + words[i]; 235 entries.put("id", id); 236 237 entries.put(name, StringArray.join(words)); 238 } else { 239 for (int j = 0; j < valueList.length; j++) { 240 words[i] = valueList[j]; 241 242 id = saveId + "_" + words[i]; 243 entries.put("id", id); 244 245 entries.put(name, StringArray.join(words)); 246 boolean loopy = !(qualifier.equals("")); 247 if (loopy) loopVars.put(stem, new Integer(j)); 248 // clone needed here because we over-wrote words[i] 249 foundTestDescription_1(new HashMap<>(entries), file, line, 250 loopVars, id); 251 if (loopy) loopVars.remove(stem); 252 253 } 254 return; 255 } 256 } 257 } 258 } 259 // clone may not be necessary, check for TestDescription upgrades 260 // super.foundTestDescription(entries, file, line); 261 super.foundTestDescription(new HashMap<>(entries), file, line); 262 } // foundTestDescription_1() 263 264 protected void processEntry(Map<String, String> entries, String name, String value) { 265 // System.out.println("NAME: " + name + " VALUE: " + value); 266 if (name.equals("expand")) { 267 if (testStems != null) { 268 error(i18n, "expand.multipleTags"); 269 } 270 testStems = new HashMap<>(3); 271 String [] stems = StringArray.split(value); 272 for (int i = 0 ; i < stems.length; i++) 273 testStems.put(stems[i], TRUE); 274 } else { 275 276 // This part of this method is only needed to ensure that this 277 // test finder behaves the same as JCKTagTestFinder. If that 278 // finder ever derives from this one, it may be removed. 279 boolean valid = (validEntries.get(name.toLowerCase()) != null); 280 281 if (verify) { 282 if (!valid) { 283 error(i18n, "expand.unknownEntry", 284 new Object[] {name, getCurrentFile()} ); 285 } 286 if (name.equalsIgnoreCase("keywords")) { 287 String[] keys = StringArray.split(value); 288 if (keys != null) { 289 for (int i = 0; i < keys.length; i++) { 290 String key = keys[i]; 291 // minor modification here to allow no keywords 292 //if (validKeywords.get(key.toLowerCase()) == null) { 293 if ((validKeywords.size() > 0) && (validKeywords.get(key.toLowerCase()) == null)) { 294 error(i18n, "expand.unknownKeyword", 295 new Object[] {key, getCurrentFile()} ); 296 } 297 } 298 } 299 } 300 } 301 302 if (valid) 303 entries.put(name, value); 304 // end of code for JCKTagTestFinder emulation 305 306 // if emulation code is removed, still need to do a put 307 // super.processEntry(entries, name, value); 308 } 309 } // processEntry() 310 311 //----------member variables------------------------------------------------ 312 313 private static final String TRUE = "true"; 314 private Map<String, String> testStems = null; 315 private Map<String, String[]> expandVars; 316 private Map<String, Integer> expandVarLen; 317 318 private String[] stdValidEntries = { 319 // required 320 "keywords", 321 "source", 322 "title", 323 // optional 324 "context", 325 "executeArgs", 326 "executeClass", 327 "executeNative", 328 "id", // defined and used internally by JT Harness 329 "rmicClasses", 330 "timeout" 331 }; 332 333 private String[] stdValidKeywords = { 334 // approved 335 "compiler", 336 "runtime", 337 "positive", 338 "negative", 339 "idl_inherit", 340 "idl_tie", 341 "interactive", 342 "jniinvocationapi", 343 "optionalPJava", 344 // will eventually be superceded/deprecated 345 "serial" 346 }; 347 private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(ExpandTestFinder.class); 348 }