package org.jenkinsci.plugins.workflow.cps;
import com.gargoylesoftware.htmlunit.HttpMethod;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import hudson.model.Describable;
import hudson.model.Queue;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.jenkinsci.plugins.structs.describable.ArrayType;
import org.jenkinsci.plugins.structs.describable.DescribableModel;
import org.jenkinsci.plugins.structs.describable.DescribableParameter;
import org.jenkinsci.plugins.structs.describable.ErrorType;
import org.jenkinsci.plugins.structs.describable.HeterogeneousObjectType;
import org.jenkinsci.plugins.structs.describable.HomogeneousObjectType;
import org.jenkinsci.plugins.structs.describable.ParameterType;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.jvnet.hudson.test.JenkinsRule;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.jenkinsci.plugins.workflow.util.StaplerReferer;
import org.kohsuke.stapler.NoStaplerConstructorException;
import static org.junit.Assert.*;
/**
* Test harness to test {@link Snippetizer}.
*/
public class SnippetizerTester {
private final JenkinsRule r;
/**
* This helper requires {@link JenkinsRule}.
*/
public SnippetizerTester(JenkinsRule r) {
this.r = r;
}
/**
* Tests a form submitting part of snippetizer.
*
* @param json
* The form submission value from the configuration page to be tested.
* @param responseText
* Expected snippet to be generated
* @param referer
* needed because of {@link StaplerReferer}
*/
public void assertGenerateSnippet(@Nonnull String json, @Nonnull String responseText, @CheckForNull String referer) throws Exception {
JenkinsRule.WebClient wc = r.createWebClient();
WebRequest wrs = new WebRequest(new URL(r.getURL(), Snippetizer.GENERATE_URL), HttpMethod.POST);
if (referer != null) {
wrs.setAdditionalHeader("Referer", referer);
}
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new NameValuePair("json", json));
// WebClient.addCrumb *replaces* rather than *adds*:
params.add(new NameValuePair(r.jenkins.getCrumbIssuer().getDescriptor().getCrumbRequestField(), r.jenkins.getCrumbIssuer().getCrumb(null)));
wrs.setRequestParameters(params);
WebResponse response = wc.getPage(wrs).getWebResponse();
assertEquals("text/plain", response.getContentType());
assertEquals(responseText, response.getContentAsString().trim());
}
/**
* Given a fully configured {@link Step}, make sure the output from the snippetizer matches the expected value.
*
* <p>
* As an additional measure, this method also executes the generated snippet and makes sure
* it yields identical {@link Step} object.
*
* @param step
* A fully configured step object
* @param expected
* Expected snippet to be generated.
*/
public void assertRoundTrip(Step step, String expected) throws Exception {
assertEquals(expected, Snippetizer.step2Groovy(step));
CompilerConfiguration cc = new CompilerConfiguration();
cc.setScriptBaseClass(DelegatingScript.class.getName());
GroovyShell shell = new GroovyShell(r.jenkins.getPluginManager().uberClassLoader,new Binding(),cc);
DelegatingScript s = (DelegatingScript) shell.parse(expected);
s.o = new DSL(new DummyOwner()) {
// for testing, instead of executing the step just return an instantiated Step
@Override
protected Object invokeStep(StepDescriptor d, Object args) {
try {
return d.newInstance(parseArgs(args, d).namedArgs);
} catch (Exception e) {
throw new AssertionError(e);
}
}
};
Step actual = (Step) s.run();
r.assertEqualDataBoundBeans(step, actual);
}
/**
* Recurses through the model of a {@link Describable} class's {@link DescribableModel} and its parameters to verify that doc generation will work.
*
* @param describableClass
* A {@link Class} implementing {@link Describable}
* @throws Exception
* If any errors are encountered.
*/
@SuppressWarnings("unchecked")
public static void assertDocGeneration(Class<? extends Describable> describableClass) throws Exception {
DescribableModel<?> model = new DescribableModel(describableClass);
assertNotNull(model);
recurseOnModel(model);
}
private static void recurseOnTypes(ParameterType type) throws Exception {
if (type instanceof ErrorType) {
throw ((ErrorType)type).getError();
}
if (type instanceof ArrayType) {
recurseOnTypes(((ArrayType)type).getElementType());
} else if (type instanceof HomogeneousObjectType) {
recurseOnModel(((HomogeneousObjectType) type).getSchemaType());
} else if (type instanceof HeterogeneousObjectType) {
for (Map.Entry<String, DescribableModel<?>> entry : ((HeterogeneousObjectType) type).getTypes().entrySet()) {
recurseOnModel(entry.getValue());
}
}
}
private static void recurseOnModel(DescribableModel<?> model) throws Exception {
for (DescribableParameter param : model.getParameters()) {
recurseOnTypes(param.getType());
}
}
private static class DummyOwner extends FlowExecutionOwner {
DummyOwner() {}
@Override public FlowExecution get() throws IOException {
return null;
}
@Override public File getRootDir() throws IOException {
throw new IOException("not implemented");
}
@Override public Queue.Executable getExecutable() throws IOException {
throw new IOException("not implemented");
}
@Override public String getUrl() throws IOException {
throw new IOException("not implemented");
}
@Override public boolean equals(Object o) {
return true;
}
@Override public int hashCode() {
return 0;
}
}
}