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