Run nested task in custom task - ant

I have written my own ANT task to perform some function. However, I need this task to invoke a java task as a nest task. So I have the following code in my build file:
<mytask ... >
<java ... />
</mytask>
I would like to run a piece of code after the java task finishes executing but before mytask completes, for the purpose of cleanup.
Is this a broken design, not recommended in build files? If not, which method should I over-ride in order to run the cleanup method?

Let your task implement the org.apache.tools.ant.TaskContainer interface, write your own addTask(Task task) method.
For example (it should only take a task named "java"):
private List<Task> _nestedTask = new ArrayList<>();
public void addTask(Task task) {
if (task.getTaskName().equals("java")) {
_nestedTasks.add(task);
}
else {
throw new BuildException("Support only nested <java> task.");
}
}
Please note that if you write multiple nested <java> tasks in your build file, you need to handle them by your self. To execute the nested <java> tasks, just iterate through the list and call execute() method for each task.
Update:
When a nested task is added, it doesn't run automatically. It won't even run if its execute() method is not called in your custom task.
So... A very basic and simple example:
// your custom task's execute...
public void execute() {
//do something
for (Task task : _nestedTask) {
task.perform(); // here the nested task is executed.
}
//do something
}

Related

Jenkins plugin - how to get currently executing job?

I am building a jenkins pipeline plugin (methods to be invoked from a pipeline) and need to get retrieve information about the currently running job, which invoked my methods.
There are a couple of questions I found talking about it, for example here - Jenkins Plugin How to get Job information.
Yet I can't figure out how to use this information. I do have access to the Jenkins instance, but don't have any info about the current project, job, build, etc. How can I get hold of that info?
Note, this is a pipeline steps plugin, there is no perform method in it.
Ok, after search, I finally found the answer in the most obvious of all places - documentation for writing pipeline steps plugins and the corresponding API documentation.
The way to do it is from the Execution class. Inside it, just call getContext(), which returns StepContext, which then has .get method to get the rest of the things you need:
public class MyExecution extends SynchronousNonBlockingStepExecution<ReturnType> {
...
#Override
protected ReturnType run() throws Exception {
try {
StepContext context = getContex();
// get currently used workspace path
FilePath path = context.get(FilePath.class);
//get current run
Run run = context.get(Run.class);
// ... and so on ...
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
...
}

Inform Jenkins about failed TestNG tests from Gradle run task to mark build as Failed

For the reasons I cannot influence, there is the following mechanism on TestNG launching on the project.
In a word, it creates a new instance of TestNG, adds listeners, specifies classes and runs the tests. Then, all this dirty code is called from Gradle run task (which is actually empty and as far as I understood, simply calls the TestManager.main() method).
I removed the part of code just to show the main direction:
class TestManager {
static void main(String[] args) {
try {
runTests(args[0])
} catch (Exception e) {
e.printStackTrace()
System.in.read()
}
}
private static void runTests(Application app) {
TagsConfig.runs.each { run ->
if (run.execute) {
List<TagsSuite> suites = TagsConfig.suites
suites.each { suite ->
if (suite.execute) {
Reflections reflections = new Reflections("${app.packageName}.${suite.name}")
def classes = reflections.getSubTypesOf(${suite.name})
if (classes.size() > 0) {
TestNG testNG = new TestNG()
testNG.testClasses = classes
testNG.groupByInstances = true
testNG.outputDirectory = "testng-output"
testNG.addListener(new TestListenerAdapter())
testNG.addListener(new ExceptionListener())
testNG.addListener(new AllureTestListener())
if (TagsConfig.isSmoke) {
testNG.setGroups("smoke")
} else if (TagsConfig.isExtendedSmoke) {
testNG.setGroups("extended_smoke")
}
testNG.run()
}
}
}
}
}
}
So the test launch looks like this:
gradle clean
gradle run
I can not change the way the tests are started now and one of the problems is that the build in Jenkins is always Sucessful even if there are failed/broken/skipped tests.
So, I can get the number of failed tests from TestListenerAdapter, but how can I let Jenkins know that there were failed tests?
Maybe by returning an exit code from a Gradle run task or by installing some plugin that will check the count of failed tests in TestListenerAdapter?
For now, I'm setting a "FAILED_TESTS_COUNT" system property in onFinish() event and change the build status in pipeline if it is not 0, but this looks really dirty.
Jenkins 2.89.3
Gradle 3.5.1
TestNG 6.9.8
I personally use the Jenkins Text Finder post-build action. In the above example, it looks for a pattern in the standard output and set the build to "unstable" (orange) when it's found.
In your case, just uncheck both checkboxes and find the according matching pattern.

TestNG + Cucumber JVM parallel execution

I'm tryting to run our Cucumber JVM tests by few threads in parallel.
I'm using standart TastNG approach to do it (via suite XML file)
My xml file is:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="BDD" parallel="methods" thread-count="3" data-provider-thread-count="3">
<test name="BDD">
<classes>
<class name="com.tests.bdd.SimpleBDDTests"></class>
</classes>
</test>
</suite>
My test class is:
#CucumberOptions(features = "src/test/java/com/tests/bdd/simpleFeatures")
public class SimpleBDDTests {
private TestNGCucumberRunner tcr;
#BeforeClass(alwaysRun = true)
public void beforeClass() throws Exception {
tcr = new TestNGCucumberRunner(this.getClass());
}
#AfterClass(alwaysRun = true)
public void afterClass() {
tcr.finish();
}
#Test(dataProvider = "features")
public void feature(CucumberFeatureWrapper cucumberFeature) {
tcr.runCucumber(cucumberFeature.getCucumberFeature());
}
#DataProvider(parallel = true)
public Object[][] features() {
return tcr.provideFeatures();
}}
My feature files are like:
Feature: First test
#sanity
Scenario: First simple test
Given Base check step
I have 4 feature files, which are defines the same scenarios with only one step - Given Base check step
When these features are executed one by one, it works fine, but when i try to run them in parallel, everything gets broken.
Almost all of these featres marked as failed with the following exception:
A scoping block is already in progress
java.lang.IllegalStateException: A scoping block is already in progress
at cucumber.runtime.java.guice.impl.SequentialScenarioScope.checkState(SequentialScenarioScope.java:64)
at cucumber.runtime.java.guice.impl.SequentialScenarioScope.enterScope(SequentialScenarioScope.java:52)
at cucumber.runtime.java.guice.impl.GuiceFactory.start(GuiceFactory.java:34)
at cucumber.runtime.java.JavaBackend.buildWorld(JavaBackend.java:123)
at cucumber.runtime.Runtime.buildBackendWorlds(Runtime.java:141)
at cucumber.runtime.model.CucumberScenario.run(CucumberScenario.java:38)
at cucumber.runtime.model.CucumberFeature.run(CucumberFeature.java:165)
at cucumber.api.testng.TestNGCucumberRunner.runCucumber(TestNGCucumberRunner.java:63)
I understand that it might be happen because of multi-thread calls to the same step - Given Base check step
So my question is how can i fix that? How can i run these tests in parallel?
PS: I know that it should be possible to do it by JUnit + Maven surefire plugin, but it is not applicable for current project, we need to achieve that goal by TestNG.
Thanks.

Gradle: Set a JVM option on an ANT task

I'm using Gradle 2.1 and have an ANT task defined something like this:
task myTask {
doFirst {
ant.taskdef(name: 'mytask',
classname: 'com.blah.Blah',
classpath: configurations.gen.asPath
)
ant.mytask(foo: 'bar')
}
}
There is a property I need to pass to the com.blah.Blah as a JVM argument (because, instead of doing something sane like passing parameter values in as parameters, the creators of this ANT task have decided that system properties are a reasonable way of conveying information). I've tried a number of things, including:
Setting the systemProperty on all tasks with JavaForkOptions:
tasks.withType(JavaForkOptions) {
systemProperty 'myproperty', 'blah'
}
Passing -Dmyproperty=blah when I invoke gradle.
Various things involving ant.systemPropery, ant.options.forkOptions, ant.forkOptions, etc. (I can't actually find reliable documentation on this anywhere)
I'm at a loss here. It feels like I should be able to say something like:
task myTask {
doFirst {
ant.taskdef(name: 'mytask',
classname: 'com.blah.Blah',
classpath: configurations.gen.asPath
)
ant.systemProperty 'myProperty', 'blah'
ant.mytask(foo: 'bar')
}
}
...but that obviously doesn't work.
In Gradle you can use Groovy so there's nothing preventing you from setting the system property programmatically as shown below:
task myTask {
doFirst {
System.setProperty('myProperty', 'blah')
// Use AntBuilder
System.clearProperty('myProperty')
}
}
Keep in mind that Gradle's AntBuilder executes Ant logic in the same process used for Gradle. Therefore, setting a system property will be available to other tasks in your build. This might have side effects when two tasks use the same system property (depending on the execution order) or if you run your build in parallel.
Instead you might want to change your Ant task to use Ant properties instead to drive your logic (if that's even an option). Ant properties can be set from Gradle as such:
task myTask {
doFirst {
ant.properties.myProperty = 'blah'
// Use AntBuilder
}
}

How to execute a Groovy Script from my Grails app?

Well, it seems a simple task but I didn't manage to make it run.
I have a groovy script that runs fine under Windows Vista when calling from prompt:
> cd MY_GAILS_PROJECT_DIR
> groovy cp src/groovy scripts/myscript.groovy
Now, I want to execute this script (and passing to it some input arguments) through my my Maintenance Service Class (called from a controller) as below,
class MaintenanceService {
def executeMyScript() {
"groovy cp src/groovy scripts/myscript.groovy".execute()
}
}
It does not work at all! I don't even manage to have the execute() method recognizing any command (like "cd .".execute()) throwing exception:
Error 500: java.io.IOException: Cannot run program "cd": CreateProcess error=2, The system cannot find the file specified
1- How can I execute a groovy script from my grails application?
2- What are the best practices here? For instance, should I use the QuartzPlugin and then the triggerNow method for executing a script? should I use a Gant Task? If yes, how to do it?
Thank you.
If you don't mind your script running asynchronously (in a separate process to the service method), the following should work assuming groovy is on your PATH variable:
def cmd = ['groovy.bat', 'cp', 'src/groovy scripts/myscript.groovy']
cmd.execute()
If you want to view the output of the process in the application console, you should try something like this instead
// Helper class for redirecting output of process
class StreamPrinter extends Thread {
InputStream inputStream
StreamPrinter(InputStream is) {
this.inputStream = is
}
public void run() {
new BufferedReader(new InputStreamReader(inputStream)).withReader {reader ->
String line
while ((line = reader.readLine()) != null) {
println(line)
}
}
}
}
// Execute the script
def cmd = ['groovy', 'cp', 'src/groovy scripts/myscript.groovy']
Process executingProcess = cmd.execute()
// Read process output and print on console
def errorStreamPrinter = new StreamPrinter(executingProcess.err)
def outputStreamPrinter = new StreamPrinter(executingProcess.in)
[errorStreamPrinter, outputStreamPrinter]*.start()
Update:
In response to your comment below, try the following (which assumes you're on Windows):
1: Create the file C:\tmp\foo.groovy. The content of this file should be simply:
println 'it works!'
2: In the groovy console, run the following:
cmd = ['groovy.bat', 'C:\\tmp\\foo.groovy']
cmd.execute().text
3: You should see the result of the script (the text 'it works!') shown in the Groovy console
If you can't get this simple example working, there's something wrong with your environment, e.g. 'groovy.bat' is not on your PATH. If you can get this example working, then you should be able to work forward from it to achieve your objective.
As of grails 1.3.6 the run-script command is built in to let you run
grails run-script myScript.groovy
For earlier versions of grails, check out my updated blog post from what Carlos posted above.
Easiest Way:
Generate an Groovy Class and place at in your /src/groovy Folder of your Grails Project.
Import that Class in your Domain Class and use the Functions you defined.
My 2 Cents...
This might help as well:
http://naleid.com/blog/2008/03/31/using-gant-to-execute-a-groovy-script-within-the-grails-context-updated/
Carlos
Another decision you can use GroovyScriptEngine for example:
file MyScript.groovy:
static String showMessage() {
println("Message from showMessage")
}
file BootStrap.groovy:
class BootStrap {
def init = { servletContext ->
new GroovyScriptEngine("scripts")
.loadScriptByName("MyScript.groovy")
.showMessage()
}
def destroy = {
}
}

Resources