Created
September 9, 2018 15:59
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------- | |
// *** Deep Group Hierarchies | |
// *** with functionality fragments | |
//------------------------------------- | |
// Breaklet Config | |
//------------------------------------- | |
def breakForDurationInSec = 60 | |
def componentsOnTestPage = 10 | |
def funcFragmentsPerIteration = 5 | |
def subFuncFragments = 5 | |
def contentBasePath = "/content/test" | |
def acToolDir = "/apps/test-deep-group-hierarchies" | |
def sessionUser = 'admin' | |
def sessionPassword = 'admin' | |
def breakletUser = 'breakletuser' | |
def breakletPassword = 'breakletpassword' | |
def breakletUser2 = 'breakletuser2' | |
def breakletPassword2 = 'breakletpassword' | |
def noPaddingWidth = 4 | |
//------------------------------------- | |
// Breaklet | |
//------------------------------------- | |
new Breaklet(this, breakForDurationInSec, sessionUser, sessionPassword).run { breaklet, currentIt, itSession -> | |
def contentBasePathJcrContent = contentBasePath + "/jcr:content" | |
def contentBasePathJcrContentInvisibleSubnode = contentBasePathJcrContent + "/compinvisible" | |
def acToolUserConfigs = [] | |
def acToolGroupConfigs = [] | |
def acToolAceConfigs = [] | |
acToolGroupConfigs.add(""" | |
| - breaklet-deny: | |
| - isMemberOf: | |
| path: /home/groups/breakletfragments | |
""".stripMargin()) | |
acToolGroupConfigs.add(""" | |
| - breaklet-allow: | |
| - isMemberOf: | |
| path: /home/groups/breakletfragments | |
""".stripMargin()) | |
acToolAceConfigs.add(""" | |
| | |
| - breaklet-deny: | |
| | |
| - path: ${contentBasePath} | |
| permission: deny | |
| privileges: jcr:all | |
| | |
| | |
""".stripMargin()) | |
acToolAceConfigs.add(""" | |
| | |
| - breaklet-allow: | |
| | |
| - path: ${contentBasePath} | |
| permission: allow | |
| privileges: jcr:read | |
| | |
""".stripMargin()) | |
def userIsMemberOfSeesAll = [] | |
def userIsMemberOfSees30Percent = [] | |
def pageContentNode = JcrUtils.getOrCreateByPath(contentBasePathJcrContent, "cq:Page", "cq:PageContent", itSession, false) | |
pageContentNode.setProperty("sling:resourceType", "wcm/foundation/components/responsivegrid") | |
for(int componentNo=1; componentNo<= componentsOnTestPage; componentNo++) { | |
String compNoPadded = StringUtils.leftPad(componentNo+"", noPaddingWidth, "0") | |
def contentComponentPath = contentBasePathJcrContent + "/comp"+compNoPadded | |
def contentComponentNode = JcrUtils.getOrCreateByPath(contentComponentPath, "nt:unstructured", "nt:unstructured", itSession, false) | |
contentComponentNode.setProperty("sling:resourceType", "wcm/foundation/components/responsivegrid") | |
def funcFragmentNo = funcFragmentsPerIteration * currentIt | |
for(int funcFragmentItNo = 1; funcFragmentItNo <=funcFragmentNo; funcFragmentItNo++) { | |
String funcFragmentNoPadded = StringUtils.leftPad(funcFragmentItNo+"", noPaddingWidth, "0") | |
def contentPathFragment = contentComponentPath + "/frag" + funcFragmentNoPadded | |
def funcFragmentNode = JcrUtils.getOrCreateByPath(contentPathFragment, "nt:unstructured", "nt:unstructured", itSession, false) | |
funcFragmentNode.setProperty("sling:resourceType", "wcm/foundation/components/responsivegrid") | |
def contentPathFragmentBase = contentPathFragment + "/base" | |
def funcFragmentNodeBase = JcrUtils.getOrCreateByPath(contentPathFragmentBase, "nt:unstructured", "nt:unstructured", itSession, false) | |
funcFragmentNodeBase.setProperty("sling:resourceType", "wcm/foundation/components/text") | |
def funcFragmentDescription = "Base Func Fragment Comp "+componentNo + " Fragment "+ funcFragmentItNo | |
funcFragmentNodeBase.setProperty("text", funcFragmentDescription) | |
String funcFragmentGroupName = "g-comp"+compNoPadded+"-funcfragment"+funcFragmentNoPadded | |
def subFragmentNames = [] | |
def subFuncFragmentNo = subFuncFragments | |
for(int subFuncFragmentItNo = 1; subFuncFragmentItNo <=subFuncFragmentNo; subFuncFragmentItNo++) { | |
String subFuncFragmentNoPadded = StringUtils.leftPad(subFuncFragmentItNo+"", noPaddingWidth, "0") | |
def contentPathSubFragment = contentPathFragment + "/sub" + subFuncFragmentNoPadded | |
def subFuncFragmentNode = JcrUtils.getOrCreateByPath(contentPathSubFragment, "nt:unstructured", "nt:unstructured", itSession, false) | |
subFuncFragmentNode.setProperty("sling:resourceType", "wcm/foundation/components/text") | |
def subFuncFragmentDescription = "--- " + funcFragmentDescription+" - Sub "+ subFuncFragmentItNo | |
subFuncFragmentNode.setProperty("text", subFuncFragmentDescription) | |
String subFuncFragmentGroupName = "g-comp"+compNoPadded+"-funcfragment"+funcFragmentNoPadded + "-sub"+subFuncFragmentNoPadded | |
subFragmentNames.add(subFuncFragmentGroupName) | |
acToolGroupConfigs.add(""" | |
| | |
| - ${subFuncFragmentGroupName}: | |
| - isMemberOf: | |
| virtual: false | |
| path: /home/groups/breakletfragments/c${compNoPadded}-f${funcFragmentNoPadded} | |
| | |
""".stripMargin()) | |
acToolAceConfigs.add(""" | |
| | |
| - breaklet-deny: | |
| | |
| - path: ${contentPathSubFragment} | |
| permission: deny | |
| privileges: jcr:all | |
| | |
""".stripMargin()) | |
acToolAceConfigs.add(""" | |
| | |
| - ${subFuncFragmentGroupName}: | |
| | |
| - path: ${contentPathSubFragment} | |
| permission: allow | |
| privileges: jcr:read | |
| | |
""".stripMargin()) | |
} | |
String isMemberOf = StringUtils.join(subFragmentNames,",") | |
acToolGroupConfigs.add(""" | |
| | |
| - ${funcFragmentGroupName}: | |
| - isMemberOf: ${isMemberOf} | |
| path: /home/groups/breakletfragments/c${compNoPadded}-f${funcFragmentNoPadded} | |
| | |
""".stripMargin()) | |
acToolAceConfigs.add(""" | |
| | |
| - breaklet-deny: | |
| | |
| - path: ${contentPathFragmentBase} | |
| permission: deny | |
| privileges: jcr:all | |
| | |
""".stripMargin()) | |
acToolAceConfigs.add(""" | |
| | |
| - ${funcFragmentGroupName}: | |
| | |
| - path: ${contentPathFragmentBase} | |
| permission: allow | |
| privileges: jcr:read | |
| | |
""".stripMargin()) | |
userIsMemberOfSeesAll.add(funcFragmentGroupName) | |
if(funcFragmentItNo % 10 == 3 ) { | |
userIsMemberOfSees30Percent.add(funcFragmentGroupName) | |
} | |
} | |
} | |
def userIsMemberOfSeesAllStr = StringUtils.join(userIsMemberOfSeesAll, ",") | |
acToolUserConfigs.add(""" | |
| - ${breakletUser}: | |
| - isMemberOf: breaklet-deny,breaklet-allow,${userIsMemberOfSeesAllStr} | |
| password: ${breakletPassword} | |
| path: /home/users/breaklet | |
""".stripMargin()) | |
def userIsMemberOfSees30PercentStr = StringUtils.join(userIsMemberOfSees30Percent, ",") | |
acToolUserConfigs.add(""" | |
| - ${breakletUser2}: | |
| - isMemberOf: breaklet-deny,breaklet-allow,${userIsMemberOfSees30PercentStr} | |
| password: ${breakletPassword2} | |
| path: /home/users/breaklet | |
""".stripMargin()) | |
def acToolYaml = "- user_config:\n\n" + StringUtils.join(acToolUserConfigs, "\n") + | |
"\n\n- group_config:\n\n" + StringUtils.join(acToolGroupConfigs, "\n") + | |
"\n\n- ace_config:\n\n" + StringUtils.join(acToolAceConfigs, "\n") | |
def acToolDirNode = JcrUtils.getOrCreateByPath(acToolDir, "nt:folder", "nt:folder", itSession, false) | |
JcrUtils.putFile(acToolDirNode, "breaklet.yaml", "text/yaml", new ByteArrayInputStream(acToolYaml.getBytes(StandardCharsets.UTF_8))) | |
itSession.save() | |
breaklet.logOpFinished("Setup content for "+funcFragmentsPerIteration+ " more fragments (with "+subFuncFragments+" sub fragments each)") | |
getService("biz.netcentric.cq.tools.actool.api.AcInstallationService").apply(acToolDir, [contentBasePath] as String[]) | |
breaklet.logOpFinished("Applied AC Tool", "", null, true) | |
def requestPath = contentBasePath+".html?wcmmode=disabled" | |
def responseText = breaklet.performRequest(requestPath, breakletUser, breakletPassword) | |
breaklet.logOpFinished("User with all fragments", responseText.length() + " chars received") | |
responseText = breaklet.performRequest(requestPath, breakletUser2, breakletPassword2) | |
breaklet.logOpFinished("User with 30% fragments", responseText.length() + " chars received") | |
responseText = breaklet.performRequest(requestPath, sessionUser, sessionPassword) | |
breaklet.logOpFinished("Admin request", responseText.length() + " chars received") | |
acToolYaml = acToolYaml.replaceAll('virtual: false', 'virtual: true') | |
JcrUtils.putFile(acToolDirNode, "breaklet.yaml", "text/yaml", new ByteArrayInputStream(acToolYaml.getBytes(StandardCharsets.UTF_8))) | |
itSession.save() | |
breaklet.logOpFinished("Setup AC yaml with virtual groups") | |
getService("biz.netcentric.cq.tools.actool.api.AcInstallationService").apply(acToolDir, [contentBasePath] as String[]) | |
breaklet.logOpFinished("Applied AC Tool", "", null, true) | |
responseText = breaklet.performRequest(requestPath, breakletUser, breakletPassword) | |
breaklet.logOpFinished("User with all fragments (virtual groups)", responseText.length() + " chars received") | |
responseText = breaklet.performRequest(requestPath, breakletUser2, breakletPassword2) | |
breaklet.logOpFinished("User with 30% fragments (virtual groups)", responseText.length() + " chars received") | |
responseText = breaklet.performRequest(requestPath, sessionUser, sessionPassword) | |
breaklet.logOpFinished("Admin request (virtual groups)", responseText.length() + " chars received") | |
} | |
// DO NOT CHANGE BEYOND THIS POINT - master is https://gist.github.com/ghenzler/58ce57794ba9b5a60f2a2e60160be747 here | |
// -------------------------------------------------------------------- | |
// Breaklet Base Utilities (to be copied at bottom of other breaklets) | |
// -------------------------------------------------------------------- | |
import org.apache.jackrabbit.commons.JcrUtils | |
import org.apache.commons.lang3.StringUtils | |
import java.util.concurrent.ThreadLocalRandom | |
import javax.jcr.query.* | |
import org.apache.sling.jcr.api.* | |
import org.apache.sling.api.resource.observation.* | |
import java.util.Properties | |
import java.io.ByteArrayInputStream | |
import java.nio.charset.StandardCharsets | |
import org.osgi.service.event.EventConstants | |
import org.osgi.service.event.EventHandler | |
import org.osgi.service.event.Event | |
import org.apache.commons.io.IOUtils | |
import org.apache.sling.hc.util.FormattingResultLog | |
class BreakletBrokeException extends RuntimeException { | |
String opName | |
String timeSpent | |
BreakletBrokeException(opName, timeSpent) { | |
this.opName = opName | |
this.timeSpent = timeSpent | |
} | |
} | |
class BreakletPanicException extends RuntimeException { } | |
class Breaklet { | |
int iterations | |
int breakForDurationInSec | |
String user | |
String password | |
long lastTimestamp = System.currentTimeMillis() | |
def script | |
long currentIteration | |
String currentItLogMarker | |
Map<String,List> timings = new LinkedHashMap<String,List>() | |
Browser browser | |
def resourceChangeListener = null | |
def resourceChangeListenerReg = null | |
def resourceChangeListenerPath = null | |
def resourceChangeEventHandler = null | |
def resourceChangeEventHandlerReg = null | |
def resourceChangeEventHandlerPath = null | |
Breaklet(def script, int breakForDurationInSec, String user=null, String password=null, int iterations=Integer.MAX_VALUE) { | |
this.script = script | |
this.breakForDurationInSec = breakForDurationInSec | |
this.user = user | |
this.password = password | |
this.iterations = iterations | |
this.browser = new Browser(script, breakForDurationInSec, user, password) | |
} | |
def logOpFinished(String opName, String opDescription="", def timeSpentMillis=null, def neverBreak=false) { | |
script.logMsg(StringUtils.leftPad(currentItLogMarker, 7)+" " + StringUtils.rightPad(getTimeSpent(opName, timeSpentMillis as Long, neverBreak) + ":", 8) + " "+opName + " "+opDescription) | |
currentItLogMarker = "" | |
} | |
def getTimeSpent(String opName, Long timeSpentMillis=null, def neverBreak=false) { | |
long delta = timeSpentMillis ?: System.currentTimeMillis()-lastTimestamp | |
def timingSeriesForOp = timings.get(opName) | |
if(timingSeriesForOp==null) { timings.put(opName, (timingSeriesForOp = new ArrayList<Long>())) } | |
while(timingSeriesForOp.size() < currentIteration-1) { timingSeriesForOp.add(timingSeriesForOp.size(), '') } | |
timingSeriesForOp.add((int)currentIteration-1, delta) | |
String timeSpent = FormattingResultLog.msHumanReadable(delta) | |
if((delta/1000) > breakForDurationInSec && !neverBreak) { | |
throw new BreakletBrokeException(opName, timeSpent) | |
} | |
lastTimestamp = System.currentTimeMillis() | |
return timeSpent | |
} | |
def run(toRun) { | |
File currentWorkingDir = new File(System.getProperty('user.dir')) | |
File panicFile = new File(currentWorkingDir, 'panic') | |
try { | |
(1..iterations).each { itNo -> | |
currentItLogMarker = itNo+":" | |
def sessionForIteration = null | |
try { | |
if(StringUtils.isNotBlank(user)) { | |
sessionForIteration = getSessionFor(user, password) | |
} | |
currentIteration = itNo | |
def sessionForIt = sessionForIteration ?: script.session | |
toRun(this, currentIteration, sessionForIt) | |
if(panicFile.exists()) { | |
panicFile.renameTo(new File(currentWorkingDir, 'no-panic')) | |
throw new BreakletPanicException() | |
} | |
} finally { | |
if(sessionForIteration!=null && sessionForIteration.isLive()) { | |
sessionForIteration.logout() | |
} | |
} | |
} | |
script.logMsg("\n\nBreaklet survived - "+iterations+" iterations finished without exceeding the threshold\n") | |
} catch(BreakletBrokeException e) { | |
script.logMsg("\n\nBreaklet broke at iteration "+currentIteration+": Operation '"+e.opName+"' "+e.timeSpent + " >= "+breakForDurationInSec + "sec\n") | |
} catch(BreakletPanicException e) { | |
script.logMsg("\nBreaklet broke due to panic after iteration "+currentIteration+"\n") | |
} catch(Exception e) { | |
script.logMsg("\n\nBreaklet broke at iteration "+currentIteration+" with unexpected exception: "+e) | |
script.log.info("Exception:"+e, e) | |
} finally { | |
String result = "" | |
timings.keySet().each { key -> | |
result += ""+key+","+ StringUtils.join(timings.get(key),',') + "\n" | |
} | |
new File(currentWorkingDir, 'breaklet-result-'+new Date().getTime()+'.csv').text = result | |
if(resourceChangeListenerReg) { | |
resourceChangeListenerReg.unregister() | |
} | |
if(resourceChangeEventHandlerReg) { | |
resourceChangeEventHandlerReg.unregister() | |
} | |
} | |
} | |
def runInBrowser(String puppeteerScript) { | |
return browser.run(puppeteerScript) | |
} | |
def getSessionFor(sessionUser, sessionPassword) { | |
def repository = script.session.getRepository() | |
def session = repository.login(new SimpleCredentials(sessionUser, sessionPassword.toCharArray())); | |
return session | |
} | |
def registerResourceChangeListener(paths) { | |
resourceChangeListenerPath = paths | |
resourceChangeListener = new ResourceChangeListener() { | |
int changesReceived = 0 | |
int callsReceived = 0 | |
def allChanges = [] | |
public void onChange(List<ResourceChange> changes) { | |
changesReceived += changes.size() | |
callsReceived++ | |
allChanges.addAll(changes) | |
} | |
public void reset() { | |
changesReceived = 0 | |
callsReceived = 0 | |
allChanges = [] | |
} | |
} | |
resourceChangeListenerReg = script.bundleContext.registerService("org.apache.sling.api.resource.observation.ResourceChangeListener", resourceChangeListener, new Hashtable([(ResourceChangeListener.PATHS): paths])) | |
return resourceChangeListener | |
} | |
def registerResourceChangeEventHandler(path) { | |
resourceChangeEventHandlerPath = path | |
resourceChangeEventHandler = new EventHandler() { | |
int eventsReceived = 0 | |
def allEvents = [] | |
public void handleEvent(final Event event) { | |
eventsReceived++ | |
allEvents.addAll(event) | |
} | |
public void reset() { | |
eventsReceived = 0 | |
allEvents = [] | |
} | |
} | |
resourceChangeEventHandlerReg = script.bundleContext.registerService("org.osgi.service.event.EventHandler", resourceChangeEventHandler, | |
new Hashtable([ | |
(EventConstants.EVENT_TOPIC): "org/apache/sling/api/resource/Resource/*", | |
(EventConstants.EVENT_FILTER): "("+SlingConstants.PROPERTY_PATH+"="+path+"*)", | |
])) | |
return resourceChangeEventHandler | |
} | |
def waitForResourceChangedEvents(long minimumExpectedChanges, long maxWaitInSec=300, String opName=null) { | |
boolean timeout = false | |
int waitCounter = 0 | |
while((resourceChangeListener && resourceChangeListener.changesReceived < minimumExpectedChanges) | |
|| (resourceChangeEventHandler && resourceChangeEventHandler.eventsReceived < minimumExpectedChanges) | |
) { | |
if((lastTimestamp + maxWaitInSec*1000) < System.currentTimeMillis()) { | |
timeout = true | |
break | |
} | |
Thread.sleep(50) | |
} | |
String resourceChangeListenerResult = resourceChangeListener ? " changesReceived="+resourceChangeListener.changesReceived+" (in "+resourceChangeListener.callsReceived +" calls) " : "" | |
String resourceChangeEventHandlerResult = resourceChangeEventHandler ? " eventsReceived="+resourceChangeEventHandler.eventsReceived : "" | |
opName = opName ?: "Wait for resource changed events ("+(resourceChangeListenerPath?:resourceChangeEventHandlerPath)+")" | |
logOpFinished(opName, (timeout?'Timed out after '+maxWaitInSec+'sec ':'') + resourceChangeListenerResult + resourceChangeEventHandlerResult) | |
resourceChangeListener?.reset() | |
resourceChangeEventHandler?.reset() | |
} | |
def performRequest(String requestPath, String reqUser=user, String reqPassword=password) { | |
def con = new URL(script.getBaseUrl()+requestPath).openConnection(); | |
String encoded = Base64.getEncoder().encodeToString((reqUser+":"+reqPassword).getBytes(StandardCharsets.UTF_8)); | |
con.setRequestProperty("Authorization", "Basic "+encoded); | |
def result = IOUtils.toString(con.getInputStream()); | |
return result | |
} | |
} | |
class Browser { | |
// config | |
def script | |
String user | |
String password | |
int breakForDurationInSec | |
// state | |
File browserDir | |
String testJsTemplate | |
Properties resultProps | |
Browser(def script, int breakForDurationInSec, String user, String password) { | |
this.script = script | |
this.breakForDurationInSec = breakForDurationInSec | |
this.user = user | |
this.password = password | |
} | |
def init() { | |
browserDir = new File(System.getProperty('user.dir')+'/puppeteer') | |
if(!browserDir.exists()) { | |
script.logMsg("init "+ browserDir) | |
browserDir.mkdir() | |
new File(browserDir, 'package.json').text = """ | |
{ | |
"name": "puppeteer-breaklet", | |
"version": "1.0.0", | |
"scripts": { | |
"test": "node test.js" | |
}, | |
"dependencies": { | |
"puppeteer": "1.7.0" | |
} | |
} | |
""".stripIndent() | |
def result = script.exec("npm install", browserDir) | |
script.logMsg("npm install result: "+ result) | |
} | |
testJsTemplate = ''' | |
const puppeteer = require('puppeteer'); | |
let baseUrl = process.argv[2] | |
let user = process.argv[3] | |
let password = process.argv[4] | |
let startTime = process.argv[5] | |
let defaultTimeout = 120*1000 | |
let screenshotCreated = false | |
let loginPage = "/libs/granite/core/content/login.html" | |
function sleep(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
function getCrxdeXPath(nodeName) { | |
return "//*[@id='repository']//a[span/text()='"+nodeName+"']" | |
} | |
async function waitForXPath(page, xpath) { | |
await page.waitForXPath(xpath, { timeout: defaultTimeout}) | |
let nodeElements = await page.$x(xpath) | |
return nodeElements[0] | |
} | |
async function waitForCrxdeNodeWithName(page, nodeName) { | |
let nodeElXPath = getCrxdeXPath(nodeName) | |
return await waitForXPath(page, nodeElXPath) | |
} | |
async function waitForAndClick(page, selector, retry=true) { | |
await page.waitForSelector(selector, { timeout: defaultTimeout, visible: true}) | |
if (await page.$(selector) === null) { | |
throw new Error("Could not find " + selector + " even after waiting for " + defaultTimeout); | |
} | |
try { | |
await page.click(selector) | |
} catch(err) { | |
if(retry) { | |
console.log("Error " +err+" while clicking on selector "+selector+", retrying...") | |
await sleep(200) | |
waitForAndClick(page, selector, false); | |
} else { | |
throw err | |
} | |
} | |
} | |
async function clickOnIconOfCrxdeNode(page, nodeHandle) { | |
await page.evaluate(node => node.previousSibling.previousSibling.click(), nodeHandle); | |
} | |
async function createScreenshot(page, name=null) { | |
if(page) { | |
await page.screenshot({path: 'run-'+startMillis+(name ? '-'+name : '')+'.png'}); | |
screenshotCreated = true | |
} | |
} | |
async function run() { | |
var resultProps = {}; | |
const browser = await puppeteer.launch({headless: true, dumpio: true, args: ['--window-size=1440,900', "--no-sandbox"]}); | |
let page = null | |
try { | |
//const context = await browser.createIncognitoBrowserContext(); | |
page = await browser.newPage(); // context or browser | |
await page.setViewport({width: 1440, height: 900}); | |
await page.goto(baseUrl + loginPage); | |
await page.$eval('#username', (el, pUsername) => el.value = pUsername, user); | |
await page.$eval('#password', (el, pPassword) => el.value = pPassword, password); | |
await sleep(500); | |
await page.$eval('#login', form => form.submit()); | |
await page.waitForNavigation({ waitUntil: 'networkidle0'}) | |
if(new RegExp('.*?'+loginPage+'.*').test(await page.url())) { | |
throw new Error("Could not login!"); | |
} else { | |
console.log("Logged in user "+user) | |
} | |
startMillis = new Date().getTime() | |
console.log("Starting user script at start milliseconds =", startMillis) | |
%SCRIPT% | |
} catch(err) { | |
console.log("Error during execution: ", err) | |
} finally { | |
if(!screenshotCreated) { | |
await createScreenshot(page) | |
} | |
browser.close(); | |
let delta = new Date().getTime()-startMillis | |
console.log("----------------") | |
for (let key in resultProps) { | |
console.log(key+"="+resultProps[key]) | |
} | |
console.log("browserDurationInMillis="+delta) | |
} | |
} | |
run(); | |
'''.stripIndent() | |
} | |
def run(String scriptToRun) { | |
init() | |
new File(browserDir, 'test.js').text = testJsTemplate.replace('%SCRIPT%', scriptToRun) | |
def baseUrl = script.getBaseUrl() | |
def startTime = System.currentTimeMillis() | |
def cmd = "npm test $baseUrl $user $password $startTime" | |
def scriptResult = script.exec(cmd, browserDir, breakForDurationInSec) | |
new File(browserDir, 'run-'+new Date().getTime()+".log").text = scriptResult | |
if(scriptResult.contains("Error during execution:")) { | |
throw new IllegalStateException("Browser execution failed") | |
} | |
def propsString = StringUtils.substringAfter(scriptResult, "----------------\n") | |
resultProps = new Properties() | |
resultProps.load(new ByteArrayInputStream(propsString.getBytes(StandardCharsets.UTF_8))) | |
long browserDurationInMillis = resultProps.containsKey('breakForDurationInSec') ? Long.parseLong(resultProps.getProperty('breakForDurationInSec')) : (System.currentTimeMillis()-startTime) | |
return browserDurationInMillis | |
} | |
} | |
def logMsg(String msg) { | |
log.info(msg) | |
println(msg) | |
} | |
def getBaseUrl() { | |
def aemPort = System.getProperty('sun.java.command').replaceFirst(".*?-p ([0-9]+) .*", "\$1") | |
return "http://localhost:"+aemPort | |
} | |
import org.apache.commons.exec.CommandLine | |
import org.apache.commons.exec.DefaultExecutor | |
import org.apache.commons.exec.PumpStreamHandler | |
import org.apache.commons.exec.ExecuteWatchdog | |
def exec(String command, File dir, int breakForDurationInSec=600) { | |
CommandLine cmdLine = CommandLine.parse(command); | |
DefaultExecutor executor = new DefaultExecutor(); | |
executor.setWorkingDirectory(dir) | |
ExecuteWatchdog watchdog = new ExecuteWatchdog(breakForDurationInSec * 1000); | |
executor.setWatchdog(watchdog); | |
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream); | |
executor.setStreamHandler(streamHandler); | |
executor.setExitValues([0,1,143] as int[]) | |
int exitValue = executor.execute(cmdLine); | |
String scriptOutput = outputStream.toString() | |
if(exitValue == 1) { | |
logMsg("Script '"+command+"' returned exit code 1, output:\n "+ scriptOutput) | |
} | |
return scriptOutput | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment