Call Gradle task with parameters inside other task - kotlin

I have a Gradle task which looks like this (it collects inputs from user and then passes them to the second task):
task A() {
doFirst {
ant.input(message: "Version:", addproperty: 'version')
tasks.b "-Pversion=${ant.properties.version}" //attempt to invoke task B()
}
}
The second task just print the passed variables:
task B() {
doFirst {
println "Version: ${version}"
}
}
The problem is, that the task A does not invoke task B. I've tried with finalizedBy and doLast but I couldn't find a solution how to pass arguments while executing the task inside the other task.
When I execute the task B via command line like:
gradle B -Pversion=1.0.0
it works fine, and params are passed correctly.

It's not possible to 'invoke' task B from task A - Gradle tasks are not like functions.
The only way to pass data from one task to another is if
the first task saves the variables to a file,
then the second task reads the file produced by the first task.
There is an example in the docs, which you can adjust to capture user input
abstract class Producer extends DefaultTask {
#OutputFile
abstract RegularFileProperty getOutputFile()
#TaskAction
void produce() {
// capture user input
ant.input(message: "Version:", addproperty: 'version')
String message = ant.properties.version
// save the user input to a file
def output = outputFile.get().asFile
output.text = message
logger.quiet("Wrote '${message}' to ${output}")
}
}

Related

kotlin flow using flatmap cannot call method in collect

lifecycleScope.launch {
adapter?.getData()?.let {
val flowable = it.asFlow()
flowable.onEach {
doCompress(it)
}.flatMapConcat {
flow<Unit> {
updateProgressInMain()
}.flowOn(Dispachers.Main)
}.catch {
dismissLoading()
}.flowOn(Dispatchers.IO).collect {
Log.d("Collect", "" + Thread.currentThread())
}
}
}
As above code, I cannot print 'Collect' log in console but other code can run well. However, I can print the log when I use 'WithContext()' in onEach period instead of flatMapConcat to switch Thread. Could anyone discribe what happened?
You produce an empty Flow that never emits in flatMapConcat, so the resulting Flow will never emit anything either.
Your code doesn't quite make sense to me, but supposing the task you want to do is, for each item emitted by the source LiveData as Flow:
Pass it to doCompress() on the IO Dispatcher. Apparently doCompress() doesn't return anything.
Call updateProgressInMain() on the main thread after eeach item is compressed.
And then call dismissLoading() whether or not it failed.
Then this simpler code should do it:
adapter?.getData()?.asFlow()?.onEach {
runCatching {
withContext(Dispatchers.IO) {
doCompress(it)
Log.d("Collect", "" + Thread.currentThread())
}
updateProgressInMain()
}
dismissLoading()
}?.launchIn(lifecycleScope)

How to Take Screenshot when TestNG Assert fails?

String Actualvalue= d.findElement(By.xpath("//[#id=\"wrapper\"]/main/div[2]/div/div[1]/div/div[1]/div[2]/div/table/tbody/tr[1]/td[1]/a")).getText();
Assert.assertEquals(Actualvalue, "jumlga");
captureScreen(d, "Fail");
The assert should not be put before your capture screen. Because it will immediately shutdown the test process so your code
captureScreen(d, "Fail");
will be not reachable
This is how i usually do:
boolean result = false;
try {
// do stuff here
result = true;
} catch(Exception_class_Name ex) {
// code to handle error and capture screen shot
captureScreen(d, "Fail");
}
# then using assert
Assert.assertEquals(result, true);
1.
A good solution will be is to use a report framework like allure-reports.
Read here:allure-reports
2.
We don't our tests to be ugly by adding try catch in every test so we will use Listeners which are using an annotations system to "Listen" to our tests and act accordingly.
Example:
public class listeners extends commonOps implements ITestListener {
public void onTestFailure(ITestResult iTestResult) {
System.out.println("------------------ Starting Test: " + iTestResult.getName() + " Failed ------------------");
if (platform.equalsIgnoreCase("web"))
saveScreenshot();
}
}
Please note I only used the relevant method to your question and I suggest you read here:
TestNG Listeners
Now we will want to take a screenshot built in method by allure-reports every time a test fails so will add this method inside our listeners class
Example:
#Attachment(value = "Page Screen-Shot", type = "image/png")
public byte[] saveScreenshot(){
return ((TakesScreenshot)driver).getScreenshotAs(OutputType.BYTES);
}
Test example
#Listeners(listeners.class)
public class myTest extends commonOps {
#Test(description = "Test01: Add numbers and verify")
#Description("Test Description: Using Allure reports annotations")
public void test01_myFirstTest(){
Assert.assertEquals(result, true)
}
}
Note we're using at the beginning of the class an annotation of #Listeners(listeners.class) which allows our listeners to listen to our test, please mind the (listeners.class) can be any class you named your listeners.
The #Description is related to allure-reports and as the code snip suggests you can add additional info about the test.
Finally, our Assert.assertEquals(result, true) will take a screen shot in case the assertion fails because we enabled our listener.class to it.

