Groovy call another script to set variables - groovy

I'm trying to define variables in another groovy script that I want to use in my current script. I have two scripts like this:
script1.groovy
thing = evaluate(new File("script2.groovy"))
thing.setLocalEnv()
println(state)
script2.groovy
static def setLocalEnv(){
def state = "hi"
def item = "hey"
}
When I println(state), I get a missing property exception. Basically I want script2 to have config variables that I can load in the context of script1. How can I do this?

I'm not sure what/how you want to do exactly, but I guess you can achieve your goal using one of the class available in groovy dynamique scripting capabilities: groovy.lang.Binding or GroovyClassLoader or GroovyScriptEngine, here is an example using GroovyShell class:
abstract class MyScript extends Script {
String name
String greet() {
"Hello, $name!"
}
}
import org.codehaus.groovy.control.CompilerConfiguration
def config = new CompilerConfiguration()
config.scriptBaseClass = 'MyScript'
def shell = new GroovyShell(this.class.classLoader, new Binding(), config)
def script = shell.parse('greet()')
assert script instanceof MyScript
script.setName('covfefe')
assert script.run() == 'Hello, covfefe!'
This is one way to bind a variable to an external script file, more examples from the doc:
http://docs.groovy-lang.org/latest/html/documentation/guide-integrating.html
P.S. Loading external file can be done with GroovyClassLoader:
def gcl = new GroovyClassLoader()
def clazz2 = gcl.parseClass(new File(file.absolutePath))
Hope this helps.

Related

Unable to apply Newify on Groovyshell

I want to run some dynamic script with help of Groovyshell. But, i don't want to write new keyword in dynamic script. So, i thought of adding a CompilerConfiguration with Newify keyword. But, things are not working as expected.
CompilerConfiguration configuration = new CompilerConfiguration()
configuration.addCompilationCustomizers(
new ASTTransformationCustomizer(
[pattern: "[A-Za-z0-9].*"],
Newify
))
GroovyShell shell = new GroovyShell(profile, configuration)
Still i am getting error
Cannot find matching method sample#BoundingRegion(int, int, int, int)
where BoundingRegion is a class
Perhaps you need to provide more information. This works for me:
import org.codehaus.groovy.control.*
import org.codehaus.groovy.control.customizers.*
def script = '''
class Main {
static main(args) {
assert BigInteger.new(42).toString() == '42' // Ruby style
assert BigDecimal('3.14').toString() == '3.14' // Python style matching regex
}
}
'''
def configuration = new CompilerConfiguration()
configuration.addCompilationCustomizers(
new ASTTransformationCustomizer(
[pattern: "[A-Za-z0-9].*"],
Newify
))
new GroovyShell(configuration).evaluate(script)

Getting variables from a different file in Jenkins Pipeline

I have a contants.groovy file as below
def testFilesList = 'test-package.xml'
def testdataFilesList = 'TestData.xml'
def gitId = '9ddfsfc4-fdfdf-sdsd-bd18-fgdgdgdf'
I have another groovy file that will be called in Jenkins pipeline job
def constants
node ('auto111') {
stage("First Stage") {
container('alpine') {
script {
constants = evaluate readTrusted('jenkins_pipeline/constants.groovy')
def gitCredentialsId = constants."${gitId}"
}
}
}
}
But constants."${gitId}" is says "cannot get gitID from null object". How do I get it?
It's because they are local variables and cannot be referenced from outside. Use #Field to turn them into fields.
import groovy.transform.Field
#Field
def testFilesList = 'test-package.xml'
#Field
def testdataFilesList = 'TestData.xml'
#Field
def gitId = '9ddfsfc4-fdfdf-sdsd-bd18-fgdgdgdf'
return this;
Then in the main script you should load it using load step.
script {
//make sure that file exists on this node
checkout scm
def constants = load 'jenkins_pipeline/constants.groovy'
def gitCredentialsId = constants.gitId
}
You can find more details about variable scope in this answer

Groovy Binding: Cannot cast object with class 'custompackage.CustomClass' to class 'custompackage.CustomClass'

