Skip to content

Commit

Permalink
pongasoft#80: added glu script unit test framework
Browse files Browse the repository at this point in the history
* added Shell.httpPost
* added unit test framework for glu scripts
* added unit test for JettyGluScript to demonstrate/validate test framework
  • Loading branch information
ypujante committed Jul 24, 2011
1 parent 3b5600e commit 66ad3a2
Show file tree
Hide file tree
Showing 12 changed files with 703 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,20 @@ def interface Shell
*/
Map httpHead(location)

/**
* Issue a 'POST' request. The location should be an http or https link. The request will be
* made with <code>application/x-www-form-urlencoded</code> content type.
*
* @param location
* @param parameters the parameters of the post as map of key value pairs (value can be a single
* value or a collection of values)
* @return a map with the following entries:
* responseCode: 200, 404... {@link java.net.HttpURLConnection#getResponseCode()}
* responseMessage: message {@link java.net.HttpURLConnection#getResponseMessage()}
* headers: representing all the headers {@link java.net.URLConnection#getHeaderFields()}
*/
Map httpPost(location, Map parameters)

/**
* Similarly to the unix grep command, checks the location one line at a time and returns
* all the lines which matches the pattern.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import org.linkedin.util.clock.Timespan
import org.linkedin.glu.agent.api.TimeOutException
import org.linkedin.glu.utils.tags.Taggeable
import org.linkedin.glu.utils.tags.TaggeableTreeSetImpl
import org.linkedin.glu.agent.impl.script.ScriptNode
import java.util.concurrent.ExecutionException

/**
* The main implementation of the agent
Expand Down Expand Up @@ -330,6 +332,10 @@ def class AgentImpl implements Agent, AgentContext, Shutdownable
}
return res
}
catch(ExecutionException e)
{
throw e.cause
}
catch (TimeoutException e)
{
if(log.isDebugEnabled())
Expand Down Expand Up @@ -597,4 +603,12 @@ def class AgentImpl implements Agent, AgentContext, Shutdownable
throw new AgentException('unexpected exception', th)
}
}

/**
* @return a script previously installed (or <code>null</code> if not found)
*/
ScriptNode findScript(mountPoint)
{
return _scriptManager.findScript(mountPoint)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import org.apache.tools.ant.filters.ReplaceTokens.Token
import org.linkedin.util.io.resource.Resource
import javax.management.Attribute
import org.linkedin.glu.agent.impl.storage.AgentProperties
import org.linkedin.util.url.QueryBuilder

/**
* contains the utility methods for the shell
Expand Down Expand Up @@ -414,6 +415,66 @@ def class ShellImpl implements Shell
return res
}

/**
* Issue a 'POST' request. The location should be an http or https link. The request will be
* made with <code>application/x-www-form-urlencoded</code> content type.
*
* @param location
* @param parameters the parameters of the post as map of key value pairs (value can be a single
* value or a collection of values)
* @return a map with the following entries:
* responseCode: 200, 404... {@link java.net.HttpURLConnection#getResponseCode()}
* responseMessage: message {@link java.net.HttpURLConnection#getResponseMessage()}
* headers: representing all the headers {@link java.net.URLConnection#getHeaderFields()}
*/
Map httpPost(location, Map parameters)
{
Map res = [:]

URI uri = GroovyNetUtils.toURI(location)

URL url = uri.toURL()
URLConnection cx = url.openConnection()
try
{
if(cx instanceof HttpURLConnection)
{
cx.requestMethod = 'POST'
cx.setRequestProperty('Content-Type', 'application/x-www-form-urlencoded')
cx.doInput = true
cx.doOutput = true

cx.connect()

QueryBuilder qb = new QueryBuilder()

parameters?.each { k, v ->
if(v != null)
{
if(v instanceof Collection)
qb.addParameters(k.toString(), v.collect { it.toString() } as String[])
else
qb.addParameter(k.toString(), v.toString())
}
}

cx.outputStream << qb.toString()
cx.outputStream.close()

res.responseCode = cx.responseCode
res.responseMessage = cx.responseMessage
res.headers = cx.headerFields
}
}
finally
{
if(cx.respondsTo('close'))
cx.close()
}

return res
}

/**
* Similarly to the unix grep command, checks the location one line at a time and returns
* all the lines which matches the pattern.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ def interface ScriptManager extends Shutdownable
*/
ScriptNode installScript(args)

/**
* @return a script previously installed (or <code>null</code> if not found)
*/
ScriptNode findScript(mountPoint)

/**
* Installs the root script
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ package org.linkedin.glu.agent.impl.script

import org.linkedin.glu.agent.api.MountPoint
import org.linkedin.glu.agent.impl.storage.Storage
import org.slf4j.Logger
import org.linkedin.glu.agent.api.FutureExecution

/**
* The purpose of this class is to keep track and record the state of the script manager
Expand All @@ -34,7 +32,7 @@ def class StateKeeperScriptManager implements ScriptManager
public static final String MODULE = StateKeeperScriptManager.class.getName();
public static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MODULE);

private final ScriptManager _scriptManager
private final @Delegate ScriptManager _scriptManager
private final Storage _storage

StateKeeperScriptManager(args)
Expand Down Expand Up @@ -145,65 +143,6 @@ def class StateKeeperScriptManager implements ScriptManager
_storage.clearState(MountPoint.create(mountPoint))
}

FutureExecution executeAction(args)
{
return _scriptManager.executeAction(args);
}

boolean interruptAction(args)
{
return _scriptManager.interruptAction(args)
}


def executeCall(args)
{
return _scriptManager.executeCall(args)
}

Logger findLog(mountPoint)
{
return _scriptManager.findLog(mountPoint)
}

/**
* Clears the error of the script mounted at the provided mount point
*/
void clearError(mountPoint)
{
_scriptManager.clearError(mountPoint)
}

public getMountPoints()
{
return _scriptManager.mountPoints
}

boolean isMounted(mountPoint)
{
return _scriptManager.isMounted(mountPoint);
}

boolean waitForState(args)
{
return _scriptManager.waitForState(args);
}

def waitForAction(args)
{
return _scriptManager.waitForAction(args)
}

def getState(mountPoint)
{
return _scriptManager.getState(mountPoint);
}

public getFullState(mountPoint)
{
return _scriptManager.getFullState(mountPoint)
}

void shutdown()
{
_scriptManager.shutdown()
Expand Down
2 changes: 1 addition & 1 deletion project-spec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
spec = [
name: 'glu',
group: 'org.linkedin',
version: '3.0.0',
version: '3.1.0',

versions: [
grails: '1.3.5',
Expand Down
4 changes: 2 additions & 2 deletions scripts/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import org.linkedin.gradle.tasks.SingleArtifactTask
import org.apache.tools.ant.filters.ReplaceTokens

def gluScriptsTestProjectName = 'org.linkedin.glu.scripts.tests'
def gluScriptsTestProjectName = 'org.linkedin.glu.scripts-test-fwk'

configure(subprojects.findAll {it.name != gluScriptsTestProjectName}) {
apply plugin: 'groovy'
Expand All @@ -36,7 +36,7 @@ configure(subprojects.findAll {it.name != gluScriptsTestProjectName}) {
compile project(':agent:org.linkedin.glu.agent-api')
groovy spec.external.groovy

//testCompile project(gluScriptsTestProjectName)
testCompile project(":scripts:${gluScriptsTestProjectName}")
testCompile spec.external.junit
}

Expand Down
38 changes: 38 additions & 0 deletions scripts/org.linkedin.glu.script-jetty/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2011 Yan Pujante
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

configurations {
jetty
}

dependencies {
jetty spec.external.jettyPackage
}

// fetching jetty (the skeleton)
def jettyArchive = configurations.jetty.resolve().toList()[0]

// compiling the sample app to deploy in jetty
def sampleWebappProject = evaluationDependsOn(':samples:org.linkedin.glu.samples.sample-webapp')
File sampleWebapp = sampleWebappProject.tasks.getByPath('war').archivePath

// providing the values to the test
test {
systemProperties = [skeleton: jettyArchive,
war: sampleWebapp.canonicalPath]
}

tasks.'test'.dependsOn(sampleWebappProject.tasks.getByPath('war'))
Loading

0 comments on commit 66ad3a2

Please sign in to comment.