How to run a command line command with Kotlin DSL in Gradle 6.1.1?

I am trying to run the code block below, after reading multiple posts on the topic and the Gradle manual. I run the below and get the following error: execCommand == null!
Any ideas on what I am doing wrong with the below code block?
open class BuildDataClassFromAvro : org.gradle.api.tasks.Exec() {
#TaskAction
fun build() {
println("Building data classes.....")
commandLine("date")
}
}
tasks.register<BuildDataClassFromAvro>("buildFromAvro") {
description = "Do stuff"
}
To define a Gradle task that runs a command-line using the Gradle Kotlin DSL do something like this in your build file:
task<Exec>("buildFromAvro") {
commandLine("echo", "test")
}
In the example above the commandLine will simply run echo, outputting the value test. So replace that with whatever you want to actually do.
You can then run that with gradle buildFromAvro
More info here: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html
If adding to an existing task:
exec {
commandLine("echo", "hi")
}
Another approach is to use the Java ProcessBuilder API:
tasks.create("MyTask") {
val command = "echo Hello"
doLast {
val process = ProcessBuilder()
.command(command.split(" "))
.directory(rootProject.projectDir)
.redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT)
.start()
.waitFor(60, TimeUnit.SECONDS)
val result = process.inputStream.bufferedReader().readText()
println(result) // Prints Hello
}
}

Project Reactor - subscribe on parallel scheduler doesn't work

I'm looking at examples and reading documentation and I've found some problems while trying to subscribe on Flux in a parallel manner.
I have a 3 functions, as below.
private val log = LoggerFactory.getLogger("main")
private val sequence = Flux.just(1, 2)
fun a() {
sequence.subscribeOn(Schedulers.parallel()).subscribe { log.info("*** {}", it) }
sequence.subscribe { log.info(">>> {}", it) }
}
fun b() {
sequence.subscribe { log.info(">>> {}", it) }
}
fun c() {
sequence.subscribeOn(Schedulers.parallel()).subscribe { log.info("*** {}", it) }
}
Now, when I run each method separately I have a proper output from functions a() and b(), but output from c() is empty. Is that to be expected, is it by design? If so, why is that happening?
Flux.just(...) captures value(s) and thus is optimized to execute immediately in the subscribing Thread.
When you use subscribeOn, you change that subscribing Thread from main to something else, making the just truly asynchronous.
In a(), without a subscribeOn that second just would block the main thread just enough that the test doesn't finish before the asynchronous alternative completes.
In c(), there is no such blocking of the main thread. As a consequence, the test terminates before the asynchronous just has had time to emit anything, and that is why you see no output.
To make that more visible, add a Thread.sleep(10) and you'll see some output.

Gradle task: how to wait for the file operation to complete

