1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package compiler.compilercontrol.share.scenario;
  25 
  26 import compiler.compilercontrol.share.method.MethodDescriptor;
  27 import compiler.compilercontrol.share.processors.CommandProcessor;
  28 import compiler.compilercontrol.share.processors.LogProcessor;
  29 import compiler.compilercontrol.share.processors.PrintDirectivesProcessor;
  30 import compiler.compilercontrol.share.processors.PrintProcessor;
  31 import compiler.compilercontrol.share.processors.QuietProcessor;
  32 import jdk.test.lib.Asserts;
  33 import jdk.test.lib.OutputAnalyzer;
  34 import jdk.test.lib.Pair;
  35 import pool.PoolHelper;
  36 
  37 import java.lang.reflect.Executable;
  38 import java.util.ArrayList;
  39 import java.util.Collections;
  40 import java.util.HashMap;
  41 import java.util.LinkedHashSet;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Set;
  45 import java.util.concurrent.Callable;
  46 import java.util.function.Consumer;
  47 
  48 /**
  49  * Test scenario
  50  */
  51 public final class Scenario {
  52     private final boolean isValid;
  53     private final Map<Executable, State> states;
  54     private final List<Consumer<OutputAnalyzer>> processors;
  55     private final Executor executor;
  56     private final Consumer<List<OutputAnalyzer>> jcmdProcessor;
  57 
  58     private Scenario(boolean isValid,
  59                      List<String> vmopts,
  60                      Map<Executable, State> states,
  61                      List<CompileCommand> compileCommands,
  62                      List<JcmdCommand> jcmdCommands,
  63                      List<CompileCommand> directives) {
  64         this.isValid = isValid;
  65         this.states = states;
  66         processors = new ArrayList<>();
  67         processors.add(new LogProcessor(states));
  68         processors.add(new PrintProcessor(states));
  69         List<CompileCommand> nonQuieted = new ArrayList<>();
  70         List<CompileCommand> quieted = new ArrayList<>();
  71         boolean metQuiet = false;
  72         for (CompileCommand cc : compileCommands) {
  73             metQuiet |= cc.command == Command.QUIET;
  74             if (metQuiet) {
  75                 quieted.add(cc);
  76             } else {
  77                 nonQuieted.add(cc);
  78             }
  79         }
  80         processors.add(new CommandProcessor(nonQuieted));
  81         processors.add(new QuietProcessor(quieted));
  82         List<String> jcmdExecCommands = new ArrayList<>();
  83         boolean addCommandMet = false;
  84         boolean printCommandMet = false;
  85         for (JcmdCommand cmd : jcmdCommands) {
  86             switch (cmd.jcmdType) {
  87                 case ADD:
  88                     if (!addCommandMet) {
  89                         jcmdExecCommands.add(JcmdType.ADD.command);
  90                     }
  91                     addCommandMet = true;
  92                     break;
  93                 case PRINT:
  94                     printCommandMet = true;
  95                     break;
  96                 default:
  97                     jcmdExecCommands.add(cmd.jcmdType.command);
  98                     break;
  99             }
 100         }
 101         // Add print command only in the end to get directives printed
 102         if (printCommandMet) {
 103             jcmdExecCommands.add(JcmdType.PRINT.command);
 104         }
 105         jcmdProcessor = new PrintDirectivesProcessor(directives);
 106         executor = new Executor(isValid, vmopts, states, jcmdExecCommands);
 107     }
 108 
 109     /**
 110      * Executes scenario
 111      */
 112     public void execute() {
 113         List<OutputAnalyzer> outputList = executor.execute();
 114         // The first one contains output from the test VM
 115         OutputAnalyzer mainOuput = outputList.get(0);
 116         if (isValid) {
 117             mainOuput.shouldHaveExitValue(0);
 118             processors.forEach(processor -> processor.accept(mainOuput));
 119             // only the last output contains directives got from print command
 120             List<OutputAnalyzer> last = new ArrayList<>();
 121             last.add(outputList.get(outputList.size() - 1));
 122             jcmdProcessor.accept(last);
 123         } else {
 124             Asserts.assertNE(mainOuput.getExitValue(), 0, "VM should exit with "
 125                     + "error for incorrect directives");
 126             mainOuput.shouldContain("Parsing of compiler directives failed");
 127         }
 128     }
 129 
 130     /**
 131      * Gets states of methods for this scenario
 132      *
 133      * @return pairs of executable and its state
 134      */
 135     public Map<Executable, State> getStates() {
 136         return states;
 137     }
 138 
 139     public static enum Compiler {
 140         C1("c1"),
 141         C2("c2");
 142 
 143         public final String name;
 144 
 145         Compiler(String name) {
 146             this.name = name;
 147         }
 148     }
 149 
 150     /**
 151      * Type of diagnostic (jcmd) command
 152      */
 153     public static enum JcmdType {
 154         ADD("Compiler.directives_add " + Type.JCMD.fileName),
 155         PRINT("Compiler.directives_print"),
 156         CLEAR("Compiler.directives_clear"),
 157         REMOVE("Compiler.directives_remove");
 158 
 159         public final String command;
 160         private JcmdType(String command) {
 161             this.command = command;
 162         }
 163     }
 164 
 165     /**
 166      * Type of the compile command
 167      */
 168     public static enum Type {
 169         OPTION(""),
 170         FILE("command_file"),
 171         DIRECTIVE("directives.json"),
 172         JCMD("jcmd_directives.json") {
 173             @Override
 174             public CompileCommand createCompileCommand(Command command,
 175                     MethodDescriptor md, Compiler compiler) {
 176                 return new JcmdCommand(command, md, compiler, this,
 177                         JcmdType.ADD);
 178             }
 179         };
 180 
 181         public final String fileName;
 182 
 183         public CompileCommand createCompileCommand(Command command,
 184                 MethodDescriptor md, Compiler compiler) {
 185             return new CompileCommand(command, md, compiler, this);
 186         }
 187 
 188         private Type(String fileName) {
 189             this.fileName = fileName;
 190         }
 191     }
 192 
 193     public static Builder getBuilder() {
 194         return new Builder();
 195     }
 196 
 197     public static class Builder {
 198         private final Set<String> vmopts = new LinkedHashSet<>();
 199         private final Map<Type, StateBuilder<CompileCommand>> builders
 200                 = new HashMap<>();
 201         private final JcmdStateBuilder jcmdStateBuilder;
 202         private final List<JcmdCommand> jcmdCommands = new ArrayList<>();
 203 
 204         public Builder() {
 205             builders.put(Type.FILE, new CommandFileBuilder(Type.FILE.fileName));
 206             builders.put(Type.OPTION, new CommandOptionsBuilder());
 207             builders.put(Type.DIRECTIVE, new DirectiveBuilder(
 208                     Type.DIRECTIVE.fileName));
 209             jcmdStateBuilder = new JcmdStateBuilder(Type.JCMD.fileName);
 210         }
 211 
 212         public void add(CompileCommand compileCommand) {
 213             String[] vmOptions = compileCommand.command.vmOpts;
 214             Collections.addAll(vmopts, vmOptions);
 215             if (compileCommand.type == Type.JCMD) {
 216                 jcmdStateBuilder.add((JcmdCommand) compileCommand);
 217                 jcmdCommands.add((JcmdCommand) compileCommand);
 218             } else {
 219                 StateBuilder<CompileCommand> builder = builders.get(
 220                         compileCommand.type);
 221                 if (builder == null) {
 222                     throw new Error("TESTBUG: Missing builder for the type: "
 223                             + compileCommand.type);
 224                 }
 225                 builder.add(compileCommand);
 226             }
 227         }
 228 
 229         public Scenario build() {
 230             boolean isValid = true;
 231 
 232             // Get states from each of the state builders
 233             Map<Executable, State> commandFileStates
 234                     = builders.get(Type.FILE).getStates();
 235             Map<Executable, State> commandOptionStates
 236                     = builders.get(Type.OPTION).getStates();
 237             Map<Executable, State> directiveFileStates
 238                     = builders.get(Type.DIRECTIVE).getStates();
 239 
 240             // check if directives stack was cleared by jcmd
 241             boolean isClearedState = false;
 242             if (jcmdContainsCommand(JcmdType.CLEAR)) {
 243                 isClearedState = true;
 244             }
 245 
 246             // Merge states
 247             List<Pair<Executable, Callable<?>>> methods = new PoolHelper()
 248                     .getAllMethods();
 249             Map<Executable, State> finalStates = new HashMap<>();
 250             Map<Executable, State> jcmdStates = jcmdStateBuilder.getStates();
 251             for (Pair<Executable, Callable<?>> pair : methods) {
 252                 Executable x = pair.first;
 253                 State commandOptionState = commandOptionStates.get(x);
 254                 State commandFileState = commandFileStates.get(x);
 255                 State st = State.merge(commandOptionState, commandFileState);
 256                 if (!isClearedState) {
 257                     State directiveState = directiveFileStates.get(x);
 258                     if (directiveState != null) {
 259                         st = directiveState;
 260                     }
 261                 }
 262                 State jcmdState = jcmdStates.get(x);
 263                 st = State.merge(st, jcmdState);
 264 
 265                 finalStates.put(x, st);
 266             }
 267 
 268             /*
 269              * Create a list of commands from options and file
 270              * to handle quiet command
 271              */
 272             List<CompileCommand> ccList = new ArrayList<>();
 273             ccList.addAll(builders.get(Type.OPTION).getCompileCommands());
 274             ccList.addAll(builders.get(Type.FILE).getCompileCommands());
 275 
 276             /*
 277              * Create a list of directives to check which one was printed
 278              */
 279             List<CompileCommand> directives = new ArrayList<>();
 280             if (jcmdContainsCommand(JcmdType.PRINT)) {
 281                 if (!isClearedState) {
 282                     directives.addAll(builders.get(Type.DIRECTIVE)
 283                             .getCompileCommands());
 284                 }
 285                 directives.addAll(jcmdStateBuilder.getCompileCommands());
 286             }
 287 
 288             // Get all VM options after we build all states and files
 289             List<String> options = new ArrayList<>();
 290             options.addAll(vmopts);
 291             for (StateBuilder<?> builder : builders.values()) {
 292                 options.addAll(builder.getOptions());
 293                 isValid &= builder.isValid();
 294             }
 295             options.addAll(jcmdStateBuilder.getOptions());
 296             return new Scenario(isValid, options, finalStates, ccList,
 297                     jcmdCommands, directives);
 298         }
 299 
 300         // shows if jcmd have passed a specified jcmd command type
 301         private boolean jcmdContainsCommand(JcmdType type) {
 302             for (JcmdCommand jcmdCommand : jcmdCommands) {
 303                 if (jcmdCommand.jcmdType == type) {
 304                     return true;
 305                 }
 306             }
 307             return false;
 308         }
 309     }
 310 }