Scenario.java revision 9192:535c335eb11c
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
24package compiler.compilercontrol.share.scenario;
25
26import compiler.compilercontrol.share.actions.BaseAction;
27import compiler.compilercontrol.share.method.MethodDescriptor;
28import compiler.compilercontrol.share.processors.CommandProcessor;
29import compiler.compilercontrol.share.processors.LogProcessor;
30import compiler.compilercontrol.share.processors.PrintProcessor;
31import compiler.compilercontrol.share.processors.QuietProcessor;
32import jdk.test.lib.Asserts;
33import jdk.test.lib.OutputAnalyzer;
34import jdk.test.lib.Pair;
35import jdk.test.lib.ProcessTools;
36import pool.PoolHelper;
37
38import java.io.BufferedReader;
39import java.io.IOException;
40import java.io.InputStreamReader;
41import java.io.PrintWriter;
42import java.lang.reflect.Executable;
43import java.net.ServerSocket;
44import java.net.Socket;
45import java.util.ArrayList;
46import java.util.Collections;
47import java.util.HashMap;
48import java.util.LinkedHashSet;
49import java.util.List;
50import java.util.Map;
51import java.util.Set;
52import java.util.concurrent.Callable;
53import java.util.function.Consumer;
54
55/**
56 * Test scenario
57 */
58public final class Scenario {
59    private final boolean isValid;
60    private final List<String> vmopts;
61    private final Map<Executable, State> states;
62    private final List<Consumer<OutputAnalyzer>> processors;
63
64    private Scenario(boolean isValid,
65                     List<String> vmopts,
66                     Map<Executable, State> states,
67                     List<CompileCommand> compileCommands) {
68        this.isValid = isValid;
69        this.vmopts = vmopts;
70        this.states = states;
71        processors = new ArrayList<>();
72        processors.add(new LogProcessor(states));
73        processors.add(new PrintProcessor(states));
74        List<CompileCommand> nonQuieted = new ArrayList<>();
75        List<CompileCommand> quieted = new ArrayList<>();
76        boolean metQuiet = false;
77        for (CompileCommand cc : compileCommands) {
78            metQuiet |= cc.command == Command.QUIET;
79            if (metQuiet) {
80                quieted.add(cc);
81            } else {
82                nonQuieted.add(cc);
83            }
84        }
85        processors.add(new CommandProcessor(nonQuieted));
86        processors.add(new QuietProcessor(quieted));
87    }
88
89    /**
90     * Executes scenario
91     */
92    public void execute() {
93        // Construct execution command with CompileCommand and class
94        List<String> argsList = new ArrayList<>();
95        // Add VM options
96        argsList.addAll(vmopts);
97        // Add class name that would be executed in a separate VM
98        String classCmd = BaseAction.class.getName();
99        argsList.add(classCmd);
100        OutputAnalyzer output;
101        try (ServerSocket serverSocket = new ServerSocket(0)) {
102            if (isValid) {
103                // Get port test VM will connect to
104                int port = serverSocket.getLocalPort();
105                if (port == -1) {
106                    throw new Error("Socket is not bound: " + port);
107                }
108                argsList.add(String.valueOf(port));
109                // Start separate thread to connect with test VM
110                new Thread(() -> connectTestVM(serverSocket)).start();
111            }
112            // Start test VM
113            output = ProcessTools.executeTestJvmAllArgs(
114                    argsList.toArray(new String[argsList.size()]));
115        } catch (Throwable thr) {
116            throw new Error("Execution failed", thr);
117        }
118        if (isValid) {
119            output.shouldHaveExitValue(0);
120            for (Consumer<OutputAnalyzer> processor : processors) {
121                processor.accept(output);
122            }
123        } else {
124            Asserts.assertNE(output.getExitValue(), 0, "VM should exit with "
125                    + "error for incorrect directives");
126            output.shouldContain("Parsing of compiler directives failed");
127        }
128    }
129
130    /*
131     * Performs connection with a test VM, sends method states
132     */
133    private void connectTestVM(ServerSocket serverSocket) {
134        /*
135         * There are no way to prove that accept was invoked before we started
136         * test VM that connects to this serverSocket. Connection timeout is
137         * enough
138         */
139        try (
140                Socket socket = serverSocket.accept();
141                PrintWriter pw = new PrintWriter(socket.getOutputStream(),
142                        true);
143                BufferedReader in = new BufferedReader(new InputStreamReader(
144                        socket.getInputStream()))) {
145            // Get pid of the executed process
146            int pid = Integer.parseInt(in.readLine());
147            Asserts.assertNE(pid, 0, "Got incorrect pid");
148            // serialize and send state map
149            for (Executable x : states.keySet()) {
150                pw.println("{");
151                pw.println(x.toGenericString());
152                pw.println(states.get(x).toString());
153                pw.println("}");
154            }
155        } catch (IOException e) {
156            throw new Error("Failed to write data", e);
157        }
158    }
159
160    /**
161     * Gets states of methods for this scenario
162     *
163     * @return pairs of executable and its state
164     */
165    public Map<Executable, State> getStates() {
166        return states;
167    }
168
169    public static enum Compiler {
170        C1("c1"),
171        C2("c2");
172
173        public final String name;
174
175        Compiler(String name) {
176            this.name = name;
177        }
178    }
179
180    /**
181     * Type of the compile command
182     */
183    public static enum Type {
184        OPTION(""),
185        FILE("command_file"),
186        DIRECTIVE("directives.json");
187
188        public final String fileName;
189
190        public CompileCommand createCompileCommand(Command command,
191                MethodDescriptor md, Compiler compiler) {
192            return new CompileCommand(command, md, compiler, this);
193        }
194
195        private Type(String fileName) {
196            this.fileName = fileName;
197        }
198    }
199
200    public static Builder getBuilder() {
201        return new Builder();
202    }
203
204    public static class Builder {
205        private final Set<String> vmopts = new LinkedHashSet<>();
206        private final Map<Type, StateBuilder<CompileCommand>> builders
207                = new HashMap<>();
208
209        public Builder() {
210            builders.put(Type.FILE, new CommandFileBuilder(Type.FILE.fileName));
211            builders.put(Type.OPTION, new CommandOptionsBuilder());
212            builders.put(Type.DIRECTIVE, new DirectiveBuilder(
213                    Type.DIRECTIVE.fileName));
214        }
215
216        public void add(CompileCommand compileCommand) {
217            String[] vmOptions = compileCommand.command.vmOpts;
218            Collections.addAll(vmopts, vmOptions);
219            StateBuilder builder = builders.get(compileCommand.type);
220            if (builder == null) {
221                throw new Error("TESTBUG: Missing builder for the type: "
222                        + compileCommand.type);
223            }
224            builder.add(compileCommand);
225        }
226
227        public Scenario build() {
228            boolean isValid = true;
229
230            // Get states from each of the state builders
231            Map<Executable, State> commandFileStates
232                    = builders.get(Type.FILE).getStates();
233            Map<Executable, State> commandOptionStates
234                    = builders.get(Type.OPTION).getStates();
235            Map<Executable, State> directiveFileStates
236                    = builders.get(Type.DIRECTIVE).getStates();
237
238            // Merge states
239            List<Pair<Executable, Callable<?>>> methods = new PoolHelper()
240                    .getAllMethods();
241            Map<Executable, State> finalStates = new HashMap<>();
242            for (Pair<Executable, Callable<?>> pair : methods) {
243                Executable x = pair.first;
244                State commandOptionState = commandOptionStates.get(x);
245                State commandFileState = commandFileStates.get(x);
246                State st = State.merge(commandOptionState, commandFileState);
247                State directiveState = directiveFileStates.get(x);
248                if (directiveState != null) {
249                    st = directiveState;
250                }
251                finalStates.put(x, st);
252            }
253
254            /*
255             * Create a list of commands from options and file
256             * to handle quiet command
257             */
258            List<CompileCommand> ccList = new ArrayList<>();
259            ccList.addAll(builders.get(Type.OPTION).getCompileCommands());
260            ccList.addAll(builders.get(Type.FILE).getCompileCommands());
261
262            // Get all VM options after we build all states and files
263            List<String> options = new ArrayList<>();
264            options.addAll(vmopts);
265            for (StateBuilder<?> builder : builders.values()) {
266                options.addAll(builder.getOptions());
267                isValid &= builder.isValid();
268            }
269            return new Scenario(isValid, options, finalStates, ccList);
270        }
271    }
272}
273