Here's what I'm trying to do:
Copy archive (zip) from buildscript dependencies to the temp directory
Unzip archive to another directory within build
Here's task to copy archive (works)
task copyTpcds(type: Copy) {
file('build/zip').mkdirs()
from buildscript.configurations.classpath
include 'tpcds*'
into 'build/zip'
}
And the task to unzip and then delete archive
task extractTpcds(type: Copy) {
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
def outDir = file('build/cmd/tpcds')
outDir.mkdirs() // make sure the directory exists
from zipTree(file(names[0])) // generates error when
into outDir
// now remove copied zip file
//zipFile.delete() // deletes file before the extractions completes?
}
Here are few scenarios:
If I put both tasks into build.gradle and try to run anything, even just gradle tasks then I get this error: Neither path nor baseDir may be null or empty string. path='null' basedir='C:\dev\code\td\pdo\tpcds-tpg' from this code in the task #2: file(names[0])
If I comment out code in the 2nd task, 1st task would run and copy zip file to build/zip
Now I can uncomment code in the 2nd task (except deletion) and execute gradle extractTpcds it will run and extract the archive
So it seems to me that
File operations are evaluated across all tasks regardless of which tasks is executed
If I have a code that copies the file with some follow up code that tries to operate on the file being copied then that code will not wait for copy process to complete and will fail because file is not there yet
I'm at loss on how to deal with this and would greatly appreciate your suggestions
The following works for me, using Gradle 2.12 (and assuming that the zip file resides in files):
buildscript {
configurations {
classpath
}
dependencies {
classpath files("files/tpcds.zip")
}
}
def copyFiles = { ->
ant.mkdir(dir: "build/zip")
buildscript.configurations.classpath.each { def thisFile ->
if (thisFile.name ==~ /tpcds.*/) {
ant.copy(file: thisFile.absolutePath, todir: "build/zip")
}
}
}
tasks.whenTaskAdded { task ->
if (task.name == "extractTpcds") {
copyFiles()
}
}
task copyTpcds << {
copyFiles()
}
task extractTpcds(type: Copy) {
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
def outDir = file('build/cmd/tpcds')
outDir.mkdirs() // make sure the directory exists
from zipTree(file(names[0])) // generates error when
into outDir
// now remove copied zip file
//zipFile.delete() // deletes file before the extractions completes?
}
An issue with the original involves an impedance mismatch vis-a-vis the ICE rule: Initialization phase, Configuration phase, and Execution phase. In particular, the specification of the Copy task is in Configuration phase; typically, task dependencies are enforced (e.g. dependsOn) during the Execution phase. The extractTpcds task has a dependency on other code during the Configuration phase.
In my example, there are are 2 cases:
gradle copyTpcds will call the copyFiles method during Execution phase. The << means "do this stuff last [during the Execution phase]".
gradle tasks will trigger the code in whenTaskAdded, during Configuration phase, and call copyFiles. Similarly for gradle extractTpcds
An alternative to this is simply to use the AntBuilder in both tasks, and avoid Type: Copy completely, as shown here:
buildscript {
configurations {
classpath
}
dependencies {
classpath files("files/tpcds.zip")
}
}
task copyTpcds << {
ant.mkdir(dir: "build/zip")
buildscript.configurations.classpath.each { def thisFile ->
if (thisFile.name ==~ /tpcds.*/) {
ant.copy(file: thisFile.absolutePath, todir: "build/zip")
}
}
}
task extractTpcds(dependsOn: 'copyTpcds') << {
def outDir = "build/cmd/tpcds"
ant.mkdir(dir: outDir)
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
names.eachWithIndex { zipFile, index ->
if (index == 0) {
ant.unzip(src: zipFile, dest: outDir)
}
}
}
Something like this should work, but why do you need to delete the zip? chaining the tasks with dependsOn means you only need to run the second task and the copyTpcds will automatically run unless the inputs or outputs have changed (deleted)
task extractTpcds(type: Copy) {
dependsOn(copyTpcds) //make this execute after
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
def outDir = file('build/cmd/tpcds')
outDir.mkdirs() // make sure the directory exists
from zipTree(file(names[0])) // generates error when
into outDir
// now remove copied zip file
doLast { zipFile.delete() }// deletes file after the extractions completes
}