/**
* Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* • Apache License, version 2.0
* • Apache Software License, version 1.0
* • GNU Lesser General Public License, version 3
* • Mozilla Public License, versions 1.0, 1.1 and 2.0
* • Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* License version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
package org.n52.wps.test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import java.io.IOException;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Random;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.IOUtils;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import org.rosuda.REngine.Rserve.RConnection;
import org.rosuda.REngine.Rserve.RserveException;
import org.xml.sax.SAXException;
/**
*
* To run this integration tests there has to be RServe running on the localhost and the R repository must be
* enabled in the WPS config.
*
* To start RServe:
*
* <ul>
* <li>start <code>R</code></li>
* <li>in the R console, load the library Rserve: <code>library(Rserve)</code></li>
* <li>in the R console, start Rserve: <code>Rserve()</code></li>
* <li>you should see and output "... Ok, ready to answer queries."</li>
* </ul>
*
* To enable the R process repository:
*
* <ul>
* <li>open your WPSConfiguration file, normally located in /config/wps_config.xml</li>
* <li>Find the "LocalRAlgorithmRepository"</li>
* <li>set the attribute "active" to <code>true</code></li>
* <li>(restart your WPS server)</li>
* </ul>
*/
public class Wps4rIT {
private static String wpsUrl;
@BeforeClass
public static void beforeClass() {
wpsUrl = AllTestsIT.getURL();
// Seems not to work but it would be nice if it does...
// URL resource = WPS4RTester.class
// .getResource("/R/wps_config.xml");
// WPSConfig.forceInitialization(new File(resource.getFile()).getAbsolutePath());
String host = System.getProperty("test.rserve.host", "127.0.0.1");
int port = Integer.parseInt(System.getProperty("test.rserve.port", "6311"));
String user = System.getProperty("test.rserve.user", null);
String password = System.getProperty("test.rserve.pwd", null);
try {
RConnection c = getNewConnection(host, port, user, password);
c.close();
}
catch (RserveException e1) {
Assume.assumeNoException(e1);
}
}
@AfterClass
public static void afterClass() {
// WPSConfig.forceInitialization("src/main/webapp/config/wps_config.xml");
}
private static RConnection getNewConnection(String host, int port, String user, String password) throws RserveException {
RConnection con = new RConnection(host, port);
if (con != null && con.needLogin())
con.login(user, password);
return con;
}
/*
* DN: test disabled, sessionInfo.jsp was deleted, service endpoint must be implemented.
*/
// @Test
public void sessionInfoRetrievedFromWPSWebsite() throws MalformedURLException {
String temp = wpsUrl.substring(0, wpsUrl.lastIndexOf("/"));
URL urlSessionInfo = new URL(temp + "/rsessioninfoendpoint");
try {
String response = GetClient.sendRequest(urlSessionInfo.toExternalForm());
assertThat(response, containsString("R ")); // "R version" fails if using unstable R!
assertThat(response, containsString("Platform:"));
assertThat(response, containsString("attached base packages:"));
}
catch (IOException e) {
String message = "Cannot retrieve the R session info from WPS.";
e.printStackTrace();
throw new AssertionError(message);
}
}
@Test
public void resourcesAreLoadedAndRead() throws IOException,
ParserConfigurationException,
SAXException,
XmlException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestResources.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
assertThat("response is not an exception", response, not(containsString("ExceptionReport")));
assertThat("text resource content could be read",
response,
containsString("dataType=\"xs:string\">This is a dummy txt-file"));
assertThat("image resource is loaded", response, containsString("480"));
assertThat("directories are created", response, containsString("xs:integer\">1</wps:LiteralData>"));
assertThat("subdirectory resources can be read", response, containsString("xs:double\">42.0</wps:LiteralData>"));
assertThat("subsubdirectory resources can be read",
response,
containsString("xs:integer\">17</wps:LiteralData>"));
assertThat("recursive folders all exist", response, containsString("xs:integer\">3</wps:LiteralData>"));
}
@Test
public void responseContainsSessionInfo() throws IOException,
ParserConfigurationException,
SAXException,
XmlException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestResources.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
assertThat(response, not(containsString("ExceptionReport")));
assertThat(response, containsString("attached base packages:"));
assertThat(response, containsString("locale:"));
assertThat(response, containsString("<wps:ComplexData encoding=\"UTF-8\" mimeType=\"text/plain\">"));
assertThat(response, containsString("methods"));
assertThat(response, containsString("base"));
}
@Test
public void responseContainsWarningsSection() throws IOException,
ParserConfigurationException,
SAXException,
XmlException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestWarnings.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
assertThat(response, containsString("<ows:Identifier>warnings</ows:Identifier>"));
assertThat(response, containsString("mimeType=\"text/plain\">warning"));
assertThat(response, containsString("Test warning 1"));
assertThat(response, containsString("Test warning 4: This is the LAST warning"));
}
@Test
public void responseContainsWarningsContent() throws IOException,
ParserConfigurationException,
SAXException,
XmlException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestWarnings.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
assertThat(response, containsString("This is the LAST warning."));
}
@Test
public void decribeProcess() throws IOException, ParserConfigurationException, SAXException {
String identifier = "org.n52.wps.server.r.test.resources";
String response = GetClient.sendRequest(wpsUrl, "Service=WPS&Request=DescribeProcess&Version=1.0.0&Identifier="
+ identifier);
assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
assertThat(response, not(containsString("ExceptionReport")));
assertThat(response, containsString(identifier));
assertThat(response, containsString("<ows:Identifier>" + identifier + "</ows:Identifier>"));
}
@Test
public void capabilitiesContainProcess() throws IOException, ParserConfigurationException, SAXException {
String response = GetClient.sendRequest(wpsUrl, "Service=WPS&Request=GetCapabilities");
assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
assertThat(response, not(containsString("ExceptionReport")));
assertThat(response, containsString("org.n52.wps.server.r.test.resources"));
assertThat(response, containsString("org.n52.wps.server.r.test.calculator"));
assertThat(response, containsString("org.n52.wps.server.r.test.image"));
}
@Test
public void responseTypeIsImage() throws IOException, XmlException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestImage.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
payload = payload.replace("@@@size@@@", "420");
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat(response.split("\n", 1)[0], containsString("PNG"));
assertThat(response, response, not(containsString("ExceptionReport")));
}
@Test
public void calculatorWorksCorrectly() throws IOException, ParserConfigurationException, SAXException, XmlException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestCalculator.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
Random rand = new Random();
int a = rand.nextInt(100);
payload = payload.replace("@@@a@@@", Integer.toString(a));
int b = rand.nextInt(100);
payload = payload.replace("@@@b@@@", Integer.toString(b));
int op = rand.nextInt(3);
String[] ops = new String[] {"+", "-", "*"};
String opString = ops[op];
payload = payload.replace("@@@op@@@", opString);
int result = Integer.MIN_VALUE;
if (opString.equals("+"))
result = a + b;
else if (opString.equals("-"))
result = a - b;
else if (opString.equals("*"))
result = a * b;
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat(AllTestsIT.parseXML(response), is(not(nullValue())));
String expected = "dataType=\"xs:double\">" + Integer.toString(result) + ".0";
assertThat(response, containsString(expected));
}
@Test
public void wpsOffAnnotationWorks() throws XmlException, IOException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestWpsOff.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
String response = PostClient.sendRequest(wpsUrl, payload);
String expected = "<wps:LiteralData dataType=\"xs:integer\">42</wps:LiteralData>";
assertThat("Returned value is sum of provided ones, not sum of values defined in deactivated code.",
response,
containsString(expected));
}
@Test
public void defaultValuesAreLoaded() throws XmlException, IOException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestDefaults.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
String response = PostClient.sendRequest(wpsUrl, payload);
String expected = "<wps:LiteralData dataType=\"xs:integer\">42</wps:LiteralData>";
assertThat("Returned value is sum of defaults, not sum of values defined in deactivated code.",
response,
containsString(expected));
}
@Test
public void exceptionsOnIllegalInputs() throws XmlException, IOException {
String[] illegalCommands = new String[] {
// "\\x0022;", // FIXME Rserve can be crashed with this
// "\u0071\u0075\u0069\u0074\u0028\u0029",
// "<-", "a<-q", // result in WPS parsing error
"="};
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
for (String cmd : illegalCommands) {
String payload = xmlPayload.toString();
payload = payload.replace("@@@data@@@", cmd);
String expected = "illegal input";
PostClient.checkForExceptionReport(wpsUrl, payload, HttpServletResponse.SC_BAD_REQUEST, expected, cmd);
}
}
@Test
public void syntaxErrorOnIllegalInputs() throws XmlException, IOException {
String[] illegalCommands = new String[] {"\"\";quit(\"no\");", "setwd('/root/')", "setwd(\"c:/\")"};
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
for (String cmd : illegalCommands) {
String payload = xmlPayload.toString();
payload = payload.replace("@@@data@@@", cmd);
String expected = "eval failed";
PostClient.checkForExceptionReport(wpsUrl, payload, HttpServletResponse.SC_BAD_REQUEST, expected);
}
}
@Test
public void replacementsOnIllegalInputs() throws XmlException, IOException {
String[] illegalCommands = new String[] {"unlink(getwd())", "q();", "quit()", "%lt;-",
// "system('format hardisk')",
"quit(\\\"no\\\");inputVariable;"};
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
for (String cmd : illegalCommands) {
String payload = xmlPayload.toString();
payload = payload.replace("@@@data@@@", cmd);
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
// assertThat("Response contains an echo of '" + cmd + "'", response, containsString(cmd));
}
}
@Test
public void renamingDoesNotAffectScript() throws XmlException, IOException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestEcho.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String data = UUID.randomUUID().toString();
String payload = xmlPayload.toString();
payload = payload.replace("@@@data@@@", data);
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
assertThat("Response contains an echo of '" + data + "'", response, containsString(data));
}
@Test
public void csvResponseInlineWorks() throws XmlException, IOException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestCSV.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
payload = payload.replace("@@@ref@@@", Boolean.toString(false));
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
assertThat("Response contains mime type", response, containsString("mimeType=\"text/csv\""));
assertThat("Response contains test data names",
response,
containsString("\"cadmium\",\"copper\",\"lead\",\"zinc\""));
}
@Test
public void csvResponseReferenceWorks() throws XmlException, IOException {
URL resource = Wps4rIT.class.getResource("/R/ExecuteTestCSV.xml");
XmlObject xmlPayload = XmlObject.Factory.parse(resource);
String payload = xmlPayload.toString();
payload = payload.replace("@@@ref@@@", Boolean.toString(true));
String response = PostClient.sendRequest(wpsUrl, payload);
assertThat("Response is not an exception", response, not(containsString("ExceptionReport")));
assertThat("Response contains mime type", response, containsString("mimeType=\"text/csv\""));
String urlString = response.substring(response.indexOf("http", response.indexOf("text/csv")));
urlString = urlString.substring(0, urlString.indexOf("\""));
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
String contentType = connection.getContentType();
assertThat("Response content type is correct", contentType, is("text/csv"));
StringWriter writer = new StringWriter();
IOUtils.copy(connection.getInputStream(), writer);
String csv = writer.toString();
assertThat("CSV file contains test data names", csv, containsString("\"cadmium\",\"copper\",\"lead\",\"zinc\""));
}
}