/*
* DeployrUtil.java
*
* Copyright (C) 2010-2016, Microsoft Corporation
*
* This program is licensed to you under the terms of Version 2.0 of the
* Apache License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more details.
*
*/
package com.revo.deployr;
import com.revo.deployr.client.*;
import com.revo.deployr.client.data.RData;
import com.revo.deployr.client.data.RString;
import com.revo.deployr.client.factory.RDataFactory;
import com.revo.deployr.client.params.*;
import org.junit.Ignore;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@Ignore
public class DeployrUtil {
public static final String SAMPLE_CODE = "demo(graphics); png('sampleArtifact.png'); plot(rnorm(10));";
public static final String DEFAULT_PORT = "8050";
// Prefix used to denote file or object created for the
// purpose of later loading from repository on preloadfile*
// or on preloadobject* parameters.
public static final String PLO_PREFIX = "PLO";
public static String encodeString(String s) {
StringBuffer sb = new StringBuffer();
if (s != null) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c <= 32) {
continue;
}
sb.append(c);
}
}
return sb.toString();
}
public static String getUniqueDirectoryName() {
return new StringBuilder().append("Directory-")
.append(UUID.randomUUID()).toString();
}
public static String getUniqueFileName(String extension) {
return new StringBuilder().append("test-")
.append(UUID.randomUUID())
.append(".").append(extension).toString();
}
public static String getUniqueJobName() {
return new StringBuilder().append("job")
.append(UUID.randomUUID())
.append(".").toString();
}
public static String getUniqueRName(String extension) {
String name = new StringBuilder().append("R_")
.append(UUID.randomUUID()).toString();
name = name.replaceAll("-", "_");
return name;
}
public static String getDataFromURL(URL url) {
try {
InputStream is = url.openConnection().getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuffer buff = new StringBuffer("");
String line;
while ((line = reader.readLine()) != null) {
buff.append(line);
}
reader.close();
is.close();
return buff.toString();
} catch (IOException ex) {
Logger.getLogger(DeployrUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public static String getDataFromStream(InputStream is) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuffer buff = new StringBuffer("");
String line;
while ((line = reader.readLine()) != null) {
buff.append(line);
}
reader.close();
is.close();
return buff.toString();
} catch (IOException ex) {
Logger.getLogger(DeployrUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public static RProject createPersistentProject(RUser rUser, String projectName, String projectDesc) {
RProject rProject = null;
try {
rProject = rUser.createProject(projectName, projectDesc);
} catch (Exception ex) {
rProject = null;
}
// Tests using createProjectForExecutionOptions MUST assert
// rProject returned on call is NOT NULL.
return rProject;
}
public static RProject createTemporaryProject(RUser rUser) {
RProject rProject = null;
try {
rProject = rUser.createProject();
} catch (Exception ex) {
rProject = null;
}
// Tests using createProjectForExecutionOptions MUST assert
// rProject returned on call is NOT NULL.
return rProject;
}
/*
* createCreationOptionsProject
*
* Caller is responsible for releasing RProject and List<RRepositoryFile>
* returned on this method. The releaseRepositoryArtifacts
* method is provided to simplify the latter task.
*/
public static Map<String, Object> createCreationOptionsProject(RUser rUser,
int testDataDepth) {
RProject rProject = null;
RProjectFile projectFile = null;
RRepositoryFile storedObject = null;
DirectoryUploadOptions uploadOptions = null;
List<RRepositoryFile> rProjectPreloadArtifacts = new ArrayList<RRepositoryFile>();
try {
rProject = rUser.createProject("ExecutionOptionsProject-Unit-Test",
"Created by unit test helper.");
String pid = rProject.about().id.replaceAll("-", "");
int depth = 0;
while (depth < testDataDepth) {
String objectName = pid + depth;
// Create object in workspace to use on storageOptions.objects.
rProject.executeCode(objectName + " <- rnorm(100);");
// Create object in workspace to store in repository for later use on preloadobject*.
rProject.executeCode(PLO_PREFIX + objectName + " <- " + objectName);
// Create file in directory to use on storageOptions.files.
String fileName = pid + depth;
rProject.executeCode("save(" + objectName + ", file='" + fileName + ".txt', ascii=TRUE)");
// Create binary object file in repository to use on preloadobject*.
storedObject = rProject.storeObject(PLO_PREFIX + objectName,
"Unit Test ExecutionOptionsProject stored object.",
false, null, false, true);
rProjectPreloadArtifacts.add(storedObject);
depth++;
}
rProject.close();
} catch (Exception ex) {
try {
releaseRepositoryArtifacts(rProjectPreloadArtifacts);
} catch (Exception paex) {
}
if (storedObject != null) {
try {
storedObject.delete();
} catch (Exception soex) {
}
}
try {
rProject.delete();
} catch (Exception pdex) {
}
rProject = null;
}
// Tests using createExecutionOptionsProject MUST assert
// rProject returned on call is NOT NULL.
Map map = new HashMap<String, Object>();
map.put("rProject", rProject);
map.put("rProjectPreloadArtifacts", rProjectPreloadArtifacts);
return map;
}
/*
* createCreationOptions
*
* Method returns a fully-populated ProjectCreationOptions that can
* be used to test the majority of parameters supported on the new 7.0
* standard execution model.
*
*/
public static ProjectCreationOptions createCreationOptions(RProject rProject,
int testDataDepth) {
ProjectCreationOptions createOptions = new ProjectCreationOptions();
ProjectPreloadOptions workspaceOptions = new ProjectPreloadOptions();
ProjectPreloadOptions directoryOptions = new ProjectPreloadOptions();
ProjectAdoptionOptions adoptionOptions = new ProjectAdoptionOptions();
String pid = rProject.about().id;
String pidSimplified = pid.replaceAll("-", "");
List<RData> rInputsList = new ArrayList<RData>();
int depth = 0;
while (depth < testDataDepth) {
// createOptions.preloadWorkspace
if (workspaceOptions.author != null) {
workspaceOptions.author = workspaceOptions.author + "," + "testuser";
workspaceOptions.filename = workspaceOptions.filename + "," + PLO_PREFIX + pidSimplified + depth + ".rData";
} else {
workspaceOptions.author = "testuser";
workspaceOptions.filename = PLO_PREFIX + pidSimplified + depth + ".rData";
}
// createOptions.preloadDirectory
if (directoryOptions.author != null) {
directoryOptions.author = directoryOptions.author + "," + "testuser";
directoryOptions.filename = directoryOptions.filename + "," + PLO_PREFIX + pidSimplified + depth + ".rData";
} else {
directoryOptions.author = "testuser";
directoryOptions.filename = PLO_PREFIX + pidSimplified + depth + ".rData";
}
String objectName = "inputOutputTestObject" + depth;
// createOptions.rinputs
rInputsList.add(RDataFactory.createNumeric(objectName, (double) depth));
depth++;
}
createOptions.rinputs = rInputsList;
adoptionOptions.adoptWorkspace = pid;
adoptionOptions.adoptDirectory = pid;
adoptionOptions.adoptPackages = pid;
createOptions.preloadWorkspace = workspaceOptions;
createOptions.preloadDirectory = directoryOptions;
createOptions.adoptionOptions = adoptionOptions;
return createOptions;
}
/*
* createExecutionOptionsProject
*
* Caller is responsible for releasing RProject and List<RRepositoryFile>
* returned on this method. The releaseRepositoryArtifacts
* method is provided to simplify the latter task.
*/
public static Map<String, Object> createExecutionOptionsProject(RUser rUser,
int testDataDepth) {
RProject rProject = null;
RRepositoryFile storedObject = null;
List<RRepositoryFile> rProjectPreloadArtifacts = new ArrayList<RRepositoryFile>();
try {
rProject = rUser.createProject("ExecutionOptionsProject-Unit-Test",
"Created by unit test helper.");
String pid = rProject.about().id.replaceAll("-", "");
int depth = 0;
while (depth < testDataDepth) {
String objectName = pid + depth;
// Create object in workspace to use on storageOptions.objects.
rProject.executeCode(objectName + " <- rnorm(100);");
// Create object in workspace to store in repository for later use on preloadobject*.
rProject.executeCode(PLO_PREFIX + objectName + " <- " + objectName);
// Create file in directory to use on storageOptions.files.
String fileName = pid + depth;
rProject.executeCode("save(" + objectName + ", file='" + fileName + ".txt', ascii=TRUE)");
// Create binary object file in repository to use on preloadobject*.
storedObject = rProject.storeObject(PLO_PREFIX + objectName,
"Unit Test ExecutionOptionsProject stored object.",
false, null, false, true);
rProjectPreloadArtifacts.add(storedObject);
depth++;
}
rProject.close();
} catch (Exception ex) {
try {
releaseRepositoryArtifacts(rProjectPreloadArtifacts);
} catch (Exception paex) {
}
if (storedObject != null) {
try {
storedObject.delete();
} catch (Exception soex) {
}
}
try {
rProject.delete();
} catch (Exception pdex) {
}
rProject = null;
}
// Tests using createExecutionOptionsProject MUST assert
// rProject returned on call is NOT NULL.
Map map = new HashMap<String, Object>();
map.put("rProject", rProject);
map.put("rProjectPreloadArtifacts", rProjectPreloadArtifacts);
return map;
}
/*
* createExecutionOptions
*
* Method returns a fully-populated ProjectExecutionOptions that can
* be used to test the majority of parameters supported on the new 7.0
* standard execution model.
*
* Caller is responsible for releasing any repository-files resulting
* from the execOptions.storageOptions. The releaseRepositoryArtifacts
* method is provided to simplify this task.
*/
public static AnonymousProjectExecutionOptions createExecutionOptions(RProject rProject,
int testDataDepth) {
AnonymousProjectExecutionOptions execOptions =
new AnonymousProjectExecutionOptions();
ProjectPreloadOptions workspaceOptions = new ProjectPreloadOptions();
ProjectPreloadOptions directoryOptions = new ProjectPreloadOptions();
ProjectAdoptionOptions adoptionOptions = new ProjectAdoptionOptions();
ProjectStorageOptions storageOptions = new ProjectStorageOptions();
String pid = rProject.about().id;
String pidSimplified = pid.replaceAll("-", "");
List<RData> rInputsList = new ArrayList<RData>();
List<String> rOutputsList = new ArrayList<String>();
int depth = 0;
while (depth < testDataDepth) {
// execOptions.preloadWorkspace
// Repo object existence depends on createExecutionOptionsProject.
if (workspaceOptions.author != null) {
workspaceOptions.author = workspaceOptions.author + "," + "testuser";
workspaceOptions.filename = workspaceOptions.filename + "," + PLO_PREFIX + pidSimplified + depth + ".rData";
} else {
workspaceOptions.author = "testuser";
workspaceOptions.filename = PLO_PREFIX + pidSimplified + depth + ".rData";
}
// execOptions.preloadDirectory
// Repo file existence depends on createExecutionOptionsProject.
if (directoryOptions.author != null) {
directoryOptions.author = directoryOptions.author + "," + "testuser";
directoryOptions.filename = directoryOptions.filename + "," + PLO_PREFIX + pidSimplified + depth + ".rData";
} else {
directoryOptions.author = "testuser";
directoryOptions.filename = PLO_PREFIX + pidSimplified + depth + ".rData";
}
// execOptions.storageOptions
// Any stored here needs to be handled by test cleanup.
if (storageOptions.files != null) {
storageOptions.files = storageOptions.files + "," + pidSimplified + depth + ".txt";
storageOptions.objects = storageOptions.objects + "," + pidSimplified + depth;
} else {
storageOptions.files = pidSimplified + depth + ".txt";
storageOptions.objects = pidSimplified + depth;
}
String objectName = "inputOutputTestObject" + depth;
// execOptions.rinputs
rInputsList.add(RDataFactory.createNumeric(objectName, (double) depth));
// execOptions.routputs
rOutputsList.add(objectName);
depth++;
}
execOptions.rinputs = rInputsList;
execOptions.routputs = rOutputsList;
adoptionOptions.adoptWorkspace = pid;
adoptionOptions.adoptDirectory = pid;
adoptionOptions.adoptPackages = pid;
storageOptions.workspace = pidSimplified + "Workspace.rData";
execOptions.preloadWorkspace = workspaceOptions;
execOptions.preloadDirectory = directoryOptions;
execOptions.adoptionOptions = adoptionOptions;
execOptions.storageOptions = storageOptions;
return execOptions;
}
/*
* createJobExecutionOptions
*
* Method returns a fully-populated JobExecutionOptions that can
* be used to test the majority of parameters supported on the new 7.0
* standard execution model.
*
* Caller is responsible for releasing any repository-files resulting
* from the jobExecutionOptions.storageOptions. The releaseRepositoryArtifacts
* method is provided to simplify this task.
*/
public static JobExecutionOptions createJobExecutionOptions(RProject rProject,
String priority,
boolean storeNoProject,
int testDataDepth) {
JobExecutionOptions jobExecutionOptions = new JobExecutionOptions();
ProjectPreloadOptions workspaceOptions = new ProjectPreloadOptions();
ProjectPreloadOptions directoryOptions = new ProjectPreloadOptions();
ProjectAdoptionOptions adoptionOptions = new ProjectAdoptionOptions();
ProjectStorageOptions storageOptions = new ProjectStorageOptions();
String pid = rProject.about().id;
String pidSimplified = pid.replaceAll("-", "");
List<RData> rInputsList = new ArrayList<RData>();
List<String> rOutputsList = new ArrayList<String>();
int depth = 0;
while (depth < testDataDepth) {
// jobExecutionOptions.preloadWorkspace
// Repo object existence depends on createJobExecutionOptions.
if (workspaceOptions.author != null) {
workspaceOptions.author = workspaceOptions.author + "," + "testuser";
workspaceOptions.filename = workspaceOptions.filename + "," + PLO_PREFIX + pidSimplified + depth + ".rData";
} else {
workspaceOptions.author = "testuser";
workspaceOptions.filename = PLO_PREFIX + pidSimplified + depth + ".rData";
}
// jobExecutionOptions.preloadDirectory
// Repo file existence depends on createJobExecutionOptions.
if (directoryOptions.author != null) {
directoryOptions.author = directoryOptions.author + "," + "testuser";
directoryOptions.filename = directoryOptions.filename + "," + PLO_PREFIX + pidSimplified + depth + ".rData";
} else {
directoryOptions.author = "testuser";
directoryOptions.filename = PLO_PREFIX + pidSimplified + depth + ".rData";
}
String objectName = "inputOutputTestObject" + depth;
// jobExecutionOptions.rinputs
rInputsList.add(RDataFactory.createNumeric(objectName, (double) depth));
// jobExecutionOptions.routputs
rOutputsList.add(objectName);
depth++;
}
jobExecutionOptions.priority = priority;
jobExecutionOptions.noproject = storeNoProject;
jobExecutionOptions.rinputs = rInputsList;
jobExecutionOptions.routputs = rOutputsList;
adoptionOptions.adoptWorkspace = pid;
adoptionOptions.adoptDirectory = pid;
adoptionOptions.adoptPackages = pid;
jobExecutionOptions.preloadWorkspace = workspaceOptions;
jobExecutionOptions.preloadDirectory = directoryOptions;
jobExecutionOptions.adoptionOptions = adoptionOptions;
jobExecutionOptions.storageOptions = storageOptions;
return jobExecutionOptions;
}
/*
* verifyRepositoryArtifacts
*
* Method parses ProjectStorageOptions to determine names of repository
* artifacts that should exist following an execution using these
* options. It then verifies that the set of repository artifacts
* passed on the second argument match the expected repository artifacts.
*/
public static void verifyRepositoryArtifacts(ProjectStorageOptions storageOptions,
List<RRepositoryFile> repoArtifacts)
throws Exception {
List<String> expectedNames = new ArrayList<String>();
if (storageOptions != null) {
if (storageOptions.files != null) {
String[] files = storageOptions.files.split(",");
for (String file : files) {
expectedNames.add(file.trim());
}
}
if (storageOptions.objects != null) {
String[] objects = storageOptions.objects.split(",");
for (String object : objects) {
expectedNames.add(object.trim() + ".rData");
}
}
if (storageOptions.workspace != null) {
expectedNames.add(storageOptions.workspace.trim());
}
}
verifyRepositoryArtifacts(expectedNames, repoArtifacts);
}
/*
* verifyRepositoryArtifacts
*
* Method parses ProjectStorageOptions to determine names of repository
* artifacts that should exist following an execution using these
* options. It then verifies that the set of repository artifacts
* passed on the second argument match the expected repository artifacts.
*/
public static void verifyRepositoryArtifacts(ProjectCreationOptions createOptions,
List<RRepositoryFile> repoArtifacts)
throws Exception {
List<String> expectedNames = new ArrayList<String>();
if (createOptions != null) {
if (createOptions.preloadDirectory != null) {
String[] files = createOptions.preloadDirectory.filename.split(",");
for (String file : files) {
expectedNames.add(file.trim());
}
}
if (createOptions.preloadWorkspace != null) {
String[] objects = createOptions.preloadWorkspace.filename.split(",");
for (String object : objects) {
expectedNames.add(object.trim());
}
}
}
verifyRepositoryArtifacts(expectedNames, repoArtifacts);
}
/*
* verifyRepositoryArtifacts
*
* Method verifies that the set of expected repository artifact names passed
* on the first argument match the actual repository artifacts passed on
* the second argument.
*/
public static void verifyRepositoryArtifacts(List<String> expectedNames,
List<RRepositoryFile> repoArtifacts)
throws Exception {
if (expectedNames != null && repoArtifacts != null) {
HashSet<String> artifactNames = new HashSet<String>();
for (RRepositoryFile repoFile : repoArtifacts) {
try {
artifactNames.add(repoFile.about().filename);
} catch (Exception ex) {
throw new Exception("repoFile.about failed.");
}
}
for (String expectedName : expectedNames) {
if (!artifactNames.contains(expectedName)) {
throw new Exception("Repository artifact " + expectedName + " not found.");
}
}
}
}
/*
* releaseRepositoryArtifacts
*
* Method attempts to delete each of the Repository files passed
* on the first argument. If an exeception occurs on any deletion
* that failure is reported by throwing an Exception.
*/
public static void releaseRepositoryArtifacts(List<RRepositoryFile> repoArtifacts)
throws Exception {
Exception failure = null;
if (repoArtifacts != null) {
for (RRepositoryFile repoFile : repoArtifacts) {
String filename = repoFile.about().filename;
try {
repoFile.delete();
} catch (Exception ex) {
// Capture failure but allow release loop to continue.
failure = new Exception("repoFile.delete failed: " + filename);
}
}
}
if (failure != null)
throw failure;
}
/*
* createTemporaryRScript
*
* Method returns an instance of new RRepositoryFile representing an
* R script in the repository. Tests can use this method to create
* a temporary R script to use when testing R script execution, therefore
* removing any external dependencies.
*
* The scripts returned by this method consistently generate:
* Execution results: 11
* Execution artifacts: 1
* These numbers can be asserted by tests using this method.
*/
public static RRepositoryFile createTemporaryRScript(RUser rUser,
boolean published) throws Exception {
RepoUploadOptions uploadOptions = new RepoUploadOptions();
uploadOptions.filename = getUniqueFileName("R");
uploadOptions.descr = uploadOptions.filename + " description.";
uploadOptions.published = published;
RRepositoryFile repositoryFile = null;
try {
repositoryFile = rUser.writeFile(SAMPLE_CODE, uploadOptions);
} catch (Exception ex) {
throw new Exception("rUser.writeFile failed: " + ex.getMessage());
}
return repositoryFile;
}
/*
* createTemporaryRScriptOnDisk
*
* Method returns a full path to an RScript. Tests can use this method to create
* a temporary R script to use when testing R script execution, therefore
* removing any external dependencies.
*
* The scripts returned by this method consistently generate:
* Execution results: 11
* Execution artifacts: 1
* These numbers can be asserted by tests using this method.
*/
public static String createTemporaryRScriptOnDisk(RProject rProject) throws Exception {
DirectoryUploadOptions uploadOptions = new DirectoryUploadOptions();
ProjectExecutionOptions options = new ProjectExecutionOptions();
RProjectExecution pathResult = null;
RString rPathObject = null;
String scriptPath = "";
ArrayList rList = new ArrayList();
String code = "path<-getwd()";
uploadOptions.filename = getUniqueFileName("R");
uploadOptions.descr = uploadOptions.filename + " description.";
RProjectFile projectFile = null;
try {
projectFile = rProject.writeFile(SAMPLE_CODE, uploadOptions);
} catch (Exception ex) {
throw new Exception("rProject.writeFile failed: " + ex.getMessage());
}
// get path
try {
rList.add("path");
options.routputs = rList;
pathResult = rProject.executeCode(code, options);
rPathObject = (RString) pathResult.about().workspaceObjects.get(0);
scriptPath = rPathObject.getValue();
} catch (Exception ex) {
throw new Exception("rProject.executeCode failed: " + ex.getMessage());
}
return scriptPath + File.separator + uploadOptions.filename;
}
}