I am using groovy to process a batch job, I am planning to cache domain object by using groovy's #Memoize annotation, but problem is the expiry, I want to expire the cache when the job is finished. Is there any way to tell groovy to expire the cache from code?
As per the docs, #Memoized features only a max value and a protection cache size parameters.
Since the AST under the covers will create the memoize mechanism through Closure::memoize, you could emulate it with a map and memoized closures which can be disposed:
class Job {
def log = []
def repoBase = [
sum: { a, b ->
log << "sum $a and $b"
a + b
},
multiply: { a, b ->
log << "multiply $a and $b"
}
]
def repo
def startJob() {
repo = repoBase.collectEntries { key, value ->
[(key): value.memoize()]
} as Expando
}
def withJob(Closure c) {
startJob()
c.delegate = repo
c.delegateStrategy = Closure.DELEGATE_FIRST
c(this)
repo = null
}
}
And test:
j = new Job()
j.withJob {
multiply 3, 4
multiply 3, 4
multiply 8, 8
sum 9, 9
sum 1, 2
sum 9, 9
}
assert j.log == [
"multiply 3 and 4",
"multiply 8 and 8",
"sum 9 and 9",
"sum 1 and 2"
]
Related
I'm using a map which contains string key and list values. When using a for each loop, I'm unable to use my string variable to pull the size of my lists (commented out line). My output for the below is as follows:
Task :nametest
starting test
3
NA
SEA
EUR
Ultimately, my goal is nested loops to print all of the DC's in each of my lists.
task nametest << {
def dcMap = [:];
def naDCs = ["10", "20", "30"];
def seaDCs = ["40", "50", "60", "70"]
def eurDCs = ["80", "90", "100", "110", "120"]
dcMap << ['NA':naDCs];
dcMap << ['SEA':seaDCs];
dcMap << ['EUR':eurDCs];
println "starting test";
println dcMap.NA.size();
def dcMapKeys = dcMap.keySet();
for (def reg : dcMapKeys){
println reg;
//println dcMap.reg.size();
}
}
So, your dcMap.reg.size() your code tries to get the value corresponding to "reg" String instead of the value of def reg variable.
There are several ways you can follow to get your expected output:
1. println dcMap[reg].size();
2. println dcMap."${reg}".size();
3. println dcMap.get(reg).size();
Use any of the above options.
I have a JSON array (list of maps) similar to:
def listOfMap = [[TESTCASE:1, METHOD:'CLICK', RESULT:'PASS'],
[TESTCASE:2, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:3, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:4, METHOD:'TYPETEXT', RESULT:'FAIL']]
I am grouping by the METHOD names and collecting the FAILURE % of each method
def percentage (map){
(map.FAIL ?: 0) / ((map.PASS ?: 0) + (map.FAIL ?: 0)) * 100
}
def result = listOfMap.groupBy{it.METHOD}
.collectEntries{[(it.key) : percentage(it.value.countBy{it.RESULT})]}
Now my output will be [CLICK : 66.6, TYPETEXT : 100]
To sort the above result in descending order of percentage,
def sortedResult = result.sort { a, b -> b.value <=> a.value }
Now my output will be [TYPETEXT : 100, CLICK : 66.6]
How can i get the FAIL count and PASS count of the METHOD linked to the above sorted order?
My output should be two separate lists (sorted in descending order of failure %)
passList = [0, 1] Note : [TYPETEXT passed 0 times, CLICK passed 1 time]
failList = [1, 2] Note : [TYPETEXT failed 1 time, CLICK failed 2 times]
Basically i am looking for this data to create a CSV report something like the below from the listofMap given:
Given this (from the original post):
def listOfMap = [[TESTCASE:1, METHOD:'CLICK', RESULT:'PASS'],
[TESTCASE:2, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:3, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:4, METHOD:'TYPETEXT', RESULT:'FAIL']]
Consider using Expando so that percentage is a field, but also passCount and failCount:
def percentage(passCount, failCount) {
failCount / (passCount + failCount) * 100
}
def result = listOfMap.groupBy{it.METHOD}.collectEntries{
def rec = new Expando()
def count = it.value.countBy{ it.RESULT }
rec."passCount" = count.'PASS' ?: 0
rec."failCount" = count.'FAIL' ?: 0
rec."percentage" = percentage(rec."passCount",
rec."failCount")
[(it.key) : rec]
}
def sortedResult = result.sort { a, b ->
b.value."percentage" <=> a.value."percentage"
}
sortedResult.each { println it }
The output matches the basic schema desired for the CSV:
$ groovy Example.groovy
TYPETEXT={failCount=1, percentage=100, passCount=0}
CLICK={failCount=2, percentage=66.6666666700, passCount=1}
You can do a multi-grouping to make your life easier:
def listOfMap = [[TESTCASE: 1, METHOD: 'CLICK', RESULT: 'PASS'],
[TESTCASE: 2, METHOD: 'CLICK', RESULT: 'FAIL'],
[TESTCASE: 3, METHOD: 'CLICK', RESULT: 'FAIL'],
[TESTCASE: 4, METHOD: 'TYPETEXT', RESULT: 'FAIL']]
def result = listOfMap.groupBy({ it.METHOD }, { it.RESULT })
.collectEntries { method, r ->
def passCount = r.PASS?.size() ?: 0
def failCount = r.FAIL?.size() ?: 0
[method, [passCount: passCount, failCount: failCount, failPercentage: (failCount / (passCount + failCount) * 100.0)]]
}.sort { -it.value.failPercentage }
You can then do:
result.values().failCount
or
result.values().passCount
to get the numbers you require in the question.
It's best to never try and keep multiple separate lists in the same order, much easier to store all the data together, and extract it after sorting the whole thing
Can I take a test like this and extract the where clause data table into a reusable block?
#Unroll
void "test that doSomething with #a and #b does not fail"(String a, String b) {
when:
doSomethingWithAandB(a, b)
then:
notThrown(Exception)
where:
a | b
"foo" | "bar"
"foo" | "baz"
"foo" | "foo"
}
something like this (pseudo code):
#Unroll
void "test that doSomethingElse with #a and #b does not fail"(String a, String b) {
when:
doSomethingElseWithAandB(a, b)
then:
notThrown(Exception)
where:
dataTable()
}
def dataTable(a, b) { // this is now reusable in multiple tests
a | b
"foo" | "bar"
"foo" | "baz"
"foo" | "foo"
}
Table-formatted data in where clause is actually parsed as a set of OR expressions at compile time, collected into list of lists and then transposed, so this:
where:
a | b | c
1 | 0 | 1
2 | 2 | 2
4 | 5 | 5
will be transformed into this:
where:
a << [1, 2, 4]
b << [0, 2, 5]
c << [1, 2, 5]
before the test methods are generated (see org.spockframework.compiler.WhereBlockRewriter for details). This var << list construct is referred to as "data pipe" in documentation.
Upgrading the existing answers a bit, as of Spock 1.1, one can shorten the code a bit using a construct called "Multi-Variable Data Pipes", which will transpose the table:
class SampleTest extends Specification {
#Unroll
def "max of #a and #b gives #c"() {
expect:
Math.max(a, b) == c
where:
[a, b, c] << dataTable()
}
static def dataTable() {
[
[1, 0, 1],
[2, 2, 2],
[4, 5, 5]
]
}
}
Fun fact: while the docs on Syntactic Variations don't explain why, it is because the table rows are parsed as a set of OR expressions, the double bars can also be used -
where:
a | b || c
1 | 0 || 1
2 | 2 || 2
4 | 5 || 5
Yes, you can.
import spock.lang.Specification
import spock.lang.Unroll
class SampleTest extends Specification {
#Unroll
def "max of #a and #b gives #c"() {
expect:
Math.max(a, b) == c
where:
a << aProvider()
b << bProvider()
c << cProvider()
}
private List<Integer> aProvider() {
[1 ,2 ,4]
}
private List<Integer> bProvider() {
[0 ,2 ,5]
}
private List<Integer> cProvider() {
[1 ,2 ,5]
}
}
Of course, aProvider/bProvider/cProvider can be re-written in a 'groovier way' and among other things, can be externalized to some class and reused in many tests. You don't have to specify a table, but can supply 'data pipes'. Read more in Data Driven Testing chapter.
Thanks, Mark, for your answer! Based on what I learned, I've arrived at a reasonably simple solution.
import spock.lang.Specification
import spock.lang.Unroll
class SampleTest extends Specification {
#Unroll
def "max of #a and #b gives #c"() {
expect:
Math.max(a, b) == c
where:
params << dataTable()
a = params.a
b = params.b
c = params.c
}
// this is now reusable in multiple tests
def dataTable() {
return [
// case 1
[
a: 1,
b: 0,
c: 1
],
// case 2
[
a: 1,
b: 2,
c: 2
],
// case 3
[
a: 4,
b: 5,
c: 5
]
]
}
}
I have the following map:
def map = [];
map.add([ item: "Shampoo", count: 5 ])
map.add([ item: "Soap", count: 3 ])
I would like to get the sum of all the count properties in the map. In C# using LINQ, it would be something like:
map.Sum(x => x.count)
How do I do the same in Groovy?
Assuming you have a list like so:
List list = [ [item: "foo", count: 5],
[item: "bar", count: 3] ]
Then there are multiple ways of doing it. The most readable is probably
int a = list.count.sum()
Or you could use the Closure form of sum on the whole list
int b = list.sum { it.count }
Or you could even use a more complex route such as inject
int c = list.count.inject { tot, ele -> tot + ele } // Groovy 2.0
// c = list.count.inject( 0 ) { tot, ele -> tot + ele } // Groovy < 2.0
All of these give the same result.
assert ( a == b ) && ( b == c ) && ( c == 8 )
I would use the first one.
You want to use the collect operator. I checked the following code with groovysh:
list1 = []
total = 0
list1[0] = [item: "foo", count: 5]
list1[1] = [item: "bar", count: 3]
list1.collect{ total += it.count }
println "total = ${total}"
First of all, you're confusing map and list syntax in your example. Anyhow, Groovy injects a .sum(closure) method to all collections.
Example:
[[a:1,b:2], [a:5,b:4]].sum { it.a }
===> 6
I want to return multiple values from a function written in groovy and receive them , but i am getting an error
class org.codehaus.groovy.ast.expr.ListExpression, with its value '[a,
b]', is a bad expression as the left hand side of an assignment
operator
My code is
int a=10
int b=0
println "a is ${a} , b is ${b}"
[a,b]=f1(a)
println "a is NOW ${a} , b is NOW ${b}"
def f1(int x) {
return [a*10,a*20]
}
You almost have it. Conceptually [ a, b ] creates a list, and ( a, b ) unwraps one, so you want (a,b)=f1(a) instead of [a,b]=f1(a).
int a=10
int b=0
println "a is ${a} , b is ${b}"
(a,b)=f1(a)
println "a is NOW ${a} , b is NOW ${b}"
def f1(int x) {
return [x*10,x*20]
}
Another example returning objects, which don't need to be the same type:
final Date foo
final String bar
(foo, bar) = baz()
println foo
println bar
def baz() {
return [ new Date(0), 'Test' ]
}
Additionally you can combine the declaration and assignment:
final def (Date foo, String bar) = baz()
println foo
println bar
def baz() {
return [ new Date(0), 'Test' ]
}
You can declare and assign the variables in which the return values are stored in one line like this, which is a slightly more compact syntax than that used in Justin's answer:
def (int a, int b) = f1(22)
In your particular case you may not be able to use this because one of the variables passed to f1 is also used to store a return value
When running Groovy in the context of Jenkins pipeline job the above answers do not work (at least on version 2.60.2), but the following does:
node {
obj = ret2()
fw = obj[0]
lw = obj[1]
echo "fw=${fw}"
echo "lw=${lw}"
}
def ret2()
{
return [5, 7]
}
Or alternatively:
node {
obj = ret2()
fw = obj.a
lw = obj.b
echo "fw=${fw}"
echo "lw=${lw}"
}
def ret2()
{
return [a:5, b:7]
}