/*
* The MIT License
*
* Copyright (c) 2011, 2014 Sony Mobile Communications Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sonymobile.tools.gerrit.gerritevents.mock;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.sonyericsson.hudson.plugins.gerrit.trigger.GerritServer;
import com.sonyericsson.hudson.plugins.gerrit.trigger.config.Config;
import com.sonyericsson.hudson.plugins.gerrit.trigger.config.IGerritHudsonTriggerConfig;
import org.apache.commons.lang.StringUtils;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.CommandFactory;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.UserAuth;
import org.apache.sshd.server.auth.UserAuthNone;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import com.jcraft.jsch.KeyPair;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.APPROVALS;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.AUTHOR;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.BRANCH;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.COMMIT_MESSAGE;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.CREATED_ON;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.EMAIL;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.ID;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.LAST_UPDATED;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.NAME;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.NUMBER;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.OWNER;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.PARENTS;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.PROJECT;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.REF;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.REVISION;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.STATUS;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.SUBJECT;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.TYPE;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.UPLOADER;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.URL;
import static com.sonymobile.tools.gerrit.gerritevents.dto.GerritEventKeys.VALUE;
/**
* The beginning of a mock of a sshd server. When it is done the idea is to use this to send in stream-events over the
* ssh connection, and make connection related tests without running Gerrit on the local machine. There is some progress
* but most of the predefined command types has issues.
*
* @author Robert Sandell <robert.sandell@sonyericsson.com>
*/
public class SshdServerMock implements CommandFactory {
/**
* The stream-events command.
*/
public static final String GERRIT_STREAM_EVENTS = "gerrit stream-events";
/**
* The default port that Gerrit usually listens to.
*/
public static final int GERRIT_SSH_PORT = 29418;
/**
* How long to sleep to let the ssh-keygen error output appear on stderr.
*/
protected static final int WAIT_FOR_ERROR_OUTPUT = 1000;
/**
* One second in ms.
*/
protected static final int ONE_SECOND = 1000;
/**
* Minimum time a thread can sleep.
*/
protected static final int MIN_SLEEP = 200;
private volatile CommandMock currentCommand;
private List<CommandMock> commandHistory;
private List<CommandLookup> commandLookups;
@Override
public Command createCommand(String s) {
CommandMock command = findAndCreateCommand(s);
return setCurrentCommand(command);
}
/**
* Finds a command that matches the given line or a new {@link CommandMock} if nothing is found.
*
* @param s the command line to match.
* @return a command.
*
* @see #createCommand(String)
* @see CommandLookup
*/
private CommandMock findAndCreateCommand(String s) {
CommandLookup found = null;
for (CommandLookup lookup : commandLookups) {
if (lookup.isCommand(s)) {
found = lookup;
break;
}
}
if (found != null) {
if (found.isOneShot()) {
commandLookups.remove(found);
}
return found.newInstance(s);
} else {
return new CommandMock(s);
}
}
/**
* Sets the current command being executed and adds it to the commandHistory.
*
* @param command the command.
* @return the command.
*/
protected synchronized CommandMock setCurrentCommand(CommandMock command) {
currentCommand = command;
if (commandHistory == null) {
commandHistory = new LinkedList<CommandMock>();
}
commandHistory.add(0, command);
return currentCommand;
}
/**
* The last started command. There could be other commands running in parallel.
*
* @return the current command.
*/
public CommandMock getCurrentCommand() {
return currentCommand;
}
/**
* Get the command history.
*
* @return the command history.
*/
public List<CommandMock> getCommandHistory() {
return commandHistory;
}
/**
* Gets the first running command that matches the given regular expression.
*
* @param commandSearch the regular expression to match.
* @return the found command or null.
*/
public synchronized CommandMock getRunningCommand(String commandSearch) {
if (commandHistory != null) {
Pattern p = Pattern.compile(commandSearch);
for (CommandMock command : commandHistory) {
if (!command.isDestroyed() && p.matcher(command.getCommand()).find()) {
return command;
}
}
}
return null;
}
/**
* Gets the number of commands that match the given regular expression from the command history.
*
* @param commandSearch the regular expression to match.
* @return number of found commands.
*/
public synchronized int getNrCommandsHistory(String commandSearch) {
int matches = 0;
if (commandHistory != null) {
Pattern p = Pattern.compile(commandSearch, Pattern.MULTILINE);
for (CommandMock command : commandHistory) {
Matcher matcher = p.matcher(command.getCommand());
if (matcher.find()) {
matches++;
}
}
}
return matches;
}
/**
* Specifies a command type to instantiate and give to mina when a command matching the given regular expression is
* wanted.
*
* @param commandPattern the regular expression
* @param cmd the class to create the command from. The class must have a constructor taking a single
* String argument which is the command requested.
* @throws NoSuchMethodException if there is no matching constructor.
*/
public synchronized void returnCommandFor(String commandPattern, Class<? extends CommandMock> cmd)
throws NoSuchMethodException {
returnCommandFor(commandPattern, cmd, new Object[0], new Class<?>[0]);
}
/**
* Specifies a command type to instantiate and give to mina when a command matching
* the given regular expression is wanted.
*
* @param commandPattern the regular expression
* @param cmd the class to create the command from. The class must have a constructor where the first
* argument is a String followed by the class types provided by the types parameter.
* @param arguments the other arguments to the constructor besides the command.
* @param types the other class types to match the constructor against.
* @throws NoSuchMethodException if there is no matching constructor.
*/
public synchronized void returnCommandFor(String commandPattern, Class<? extends CommandMock> cmd,
Object[] arguments, Class<?>[] types) throws NoSuchMethodException {
returnCommandFor(commandPattern, cmd, false, arguments, types);
}
/**
* Specifies a command type to instantiate and give to mina when a command matching the given regular expression is
* wanted.
*
* @param commandPattern the regular expression
* @param cmd the class to create the command from. The class must have a constructor where the first
* argument is a String followed by the class types provided by the types parameter.
* @param oneShot if this command should only be returned the first time it is called for.
* @param arguments the other arguments to the constructor besides the command.
* @param types the other class types to match the constructor against.
* @throws NoSuchMethodException if there is no matching constructor.
*/
public synchronized void returnCommandFor(String commandPattern, Class<? extends CommandMock> cmd, boolean oneShot,
Object[] arguments, Class<?>[] types) throws NoSuchMethodException {
Class<?>[] argumentTypes = new Class<?>[types.length + 1];
argumentTypes[0] = String.class;
System.arraycopy(types, 0, argumentTypes, 1, types.length);
Constructor<? extends CommandMock> constructor = cmd.getConstructor(argumentTypes);
if (constructor != null) {
if (commandLookups == null) {
commandLookups = new LinkedList<CommandLookup>();
}
commandLookups.add(new CommandLookup(cmd, commandPattern, oneShot, constructor, arguments));
}
}
/**
* Waits for a running command matching the provided regular expression to appear in the command history.
*
* @param commandSearch a regular expression.
* @param timeout the maximum time to wait for the command in ms.
* @return the command.
*/
public CommandMock waitForCommand(String commandSearch, int timeout) {
long startTime = System.currentTimeMillis();
SshdServerMock.CommandMock command = null;
do {
if (System.currentTimeMillis() - startTime >= timeout) {
throw new RuntimeException("Timeout!");
}
command = getRunningCommand(commandSearch);
if (command == null) {
try {
Thread.sleep(MIN_SLEEP);
//CS IGNORE EmptyBlock FOR NEXT 2 LINES. REASON: not needed.
} catch (InterruptedException e) {
}
}
} while (command == null);
System.out.println("Found it!!! " + command.getCommand());
return command;
}
/**
* Waits for number of commands matching the provided regular expression to appear in the command history.
*
* @param commandSearch a regular expression.
* @param need the number of occurrences to wait for.
* @param timeout the maximum time to wait for the command in ms.
* @return true if the nr of needed commands was found.
*/
public boolean waitForNrCommands(String commandSearch, int need, int timeout) {
long startTime = System.currentTimeMillis();
int got = 0;
do {
if (System.currentTimeMillis() - startTime >= timeout) {
throw new RuntimeException("Timeout!");
}
got = getNrCommandsHistory(commandSearch);
if (got != need) {
try {
Thread.sleep(MIN_SLEEP);
//CS IGNORE EmptyBlock FOR NEXT 2 LINES. REASON: not needed.
} catch (InterruptedException e) {
}
}
} while (got != need);
return true;
}
/**
* Starts a ssh server on the provided port.
*
* @param server the server mock to start
*
* @return the server.
* @throws IOException if so.
*/
public static SshServer startServer(SshdServerMock server) throws IOException {
SshServer sshd = SshServer.setUpDefaultServer();
sshd.setPort(0);
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));
List<NamedFactory<UserAuth>>userAuthFactories = new ArrayList<NamedFactory<UserAuth>>();
userAuthFactories.add(new UserAuthNone.Factory());
sshd.setUserAuthFactories(userAuthFactories);
sshd.setCommandFactory(server);
sshd.start();
return sshd;
}
/**
* Configures the GerritServer config to connect to the provided ssh server.
*
* @param sshd the server to connect to
* @param gerritServer the config to configure
* @param reconnect if the GerritServer connection should be restarted after the configuration change
*
* @see #getConfigFor(SshServer, IGerritHudsonTriggerConfig)
* @see GerritServer#stopConnection()
* @see GerritServer#startConnection()
*/
public static void configureFor(SshServer sshd, GerritServer gerritServer, boolean reconnect) {
if (reconnect) {
gerritServer.stopConnection();
}
configureFor(sshd, gerritServer);
if (reconnect) {
gerritServer.startConnection();
}
}
/**
* Configures the GerritServer config to connect to the provided ssh server.
*
* @param sshd the server to connect to
* @param sshKey the public key location to configure
* @param gerritServer the config to configure
* @see #getConfigFor(SshServer, IGerritHudsonTriggerConfig)
*/
public static void configureFor(final SshServer sshd, KeyPairFiles sshKey, GerritServer gerritServer) {
gerritServer.setConfig(getConfigFor(sshd, sshKey, gerritServer.getConfig()));
}
/**
* Configures the GerritServer config to connect to the provided ssh server.
*
* @param sshd the server to connect to
* @param gerritServer the config to configure
* @see #getConfigFor(SshServer, IGerritHudsonTriggerConfig)
*/
public static void configureFor(final SshServer sshd, GerritServer gerritServer) {
configureFor(sshd, null, gerritServer);
}
/**
* creates a new Config with the ssh hostname and port set to connect to the provided server.
*
* @param sshd the server to configure for
* @param existing the existing configuration
* @return a new Config
* @see Config#Config(IGerritHudsonTriggerConfig)
*/
public static Config getConfigFor(final SshServer sshd, IGerritHudsonTriggerConfig existing) {
return getConfigFor(sshd, null, existing);
}
/**
* creates a new Config with the ssh hostname and port set to connect to the provided server.
*
* @param sshd the server to configure for
* @param sshKey the public key location to configure
* @param existing the existing configuration
* @return a new Config
* @see Config#Config(IGerritHudsonTriggerConfig)
*/
public static Config getConfigFor(final SshServer sshd, KeyPairFiles sshKey, IGerritHudsonTriggerConfig existing) {
Config c = new Config(existing);
String host = sshd.getHost();
if (StringUtils.isBlank(host)) {
c.setGerritHostName("localhost");
} else {
c.setGerritHostName(host);
}
c.setGerritSshPort(sshd.getPort());
if (sshKey != null) {
c.setGerritAuthKeyFile(sshKey.getPrivateKey());
}
return c;
}
/**
* Generates a rsa key-pair in /tmp/jenkins-testkey for use with authenticating the trigger against the mock
* server.
*
* @return the path to the private key file
*
* @throws IOException if so.
* @throws InterruptedException if interrupted while waiting for ssh-keygen to finish.
* @throws JSchException if creation of the keys goes wrong.
*/
public static KeyPairFiles generateKeyPair() throws IOException, InterruptedException, JSchException {
File tmp = new File(System.getProperty("java.io.tmpdir"));
File priv = new File(tmp, "jenkins-testkey");
File pub = new File(tmp, "jenkins-testkey.pub");
if (!(priv.exists() && pub.exists())) {
if (priv.exists()) {
if (!priv.delete()) {
throw new IOException("Could not delete temp private key");
}
}
if (pub.exists()) {
if (!pub.delete()) {
throw new IOException("Could not delete temp public key");
}
}
System.out.println("Generating test key-pair.");
JSch jsch = new JSch();
KeyPair kpair = KeyPair.genKeyPair(jsch, KeyPair.RSA);
kpair.writePrivateKey(new FileOutputStream(priv));
kpair.writePublicKey(new FileOutputStream(pub), "Test");
System.out.println("Finger print: " + kpair.getFingerPrint());
kpair.dispose();
return new KeyPairFiles(priv, pub);
} else {
System.out.println("Test key-pair seems to already exist.");
return new KeyPairFiles(priv, pub);
}
}
/**
* Pointer to two key-pair files.
* Returned from {@link #generateKeyPair()}.
*/
public static final class KeyPairFiles {
private File privateKey;
private File publicKey;
/**
* Standard constructor.
*
* @param privateKey the private key
* @param publicKey the public key
*/
private KeyPairFiles(File privateKey, File publicKey) {
this.privateKey = privateKey;
this.publicKey = publicKey;
}
/**
* The private key.
* @return file pointer to the private key
*/
public File getPrivateKey() {
return privateKey;
}
/**
* The public key.
* @return file pointer to the public key
*/
public File getPublicKey() {
return publicKey;
}
}
/**
* A mocked ssh command.
*
* @see SshdServerMock#createCommand(String)
*/
public static class CommandMock implements Command {
/**
* The max ms to wait before checking if the command is destroyed.
*/
protected static final int WAIT_FOR_DESTROYED = 2000;
private InputStream inputStream;
private OutputStream outputStream;
private OutputStream errorStream;
private ExitCallback exitCallback;
private boolean destroyed = false;
/**
* The command.
*/
protected String command;
/**
* Standard constructor.
*
* @param command the command to "execute".
*/
public CommandMock(String command) {
this.command = command;
}
@Override
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public void setOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void setErrorStream(OutputStream errorStream) {
this.errorStream = errorStream;
}
@Override
public void setExitCallback(ExitCallback exitCallback) {
this.exitCallback = exitCallback;
}
/**
* Default implementation just waits for the command to be destroyed.
*
* @param environment env.
* @throws IOException if so.
*/
@Override
public void start(Environment environment) throws IOException {
System.out.println("Starting command: " + command);
//Default implementation just waits for a disconnect
while (!isDestroyed()) {
try {
synchronized (this) {
this.wait(WAIT_FOR_DESTROYED);
}
} catch (InterruptedException e) {
System.err.println("[SSHD-CommandMock] Awake.");
}
}
}
/**
* Stops the command from running.
*
* @param exitCode the exitCode to return to the client.
*/
public synchronized void stop(int exitCode) {
exitCallback.onExit(exitCode);
}
@Override
public void destroy() {
synchronized (this) {
destroyed = true;
notifyAll();
}
}
/**
* Is the command destroyed.
*
* @return true if so.
*/
public boolean isDestroyed() {
synchronized (this) {
return destroyed;
}
}
/**
* The input stream to the command.
*
* @return the input stream.
*/
public InputStream getInputStream() {
return inputStream;
}
/**
* the output stream from the command.
*
* @return the output stream.
*/
public OutputStream getOutputStream() {
return outputStream;
}
/**
* The error stream from the command.
*
* @return the error stream.
*/
public OutputStream getErrorStream() {
return errorStream;
}
/**
* The command from the client.
*
* @return the command.
*/
public String getCommand() {
return command;
}
}
/**
* A command that immediately returns 0. There can be some timing issues with this command.
*/
public static class EofCommandMock extends CommandMock {
/**
* Standard constructor.
*
* @param command the command.
*/
public EofCommandMock(String command) {
super(command);
}
@Override
public void start(Environment environment) throws IOException {
System.out.println("Starting EOF-command: " + getCommand());
this.stop(0);
}
}
/**
* A Command that prints a given list of lines when the {@link #now()} method is called and then exits with 0. This
* command is not working as expected yet.
*/
public static class PrintLinesCommand extends CommandMock {
private List<String> lines;
private boolean doItNow = false;
/**
* Standard constructor.
*
* @param command the command
* @param lines the lines to print.
*/
public PrintLinesCommand(String command, List<String> lines) {
super(command);
this.lines = lines;
}
/**
* call this to make the command print its lines to the output.
*/
public synchronized void now() {
doItNow = true;
this.notifyAll();
}
/**
* If it is time to start printing. Used for synchronous reading.
*
* @return true if so.
*/
private synchronized boolean isNow() {
return doItNow;
}
@Override
public void start(final Environment environment) throws IOException {
System.out.println("Starting PL-command: " + getCommand());
while (!isNow()) {
synchronized (this) {
try {
this.wait(ONE_SECOND);
} catch (InterruptedException e) {
System.err.println("Interrupted while waiting.");
}
}
}
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getOutputStream(), "UTF-8")));
for (String line : lines) {
System.out.println("Sending: " + line);
out.println(line);
out.flush();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
/**
* A Command that prints a version and then exits with 0.
*/
public static class SendVersionCommand extends CommandMock {
/**
* Standard constructor.
*
* @param command the command
*/
public SendVersionCommand(String command) {
super(command);
}
@Override
public void start(final Environment environment) throws IOException {
String line = "gerrit version 2.11.4";
System.out.println("Starting PL-command: " + getCommand());
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getOutputStream(), "UTF-8")));
System.out.println("Sending: " + line);
out.println(line);
out.flush();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
this.stop(0);
}
}
/**
* A Command that returns last patcheset with approvals.
*/
public static class SendQueryLastPatchSet extends CommandMock {
/**
* Standard constructor.
*
* @param command the command
*/
public SendQueryLastPatchSet(String command) {
super(command);
}
@Override
public void start(final Environment environment) throws IOException {
final int createdOn1 = 1449170072;
final int createdOn2 = 1449168976;
final int lastUpdated = 1449170950;
JSONObject jsonAccount = new JSONObject();
jsonAccount.put(EMAIL, "EngyCZ@gmail.com");
jsonAccount.put(NAME, "Engy");
JSONArray parents = new JSONArray();
parents.add("31581608d63510c13d7cb12d3e9a245ca4f72a62");
JSONObject currentPatchSet = new JSONObject();
currentPatchSet.put(NUMBER, "2");
currentPatchSet.put(REVISION, "87861b77a7614f8e6da19b017c588b741911983c");
currentPatchSet.put(PARENTS, parents);
currentPatchSet.put(REF, "refs/changes/00/100/2");
currentPatchSet.put(UPLOADER, jsonAccount);
currentPatchSet.put(CREATED_ON, createdOn1);
currentPatchSet.put(AUTHOR, jsonAccount);
JSONArray approvals = new JSONArray();
JSONObject crw = new JSONObject();
crw.put(TYPE, "Code-Review");
crw.put(VALUE, "2");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Code-Review");
crw.put(VALUE, "1");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Code-Review");
crw.put(VALUE, "-1");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Verified");
crw.put(VALUE, "2");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Verified");
crw.put(VALUE, "1");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Verified");
crw.put(VALUE, "-1");
approvals.add(crw);
currentPatchSet.put(APPROVALS, approvals);
JSONObject change = new JSONObject();
change.put(PROJECT, "project");
change.put(BRANCH, "branch");
change.put(ID, "I2343434344");
change.put(NUMBER, "100");
change.put(SUBJECT, "subject");
change.put(OWNER, jsonAccount);
change.put(URL, "http://localhost:8080");
change.put(COMMIT_MESSAGE, "Change");
change.put(CREATED_ON, createdOn2);
change.put(LAST_UPDATED, lastUpdated);
change.put("open", true);
change.put(STATUS, "NEW");
change.put("currentPatchSet", currentPatchSet);
System.out.println("Starting QueryLastPatchSet: " + getCommand());
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getOutputStream(), "UTF-8")));
System.out.println("Sending: " + change);
out.println(change);
out.flush();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
this.stop(0);
}
}
/**
* A Command that returns last patcheset with approvals.
*/
public static class SendQueryAllPatchSets extends CommandMock {
/**
* Standard constructor.
*
* @param command the command
*/
public SendQueryAllPatchSets(String command) {
super(command);
}
@Override
public void start(final Environment environment) throws IOException {
final int createdOn1 = 1449170072;
final int createdOn2 = 1449168976;
final int lastUpdated = 1449170950;
JSONObject jsonAccount = new JSONObject();
jsonAccount.put(EMAIL, "EngyCZ@gmail.com");
jsonAccount.put(NAME, "Engy");
JSONArray parents = new JSONArray();
parents.add("31581608d63510c13d7cb12d3e9a245ca4f72a62");
JSONObject currentPatchSet = new JSONObject();
currentPatchSet.put(NUMBER, "2");
currentPatchSet.put(REVISION, "87861b77a7614f8e6da19b017c588b741911983c");
currentPatchSet.put(PARENTS, parents);
currentPatchSet.put(REF, "refs/changes/00/100/2");
currentPatchSet.put(UPLOADER, jsonAccount);
currentPatchSet.put(CREATED_ON, createdOn1);
currentPatchSet.put(AUTHOR, jsonAccount);
JSONArray approvals = new JSONArray();
JSONObject crw = new JSONObject();
crw.put(TYPE, "Code-Review");
crw.put(VALUE, "2");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Code-Review");
crw.put(VALUE, "1");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Code-Review");
crw.put(VALUE, "-1");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Verified");
crw.put(VALUE, "2");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Verified");
crw.put(VALUE, "1");
approvals.add(crw);
crw = new JSONObject();
crw.put(TYPE, "Verified");
crw.put(VALUE, "-1");
approvals.add(crw);
currentPatchSet.put(APPROVALS, approvals);
JSONObject patchSet1 = new JSONObject();
patchSet1.put(NUMBER, "1");
patchSet1.put(REVISION, "009365b77a69cd5ecd05a699b19213682f7f8d79");
patchSet1.put(PARENTS, parents);
patchSet1.put(REF, "refs/changes/00/100/1");
patchSet1.put(UPLOADER, jsonAccount);
patchSet1.put(CREATED_ON, createdOn2);
patchSet1.put(AUTHOR, jsonAccount);
JSONObject patchSet2 = new JSONObject();
patchSet2.put(NUMBER, "2");
patchSet2.put(REVISION, "87861b77a7614f8e6da19b017c588b741911983c");
patchSet2.put(PARENTS, parents);
patchSet2.put(REF, "refs/changes/00/100/1");
patchSet2.put(UPLOADER, jsonAccount);
patchSet2.put(CREATED_ON, createdOn1);
patchSet2.put(AUTHOR, jsonAccount);
JSONArray patchSets = new JSONArray();
patchSets.add(patchSet1);
patchSets.add(patchSet2);
JSONObject change = new JSONObject();
change.put(PROJECT, "project");
change.put(BRANCH, "branch");
change.put(ID, "I2343434344");
change.put(NUMBER, "100");
change.put(SUBJECT, "subject");
change.put(OWNER, jsonAccount);
change.put(URL, "http://localhost:8080");
change.put(COMMIT_MESSAGE, "Change");
change.put(CREATED_ON, createdOn2);
change.put(LAST_UPDATED, lastUpdated);
change.put("open", true);
change.put(STATUS, "NEW");
change.put("currentPatchSet", currentPatchSet);
change.put("patchSets", patchSets);
System.out.println("Starting QueryAllPatchSets: " + getCommand());
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getOutputStream(), "UTF-8")));
System.out.println("Sending: " + change);
out.println(change);
out.flush();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
this.stop(0);
}
}
/**
* A Command that prints a project and then exits with 0 and is destroyed.
*/
public static class SendOneProjectCommand extends CommandMock {
/**
* Standard constructor.
*
* @param command the command
*/
public SendOneProjectCommand(String command) {
super(command);
}
@Override
public void start(final Environment environment) throws IOException {
String line = "abcProject";
System.out.println("Starting PL-command: " + getCommand());
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getOutputStream(), "UTF-8")));
System.out.println("Sending: " + line);
out.println(line);
out.flush();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
this.stop(0);
this.destroy();
}
}
/**
* A Command that prints 2 projects and then exits with 0.
*/
public static class SendTwoProjectsCommand extends CommandMock {
/**
* Standard constructor.
*
* @param command the command
*/
public SendTwoProjectsCommand(String command) {
super(command);
}
@Override
public void start(final Environment environment) throws IOException {
String line = "abcProject";
String line2 = "defProject";
System.out.println("Starting PL-command: " + getCommand());
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getOutputStream(), "UTF-8")));
System.out.println("Sending: " + line);
out.println(line);
System.out.println("Sending: " + line2);
out.println(line2);
out.flush();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
this.stop(0);
}
}
/**
* Utility class for looking up and creating commands.
*
* @see SshdServerMock#findAndCreateCommand(String)
*/
public static class CommandLookup {
private Class<? extends CommandMock> cmdClass;
private Pattern commandPattern;
private boolean oneShot;
private Constructor<? extends CommandMock> constructor;
private Object[] arguments;
/**
* Standard constructor.
*
* @param cmdClass the class of the command to create.
* @param commandPattern a regular expression matching a command the creation should be performed on.
* @param oneShot if this command should only be returned the first time it is called for.
* @param constructor the constructor of the command to call.
* @param arguments the arguments to the constructor except for the first actual command.
* @see SshdServerMock#returnCommandFor(String, Class)
* @see SshdServerMock#returnCommandFor(String, Class, Object[], Class[])
*/
public CommandLookup(Class<? extends CommandMock> cmdClass, Pattern commandPattern, boolean oneShot,
Constructor<? extends CommandMock> constructor, Object... arguments) {
this.cmdClass = cmdClass;
this.commandPattern = commandPattern;
this.oneShot = oneShot;
this.constructor = constructor;
this.arguments = arguments;
}
/**
* Standard constructor.
*
* @param cmdClass the class of the command to create.
* @param commandPattern a regular expression matching a command the creation should be performed on.
* @param oneShot if this command should only be returned the first time it is called for.
* @param constructor the constructor of the command to call.
* @param arguments the arguments to the constructor except for the first actual command.
* @see SshdServerMock#returnCommandFor(String, Class)
* @see SshdServerMock#returnCommandFor(String, Class, Object[], Class[])
*/
public CommandLookup(Class<? extends CommandMock> cmdClass, String commandPattern, boolean oneShot,
Constructor<? extends CommandMock> constructor, Object... arguments) {
this(cmdClass, Pattern.compile(commandPattern), oneShot, constructor, arguments);
}
/**
* If the given command matches the pattern.
*
* @param command the command
* @return true if so.
*/
public boolean isCommand(String command) {
return commandPattern.matcher(command).find();
}
/**
* If this command should only be returned the first time it is called for.
* @return true if so
*/
public boolean isOneShot() {
return oneShot;
}
/**
* Creates a new instance of the command with all it's parameters.
*
* @param command the first parameter to the constructor.
* @return a new instance of the command.
*/
public CommandMock newInstance(String command) {
try {
if (arguments == null || arguments.length <= 0) {
return constructor.newInstance(command);
} else {
Object[] args = new Object[arguments.length + 1];
args[0] = command;
System.arraycopy(arguments, 0, args, 1, arguments.length);
return constructor.newInstance(args);
}
} catch (Exception e) {
throw new RuntimeException("Unpredicted reflection error. ", e);
}
}
}
}