In the configuration phase of the task I register some dir as builtBy: thisTask. I expect gradle to automatically detect if sources were changed, but the task always being executed.
Here is the task:
subprojects {
def srcMainMirah = file('src/main/mirah')
if (srcMainMirah.exists()) {
idea.module.sourceDirs += srcMainMirah
task compileMirah {
def classesMirahMain = file("$buildDir/classes-mirah/main")
inputs.sourceDir srcMainMirah
def thisTask = delegate
sourceSets.main {
output.dir(classesMirahMain, builtBy: thisTask)
java.srcDir srcMainMirah
}
dependsOn tasks.compileJava
doFirst {
def classpath = files("$buildDir/classes/main").plus(configurations.compile)
mirahc(srcMainMirah, classesMirahMain, classpath)
}
}
}
}
It is for compiling sources in mirah language, which produces *.class files just like java compiler does.
Declaring inputs alone for a task is insufficient to determine if the task is up-to-date. You are required to also declared task.outputs
A task with no defined outputs will never be considered up-to-date. For scenarios where the outputs of a task are not files, or for more complex scenarios, the TaskOutputs.upToDateWhen() method allows you to calculate programmatically if the tasks outputs should be considered up to date.
A task with only outputs defined will be considered up-to-date if those outputs are unchanged since the previous build.
From section 17.9.1 here.
Related
I have an issue where tags from my gauge gradle task is not being passed to beforespec whereas the the tags are passed to spec files
Any idea what is the issue?
I'm in Gauge version: 0.9.1
Plugins
html-report (3.1.0)
java (0.6.2)
gradle gauge task :
task runTestsInQA(type: GaugeTask) {
doFirst {
println 'Running tests for the V1 in QA environment...'
gauge {
specsDir = 'specs'
tags = 'V1'
env = 'qa'
additionalFlags = '--verbose'
}
}
}
_
My beforespec code:
#beforespec(tags = "V1")
public void beforeSpec(ExecutionContext context)
{
System.out.println("Tags in scenario "+context.getAllTags());
}
Here, print statement is throwing null array [ ]
This gist contains an example project with gauge + java + gradle.
In your case, note that the BeforeSpec hook is a tagged execution hook, so it will get executed only when the respective tags are passed.
Also note that if you wish to get tags for a scenario, you are better off using the BeforeScenario hook, since you can then get all the tags (scenarios inherit spec tags).
I would like to set a few things for a few test tasks. More specifically, I would like to add a few environment variables and a few system properties, maybe a few other things such as "dependencies" or "workingDir". With the regular Test task I can do this,
task test1(type:Test, dependsOn:[testPrep,testPrep1]){
workingDir testWorkingPath
systemProperty 'property','abs'
environment.find { it.key ==~ /(?i)PATH/ }.value += (System.properties['path.separator'] + myLibPath)
environment.LD_LIBRARY_PATH = "/usr/lib64:/lib64:${myLibPath}:" + environment.LD_LIBRARY_PATH
}
task test2(type:Test, dependsOn:[testPrep]){
workingDir testWorkingPath
systemProperty 'property','abs'
environment.find { it.key ==~ /(?i)PATH/ }.value += (System.properties['path.separator'] + myLibPath)
environment.LD_LIBRARY_PATH = "/usr/lib64:/lib64:${myLibPath}:" + environment.LD_LIBRARY_PATH
systemPropety 'newProperty','fdsjfkd'
}
It would be nice to have a new task type MyTestType extending the regular Test task type, where the common definition is defined.
task test1(type:MyTestType){
dependsOn testPrep1
}
task test2(type:MyTestType){
systemPropety 'newProperty','fdsjfkd'
}
What would be best way to do this? It seems that the execute() method is final and cannot be extended. I will need to do something like the doFirst to set those properties. Should I add all the extra values in the constructor? Is there any other hook I can use? Thanks.
In general you can extend the 'Test' task and implement your customizations
task test1(type:MyTestType){
}
task test2(type:MyTestType){
systemProperty 'newProperty','fdsjfkd'
}
class MyTestType extends Test {
public MyTestType(){
systemProperty 'property','abs'
}
}
Alternatively you can configure all tasks of type Test with less boilerplate:
// will apply to all tasks of type test.
// regardless the task was created before this snippet or after
tasks.withType(Test) {
systemProperty 'newProperty','fdsjfkd'
}
It is also possible to specify the behavior of a particular superclass setting. Say for example you want to centralize the environment.find block, but allow setting myLibPath per task like this:
task test1(type: MyTestType) {
}
task test2(type: MyTestType) {
libPath = '/foo/bar'
}
You could do that by overriding the configure method:
class MyTestType {
#Input def String libPath
#Override
public Task configure(Closure configureClosure) {
return super.configure(configureClosure >> {
environment.find { it.key ==~ /(?i)PATH/ }.value += (System.properties['path.separator'] + (libPath ?: myLibPath))
})
}
}
Here we use the closure composition operator >> to combine the passed-in closure with our overridden behavior. The user-specified configureClosure will be run first, possibly setting the libPath property, and then we run the environment.find block after that. This can also be combined with soft defaults in the constructor, as in Rene Groeschke's answer
Note that this particular use case might break if you configure the task more than one, since the environment.find statement transforms existing state instead of replacing it.
you can do it the following ways
task TestExt{
Test {
systemPropety 'newProperty','fdsjfkd'
}
}
note: this just adds to the configure method of Test
source: https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N18D18
First off, this is my first foray into Gradle/Groovy (using Gradle 1.10). I'm setting up a multi-project environment where I'm creating a jar artifact in one project and then want to define an Exec task, in another project, which depends on the created jar. I'm setting it up something like this:
// This is from the jar building project
jar {
...
}
configurations {
loaderJar
}
dependencies {
loaderJar files(jar.archivePath)
...
}
// From the project which consumes the built jar
configurations {
loaderJar
}
dependencies {
loaderJar project(path: ":gfxd-demo-loader", configuration: "loaderJar")
}
// This is my test task
task foo << {
configurations.loaderJar.each { println it }
println configurations.loaderJar.collect { it }[0]
// The following line breaks!!!
println configurations.loaderJar[0]
}
When executing the foo task it fails with:
> Could not find method getAt() for arguments [0] on configuration ':loaderJar'.
In my foo task I'm just testing to see how to access the jar. So the question is, why does the very last println fail? if a Configuration object is a Collection/Iterable then surely I should be able to index into it?
Configuration is-a java.util.Iterable, but not a java.util.Collection. As can be seen in the Groovy GDK docs, the getAt method (which corresponds to the [] operator) is defined on Collection, but not on Iterable. Hence, you can't index into a Configuration.
I'm using Gradle as a build system for my project.
What I want is to make task A depend on task B if a given property is set to "true". Is this doable, and if the answer is yes, how can I do that?
Currently, I'm using conventionMapping but this doesn't seem to work. My code looks like this:
MyTask.conventionMapping.propertyName = { MyPluginConvention.propertyName }
if (MyTask.propertyName.equals("true")) {
MyTask.dependsOn ...
}
Thanks in advance,
Marin
Instead of working with task/convention classes, you'll have to work with their instances. Also, you'll have to defer the decision whether to add a task dependency. For example:
def myTask = project.tasks.create("myTask", MyTask)
def otherTask = ...
def myConvention = new MyConvention()
...
myTask.conventionMapping.propertyName = { myConvention.propertyName }
// defer decision whether to depend on 'otherTask'
myTask.dependsOn { myTask.propertyName == "true" ? otherTask : [] }
If there's no task variable in scope, you can also reference existing tasks via project.myTask or project.tasks["myTask"].
PS: Convention objects have been largely replaced by extension objects.
I want to use the Sauce Labs Java REST API to send Pass/Fail status back to the Sauce Labs dashboard. I am using Geb+Spock, and my Gradle build creates a test results directory where results are output in XML. My problem is that the results XML file doesn't seem to be generated until after the Spock specification's cleanupSpec() exits. This causes my code to report the results of the previous test run, rather than the current one. Clearly not what I want!
Is there some way to get to the results from within cleanupSpec() without relying on the XML? Or a way to get the results to file earlier? Or some alternative that will be much better than either of those?
Some code:
In build.gradle, I specify the testResultsDir. This is where the XML file is written after the Spock specifications exit:
drivers.each { driver ->
task "${driver}Test"(type: Test) {
cleanTest
systemProperty "geb.env", driver
testResultsDir = file("$buildDir/test-results/${driver}")
systemProperty "proj.test.resultsDir", testResultsDir
}
}
Here is the setupSpec() and cleanupSpec() in my LoginSpec class:
class LoginSpec extends GebSpec {
#Shared def SauceREST client = new SauceREST("redactedName", "redactedKey")
#Shared def sauceJobID
#Shared def allSpecsPass = true
def setupSpec() {
sauceJobID = driver.getSessionId().toString()
}
def cleanupSpec() {
def String specResultsDir = System.getProperty("proj.test.resultsDir") ?: "./build/test-results"
def String specResultsFile = this.getClass().getName()
def String specResultsXML = "${specResultsDir}/TEST-${specResultsFile}.xml"
def testsuiteResults = new XmlSlurper().parse( new File( specResultsXML ))
// read error and failure counts from the XML
def errors = testsuiteResults.#errors.text()?.toInteger()
def failures = testsuiteResults.#failures.text()?.toInteger()
if ( (errors + failures) > 0 ) { allSpecsPass = false }
if ( allSpecsPass ) {
client.jobPassed(sauceJobID)
} else {
client.jobFailed(sauceJobID)
}
}
}
The rest of this class contains login specifications that do not interact with SauceLabs. When I read the XML, it turns out that it was written at the end of the previous LoginSpec run. I need a way to get to the values of the current run.
Thanks!
Test reports are generated after a Specification has finished execution and the generation is performed by the build system, so in your case by Gradle. Spock has no knowledge of that so you are unable to get that information from within the test.
You can on the other hand quite easily get that information from Gradle. Test task has two methods that might be of interest to you here: addTestListener() and afterSuite(). It seems that the cleaner solution here would be to use the first method, implement a test listener and put your logic in afterSuite() of the listener (and not the task configuration). You would probably need to put that listener implementation in buildSrc as it looks like you have a dependency on SauceREST and you would need to build and compile your listener class before being able to use it as an argument to addTestListener() in build.gradle of your project.
Following on from erdi's suggestion, I've created a Sauce Gradle helper library, which provides a Test Listener that parses the test XML output and invokes the Sauce REST API to set the pass/fail status.
The library can be included by adding the following to your build.gradle file:
import com.saucelabs.gradle.SauceListener
buildscript {
repositories {
mavenCentral()
maven {
url "https://repository-saucelabs.forge.cloudbees.com/release"
}
}
dependencies {
classpath group: 'com.saucelabs', name: 'saucerest', version: '1.0.2'
classpath group: 'com.saucelabs', name: 'sauce_java_common', version: '1.0.14'
classpath group: 'com.saucelabs.gradle', name: 'sauce-gradle-plugin', version: '0.0.1'
}
}
gradle.addListener(new SauceListener("YOUR_SAUCE_USERNAME", "YOUR_SAUCE_ACCESS_KEY"))
You will also need to output the Selenium session id for each test, so that the SauceListener can associate the Sauce Job with the pass/fail status. To do this, include the following output in the stdout:
SauceOnDemandSessionID=SELENIUM_SESSION_ID