1 /*
   2  * Copyright (c) 2016, 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 jdk.testlibrary.tasks;
  25 
  26 import java.util.ArrayList;
  27 import java.util.Arrays;
  28 import java.util.Collections;
  29 import java.util.List;
  30 import java.util.Map;
  31 
  32 /**
  33  * The supertype for tasks.
  34  * Complex operations are modeled by building and running a "Task" object.
  35  * Tasks are typically configured in a fluent series of calls.
  36  */
  37 public interface Task {
  38 
  39     /**
  40      * {@code ALL-UNNAMED}
  41      */
  42     static final String ALL_UNNAMED = "ALL-UNNAMED";
  43 
  44     /** The platform line separator. */
  45     static final String lineSeparator = System.getProperty("line.separator");
  46     /**
  47      * Returns the name of the task.
  48      * @return the name of the task
  49      */
  50     String name();
  51 
  52     /**
  53      * Executes the task as currently configured.
  54      * @return a Result object containing the results of running the task
  55      * @throws TaskError if the outcome of the task was not as expected
  56      */
  57     Result run() throws TaskError;
  58 
  59     /**
  60      * Exception thrown by {@code Task.run} when the outcome is not as
  61      * expected.
  62      */
  63     public static class TaskError extends Error {
  64         /**
  65          * Creates a TaskError object with the given message.
  66          * @param message the message
  67          */
  68         public TaskError(String message) {
  69             super(message);
  70         }
  71     }
  72 
  73     /**
  74      * An enum to indicate the mode a task should use it is when executed.
  75      */
  76     public enum Mode {
  77         /**
  78          * The task should use the interface used by the command
  79          * line launcher for the task.
  80          * For example, for javac: com.sun.tools.javac.Main.compile
  81          */
  82         //next mode is commented out until there is a use for it
  83         //CMDLINE,
  84         /**
  85          * The task should use a publicly defined API for the task.
  86          * For example, for javac: javax.tools.JavaCompiler
  87          */
  88         //next mode is commented out until there is a use for it
  89         //API,
  90         /**
  91          * The task should use the standard launcher for the task.
  92          * For example, $JAVA_HOME/bin/javac
  93          */
  94         EXEC
  95     }
  96 
  97     /**
  98      * An enum to indicate the expected success or failure of executing a task.
  99      */
 100     public enum Expect {
 101         /** It is expected that the task will complete successfully. */
 102         SUCCESS,
 103         /** It is expected that the task will not complete successfully. */
 104         FAIL
 105     }
 106 
 107     /**
 108      * An enum to identify the streams that may be written by a {@code Task}.
 109      */
 110     public enum OutputKind {
 111         /** Identifies output written to {@code System.out} or {@code stdout}. */
 112         STDOUT,
 113         /** Identifies output written to {@code System.err} or {@code stderr}. */
 114         STDERR
 115         /** Identifies output written to a stream provided directly to the task. */
 116         //next output kind is commented out until there is a use for it
 117         //,DIRECT
 118     };
 119 
 120     /**
 121      * The results from running a {@link Task}.
 122      * The results contain the exit code returned when the tool was invoked,
 123      * and a map containing the output written to any streams during the
 124      * execution of the tool.
 125      * All tools support "stdout" and "stderr".
 126      * Tools that take an explicit PrintWriter save output written to that
 127      * stream as "main".
 128      */
 129     public static class Result {
 130         final Task task;
 131         final int exitCode;
 132         final Map<OutputKind, String> outputMap;
 133         final List<String> command;
 134 
 135         Result(Task task, int exitCode, Map<OutputKind, String> outputMap,
 136                List<String> command) {
 137             this.task = task;
 138             this.exitCode = exitCode;
 139             this.outputMap = outputMap;
 140             this.command = command;
 141         }
 142 
 143         Result(Task task, int exitCode, Map<OutputKind, String> outputMap) {
 144             this(task, exitCode, outputMap, Collections.EMPTY_LIST);
 145         }
 146 
 147         /**
 148          * Returns the exit code.
 149          * @return the exit code.
 150          */
 151         public int getExitCode() {
 152             return exitCode;
 153         }
 154 
 155         /**
 156          * Returns the exit code.
 157          * @return the exit code.
 158          */
 159         public List<String> getCommand() {
 160             return command;
 161         }
 162 
 163         /**
 164          * Returns the content of a specified stream.
 165          * @param outputKind the kind of the selected stream
 166          * @return the content that was written to that stream when the tool
 167          *  was executed.
 168          */
 169         public String getOutput(OutputKind outputKind) {
 170             return outputMap.get(outputKind);
 171         }
 172 
 173         /**
 174          * Returns the content of named streams as a list of lines.
 175          * @param outputKinds the kinds of the selected streams
 176          * @return the content that was written to the given streams when the tool
 177          *  was executed.
 178          */
 179         public List<String> getOutputLines(OutputKind... outputKinds) {
 180             List<String> result = new ArrayList<>();
 181             for (OutputKind outputKind : outputKinds) {
 182                 result.addAll(Arrays.asList(outputMap.get(outputKind).split(lineSeparator)));
 183             }
 184             return result;
 185         }
 186 
 187         /**
 188          * Writes the content of the specified stream to the log.
 189          * @param kind the kind of the selected stream
 190          * @return this Result object
 191          */
 192         public Result write(OutputKind kind) {
 193             String text = getOutput(kind);
 194             if (text == null || text.isEmpty())
 195                 System.out.println("[" + task.name() + ":" + kind + "]: empty");
 196             else {
 197                 System.out.println("[" + task.name() + ":" + kind + "]:");
 198                 System.out.print(text);
 199             }
 200             return this;
 201         }
 202 
 203         /**
 204          * Writes the content of all streams with any content to the log.
 205          * @return this Result object
 206          */
 207         public Result writeAll() {
 208             outputMap.forEach((name, text) -> {
 209                 if (!text.isEmpty()) {
 210                     System.out.println("[" + name + "]:");
 211                     System.out.print(text);
 212                 }
 213             });
 214             return this;
 215         }
 216 
 217         /**
 218          * Verify that the specified output contains the string
 219          *
 220          * @param kind the output kind
 221          * @param expectedString String that buffer should contain
 222          * @throws RuntimeException If the string was not found
 223          */
 224         public void shouldContain(OutputKind kind, String expectedString) {
 225             if (!getOutput(kind).contains(expectedString))
 226                 throw new RuntimeException("'" + expectedString
 227                         + "' missing from " + kind.name());
 228         }
 229 
 230         /**
 231          * Verify that either stdout or stderr contains the string
 232          *
 233          * @param expectedString String that buffer should contain
 234          * @throws RuntimeException If the string was not found
 235          */
 236         public void shouldContain(String expectedString) {
 237             for (OutputKind kind : OutputKind.values()) {
 238                 if (getOutput(kind).contains(expectedString))
 239                     return;
 240             }
 241             throw new RuntimeException("'" + expectedString
 242                     + "' missing from output");
 243         }
 244     }
 245 }
 246