View Javadoc
1   /*
2    * Copyright (C) 2010-2014 Hamburg Sud and the contributors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.aludratest.service.cmdline.impl;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.aludratest.exception.AutomationException;
27  import org.aludratest.exception.FunctionalFailure;
28  import org.aludratest.exception.TechnicalException;
29  import org.aludratest.service.Action;
30  import org.aludratest.service.SystemConnector;
31  import org.aludratest.service.cmdline.CommandLineCondition;
32  import org.aludratest.service.cmdline.CommandLineInteraction;
33  import org.aludratest.service.cmdline.CommandLineService;
34  import org.aludratest.service.cmdline.CommandLineVerification;
35  import org.aludratest.testcase.event.attachment.Attachment;
36  import org.databene.commons.IOUtil;
37  import org.databene.commons.Validator;
38  
39  /** Implements all {@link Action} interfaces of the {@link CommandLineService}.
40   * @author Volker Bergmann */
41  public class CommandLineActionImpl implements CommandLineInteraction, CommandLineVerification, CommandLineCondition {
42  
43      private CommandLineServiceConfiguration configuration;
44  
45      private int lastProcessId;
46      private Map<Integer, ProcessWrapper> processes;
47  
48      // configuration -----------------------------------------------------------
49  
50      /** Constructor with default visibility for preventing external instantiation
51       * @param configuration */
52      CommandLineActionImpl(CommandLineServiceConfiguration configuration) {
53          this.configuration = configuration;
54          this.lastProcessId = 0;
55          this.processes = new HashMap<Integer, ProcessWrapper>();
56      }
57  
58      @Override
59      public void setSystemConnector(SystemConnector systemConnector) {
60          // empty implementation
61      }
62  
63      // functional interface ----------------------------------------------------
64  
65      @Override
66      public int create(String processType, String processName, int processTimeout, int responseTimeout, String... command) {
67          int processId = ++lastProcessId;
68          ProcessWrapper process = new ProcessWrapper(processId, processTimeout, responseTimeout, command);
69          processes.put(processId, process);
70          return processId;
71      }
72  
73      @Override
74      public void start(String processType, String processName, int processId) {
75          ProcessWrapper process = getProcess(processId);
76          try {
77              process.start();
78          }
79          catch (IOException e) {
80              throw new AutomationException("Error executing command: " + process, e);
81          }
82      }
83  
84      @Override
85      public void assertNextLineOfStdOutMatches(String processType, String processName, int processId, Validator<String> validator) {
86          String line = readLineOfStdOut(processType, processName, processId);
87          if (!validator.valid(line)) {
88              ProcessWrapper process = getProcess(processId);
89              throw new FunctionalFailure("Standard output of process " + process + " is not accepted by " + validator + ": "
90                      + line);
91          }
92      }
93  
94      @Override
95      public void assertEmptyStdOut(String processType, String processName, int processId) {
96          ProcessWrapper process = getProcess(processId);
97          if (process.getStdOut().availableWithinTimeout()) {
98              throw new FunctionalFailure("Unexpected output of process " + process + ": " + readLineOfStdOut(process));
99          }
100     }
101 
102     @Override
103     public String readLineOfStdOut(String processType, String processName, int processId) {
104         ProcessWrapper process = getProcess(processId);
105         return readLineOfStdOut(process);
106     }
107 
108     @Override
109     public void assertNextLineOfErrOutMatches(String processType, String processName, int processId, Validator<String> validator) {
110         String line = readLineOfErrOut(processType, processName, processId);
111         if (!validator.valid(line)) {
112             ProcessWrapper process = getProcess(processId);
113             throw new FunctionalFailure("Error output of process " + process + " is not accepted by " + validator + ": " + line);
114         }
115     }
116 
117     @Override
118     public void assertEmptyErrOut(String processType, String processName, int processId) {
119         ProcessWrapper process = getProcess(processId);
120         if (process.getErrOut().availableWithinTimeout()) {
121             throw new FunctionalFailure("Unexpected error output of process " + process + ": " + readLineOfErrOut(process));
122         }
123     }
124 
125     @Override
126     public void assertExitCodeEquals(String processType, String processName, int processId, int expectedValue) {
127         ProcessWrapper process = getProcess(processId);
128         int actualValue = process.waitUntilFinished();
129         if (actualValue != expectedValue) {
130             throw new FunctionalFailure("Unexpected exit value. Expected " + expectedValue + ", but encountered " + actualValue);
131         }
132     }
133 
134     @Override
135     public void setRelativeWorkingDirectory(String processType, String processName, int processId, String relativeWorkingDirectory) {
136         try {
137             File workingDirectory = new File(configuration.getBaseDirectory(), relativeWorkingDirectory);
138             workingDirectory = workingDirectory.getCanonicalFile();
139             getProcess(processId).setWorkingDirectory(workingDirectory);
140         }
141         catch (IOException e) {
142             throw new AutomationException("Error setting working directory", e);
143         }
144     }
145 
146     @Override
147     public void setEnvironmentVariable(String processType, String processName, int processId, String key, String value) {
148         getProcess(processId).setEnvironmentVariable(key, value);
149     }
150 
151     @Override
152     public void redirectStdOutTo(String processType, String processName, int processId, OutputStream out) {
153         redirect(getProcess(processId).getStdOut(), out);
154     }
155 
156     @Override
157     public void redirectErrOutTo(String processType, String processName, int processId, OutputStream out) {
158         redirect(getProcess(processId).getErrOut(), out);
159     }
160 
161     @Override
162     public void redirectStdInFrom(String processType, String processName, int processId, InputStream in) {
163         try {
164             ProcessWrapper process = getProcess(processId);
165             OutputStream stdIn = process.getStdIn();
166             IOUtil.transfer(in, stdIn);
167             stdIn.flush();
168         }
169         catch (IOException e) {
170             throw new TechnicalException("Error redirecting stdout", e);
171         }
172     }
173 
174     @Override
175     public void skipStdOutUntilLineMatches(String processType, String processName, int processId, Validator<String> validator) {
176         ProcessWrapper process = getProcess(processId);
177         skipUntilOutputMatches(validator, process, process.getStdOut());
178     }
179 
180     @Override
181     public String readLineOfErrOut(String processType, String processName, int processId) {
182         ProcessWrapper process = getProcess(processId);
183         return readLineOfErrOut(process);
184     }
185 
186     @Override
187     public void skipErrOutUntilLineMatches(String processType, String processName, int processId, Validator<String> validator) {
188         ProcessWrapper process = getProcess(processId);
189         skipUntilOutputMatches(validator, process, process.getErrOut());
190     }
191 
192     @Override
193     public void enter(String processType, String processName, int processId, String text) {
194         getProcess(processId).enter(text);
195     }
196 
197     @Override
198     public int waitUntilFinished(String processType, String processName, int processId) {
199         return getProcess(processId).waitUntilFinished();
200     }
201 
202     @Override
203     public void destroy(String processType, String processName, int processId) {
204         getProcess(processId).destroy();
205     }
206 
207     // attachment support ------------------------------------------------------
208 
209     @Override
210     public List<Attachment> createDebugAttachments() {
211         // no attachments supported
212         return null;
213     }
214 
215     @Override
216     public List<Attachment> createAttachments(Object object, String title) {
217         // no attachments supported
218         return null;
219     }
220 
221     // private helpers ---------------------------------------------------------
222 
223     private ProcessWrapper getProcess(int processId) {
224         ProcessWrapper process = processes.get(processId);
225         if (process == null) {
226             throw new AutomationException("Invalid process id: " + processId);
227         }
228         return process;
229     }
230 
231     private String readLineOfStdOut(ProcessWrapper process) {
232         ProcessOutputReader reader = process.getStdOut();
233         try {
234             return reader.readLine();
235         }
236         catch (IOException e) {
237             throw new TechnicalException("Error reading stdout of process: " + process, e);
238         }
239     }
240 
241     private String readLineOfErrOut(ProcessWrapper process) {
242         try {
243             return process.getErrOut().readLine();
244         }
245         catch (IOException e) {
246             throw new TechnicalException("Error reading errout of process " + process, e);
247         }
248     }
249 
250     private void skipUntilOutputMatches(Validator<String> validator, ProcessWrapper process, ProcessOutputReader reader) {
251         String line;
252         do {
253             try {
254                 if (reader.availableWithinTimeout()) {
255                     line = reader.readLine();
256                 }
257                 else {
258                     // finished output without match
259                     throw new FunctionalFailure("Process '" + process + "' did not provide an output on " + reader
260                             + " that is accepted by " + validator);
261                 }
262             }
263             catch (IOException e) {
264                 throw new TechnicalException("Error reading " + reader + " of process " + process, e);
265             }
266         }
267         while (!validator.valid(line));
268         reader.pushBackLine(line);
269     }
270 
271     private void redirect(ProcessOutputReader reader, OutputStream out) {
272         try {
273             reader.redirectTo(out);
274         }
275         catch (IOException e) {
276             throw new TechnicalException("Error redirecting " + reader, e);
277         }
278     }
279 
280 }