Using Groovy Binding to execute scripts from a main controller and attempting to pass a custom object, I get the error mentioned in the title.
Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'custompackage.CustomClass#60099951' with class 'custompackage.CustomClass' to class 'custompackage.CustomClass'
Here's the relevant code:
// Controller.groovy
import custompackage.CustomClass
CustomClass test = new CustomClass()
def binding = new Binding()
def engine = new GroovyScriptEngine('./src')
binding.setProperty("test", test)
engine.run("CustomScript.groovy", binding)
The file being run with above:
// CustomScript.groovy
import custompackage.CustomClass
CustomClass t
if(!binding.variables.containsKey("test")){
t = new CustomClass()
} else {
t = test
}
I'm defining the CustomClass t at the beginning for the purpose of autocomplete in my IDE. When running as def t it works fine.
I know the object is being passed correctly, due to the exception (and further printing of the object)
The error occurs on t = test
Why is Groovy trying to cast an object of the same type to it's type, and then failing to do so? And is there a fix that will still allow me to keep the statically typed t?
Thanks!
It seems custompackage.CustomClass in Controller.groovy is not the same as in CustomScript.groovy.
I checked the class instances in CustomScript.groovy with the debugger and found something interesting:
def a = CustomClass.class // Debugger: a={Class#1499} "class custompackage.CustomClass"
def b = test.class // Debugger: b={Class#1187} "class custompackage.CustomClass"
While when using GroovyShell instead of GroovyScriptEngine in Controller.groovy I get:
def a = CustomClass.class // Debugger: a={Class#1185} "class custompackage.CustomClass"
def b = test.class // Debugger: b={Class#1185} "class custompackage.CustomClass"
and the assignment t = test works without error.
The Controller.groovy file using GroovyShell looks like this:
// Controller.groovy
import custompackage.CustomClass
CustomClass test = new CustomClass()
def binding = new Binding()
def shell = new GroovyShell(binding)
binding.setProperty("test", test)
shell.evaluate(new File("CustomScript.groovy"))
I checked the documentation of GroovyScriptEngine and found a constructor which takes a ClassLoader as argument. Maybe that's the way to go but I don't know for sure.

soapui shared datasource in groovy script

I have prepared a test case in SoapUI Open Source which loops over values in csv file and sends request for each set of values (taken care of by groovy script). I want to modify it so each thread for each new iteration uses value from next row of csv file.
import com.eviware.soapui.impl.wsdl.teststeps.*
def testDataSet = []
def fileName = "C:\\sSHhrTqA5OH55qy.csv"
new File(fileName).eachLine { line -> testDataSet.add( line.split(",") ) }
def myProps = new java.util.Properties();
myProps = testRunner.testCase.getTestStepByName("Properties");
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
def testCase = testRunner.testCase;
def testStep = testCase.getTestStepByName("TestRequest");
testRunner = new com.eviware.soapui.impl.wsdl.testcase.WsdlTestCaseRunner(testCase, null);
testStepContext = new com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext(testStep);
while (true) {
for ( i in testDataSet ) {
myProps.setPropertyValue("parameter0",i[0]);
myProps.setPropertyValue("username",i[1]);
myProps.setPropertyValue("parameter1",i[2]);
myProps.setPropertyValue("password",i[3]);
testStep.getTestRequest().setUsername(myProps.getPropertyValue("username"))
testStep.getTestRequest().setPassword(myProps.getPropertyValue("password"))
testStep.run(testRunner, testStepContext);
}
}
I want to modify this script so each thread from the pool gets unique (next) unused value from data source
I tried to use newFixedThreadPool from java.util.concurrent as suggested here (Concurrency with Groovy), however I can't get it to work - either requests are duplicated or SoapUI crashes (I am new to concurrency).
Can you please help me to get it right?
I think this would work for you:
while (true) {
for ( i in testDataSet ) {
def th = Thread.start(){
myProps.setPropertyValue("parameter0",i[0]);
myProps.setPropertyValue("username",i[1]);
myProps.setPropertyValue("parameter1",i[2]);
myProps.setPropertyValue("password",i[3]);
testStep.getTestRequest().setUsername(myProps.getPropertyValue("username"))
testStep.getTestRequest().setPassword(myProps.getPropertyValue("password"))
testStep.run(testRunner, testStepContext);
}
th.join()
}
So, new threads would be created on each loop.
If you wanted to test out if its working you could place loginfo(s) in the code...
log.info("Thread Id: " + Thread.currentThread().getId() as String)
I don't see your point. SoapUi already gives you a datasource test step that accepts a csv file as input.
So once you have all these values you can transfer the properties and run the test.

Why am I getting StackOverflowError?

In Groovy Console I have this:
import groovy.util.*
import org.codehaus.groovy.runtime.*
def gse = new GroovyScriptEngine("c:\\temp")
def script = gse.loadScriptByName("say.groovy")
this.metaClass.mixin script
say("bye")
say.groovy contains
def say(String msg) {
println(msg)
}
Edit: I filed a bug report: https://svn.dentaku.codehaus.org/browse/GROOVY-4214
It's when it hits the line:
this.metaClass.mixin script
The loaded script probably has a reference to the class that loaded it (this class), so when you try and mix it in, you get an endless loop.
A workaround is to do:
def gse = new groovy.util.GroovyScriptEngine( '/tmp' )
def script = gse.loadScriptByName( 'say.groovy' )
script.newInstance().with {
say("bye")
}
[edit]
It seems to work if you use your original script, but change say.groovy to
class Say {
def say( msg ) {
println msg
}
}

Resources