I want to run a bash script from a task (in build.gradle) before the Instrumentation tests starts. That script should run a docker container that contains a ruby bases mock server.
I don't know why I can't get it but this is all I have for now (placed in my build.gradle):
task startMock(type:Exec) {
println("Executing myScript")
def proc = "cd ../..".execute()
proc.waitForProcessOutput(System.out, System.err)
proc = "../scripts/_mock.sh -a start -p ${projectDir}/../../ -m deps/mock-config".execute()
proc.waitForProcessOutput(System.out, System.err)
}
gradle.projectsEvaluated {
connectedDebugAndroidTest.dependsOn startMock
}
The Problem is that the Task runs always, not only on calling connectedDebugAndroidTest (or connectCheck) ...
I'm confused and appreciate any help :) Maybe someone can give me a hint on how to solve this.
OK, I finally got it hoooray :)
I added the following parts to my build.gradle(app) and now the script is called before and after connectCheckhas been triggered:
task('mockStart', type: Exec){
doFirst {
println "MOCK: Start server ..."
}
executable "../../scripts/_mock.sh"
args '-a', 'start', '-p', "${projectDir}/../../", '-m', 'deps/mock-config'
}
task('mockStop', type: Exec){
doFirst {
println "MOCK: Stop Server ..."
}
executable "../../scripts/_mock.sh"
args '-a', 'stop', '-p', "${projectDir}/../../", '-m', 'deps/mock-config'
}
gradle.projectsEvaluated {
connectedDebugAndroidTest.dependsOn mockStart
connectedDebugAndroidTest.finalizedBy mockStop
}
Maybe this will help someone that has some error like I had :)
Good luck & stay tuned!
Related
I have a Flutter application that also has some Rust code, and they communicate via flutter_rust_bridge. There are already some tutorials talking about how to build such an app. However, they require me to manually trigger cargo build (or similar commands) and flutter run separately. You know, this is quite cumbersome, and if I forget to execute cargo, or that command fails, I run the risk of building a Flutter app with an old binary. Therefore, I wonder how I can automate that process. Thanks!
Android
Add the following to your build.gradle
[
new Tuple2('Debug', 'debug'),
new Tuple2('Profile', 'release'),
new Tuple2('Release', 'release')
].each {
def taskPostfix = it.first
def profileMode = it.second
tasks.whenTaskAdded { task ->
if (task.name == "javaPreCompile$taskPostfix") {
println "hello let ${task.name} dependsOn cargoBuild$taskPostfix"
task.dependsOn "cargoBuild$taskPostfix"
}
}
tasks.register("cargoBuild$taskPostfix", Exec) {
println "hello executing doLast for task cargoBuild$taskPostfix"
commandLine 'sh', '-c', "build-your-rust-code"
}
}
where build-your-rust-code is the command you use to build your Rust code. (For example, I use fastlane to wrap it (of course you can use others) and I put that in a rust folder, so I just call cd $projectDir/../rust/fastlane && bundle exec fastlane build_android profile:$profileMode.)
iOS
Method 1
The most manual way is as follows. Go to Build Phases, click + button and choose New Run Script Phase. Then enter the script you want to run to build your Rust code. (For example, for myself, I use Shell /bin/sh and call some fastlane commands which executes the actual commands.)
Method 2
A more automated approach is to use the CocoaPod files, which will automatically help you set up everything. For example, I add:
s.script_phase = {
:name => 'Compile Rust',
:script => your-script-to-compile-it,
:execution_position => :before_compile,
:shell_path => '/bin/sh'
}
(For example, my script is %q{env -i HOME="$HOME" PROJECT_DIR="$PROJECT_DIR" CONFIGURATION="$CONFIGURATION" /bin/bash -c 'source ~/.bashrc && cd $PROJECT_DIR/../.symlinks/plugins/vision_utils/rust/fastlane && LC_ALL= LANG=en_US.UTF-8 bundle exec fastlane build_ios profile:$CONFIGURATION'}. Notice that, at least in my case, I have to create a brand new shell. Otherwise the opencv in my Rust complains and fails to compile. But you may not have this problem if your Rust code does not contain c++ things.)
(Q&A style answer in order to help people who face the same situation as me, especially when not familiar with Android and iOS)
I have a few unit tests that are being ran by the command './gradlew test'. When this completes, it generates 'index.html' in the build/reports folder.
Is there a way to have that automatically open when finished?
Thanks
Edit: I can use the command
./gradlew test && start C:\\<PACKAGE>\\build\\reports\\tests\\testDebugUnitTest\\debug\\index.html
This works. However. Is there a way to just include that entire line in my gradle file so it does it automatically?
In your project build.gradle you can add this task:
task testAndOpen(type: Exec) {
//execute test task first
dependsOn 'test'
//set the base dir
workingDir './build/reports/tests/testDebugUnitTest/debug'
//launch cmd and open the file with the default associated program
commandLine 'cmd', '/c', 'start index.html'
}
then from terminal you can do .\gradlew testAndOpen.
If you are not on Windows you can do a similar thing with a bit different commands. This is the "dirty and fast" way, because the path folder must be changed manually for different flavors but it's a good starting point.
the task will open all test reports from folder MyProject/mymodule/build/reports/tests
just add the code to mymodule/build.gradle
and execute in terminal:
./gradlew app:testDebugUnitTest --tests com.examplepackage.* testOpenTestReport
task testOpenTestReport(group: "verification") {
doLast{
File reportsDir = new File("${project.buildDir.path}/reports/tests")
if (!reportsDir.exists() || !reportsDir.isDirectory()) {
println "reportsDir ${reportsDir.absolutePath} not exist"
return null
}
fileTree(reportsDir)
.filter { it.isFile() }
.files
.findAll { (it.name == "index.html") }
.forEach{file->
println "${file.absolutePath}"
//launch cmd and open the file with the default associated program
if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
println "start ${file.absolutePath}".execute().text.trim()
} else {
println "sensible-browser ${file.absolutePath}".execute().text.trim()
}
}
}
}
I have a python script that modifies a part of a java class (add static fields) according to another file.
What is the best way to add it in the Gradle build process (My script requires pyton3 (so it would be better to tell the user if he doesn't have it) and need to be executed before the java compilation) ?
I'm not certain what the best way to run python from gradle is, but one candidate is to use the exec task:
task runPython(type:Exec) {
workingDir 'path_to_script'
commandLine /*'cmd', '/c'*/ 'python', 'my_script.py'
}
You might have to add cmd and /c on windows. You can also parse/assert on stdout for success conditions. See here for more about Exec.
As for ensuring that this task always runs before compileJava, you have to add this task as a dependsOn for the compileJava task:
compileJava.dependsOn runPython
alternate syntax:
compileJava{
dependsOn runPython
}
When compileJava dependsOn runPython, runPython is executed first everytime compileJava is invoked.
Task:
Let connected Android tests work well on Android M.
Question:
How to enable read/write contacts permission when run connected Android test?
Problem:
I know pm command could enable the apk's permission.
adb shell pm grant <PACKAGE_NAME> <PERMISSION_NAME>
I want to run the tests which could run on both real apis and mock apis. If I fail to trigger pm command in gradle DSL, test code is not able to touch real api for security reason.
I try to add the step as first of connectedAndroidTest (connectedInstrumentTest) task. It doesn't work for the target apk has not been install yet. The command lines are called with error code.
android.testVariants.all { variant ->
variant.connectedInstrumentTest.doFirst {
def adb = android.getAdbExe().toString()
exec {
commandLine 'echo', "hello, world testVariants"
}
exec {
commandLine adb, 'shell', 'pm', 'grant', variant.testedVariant.applicationId, 'android.permission.READ_ACCOUNTS'
}
}
}
I try to add the step as last step of install task. It isn't called when I start connectedAndroidTest.
android.applicationVariants.all { variant ->
if (variant.getBuildType().name == "debug") {
variant.install.doLast {
def adb = android.getAdbExe().toString()
exec {
commandLine 'echo', "hello, world applicationVariants"
}
exec {
commandLine adb, 'shell', 'pm', 'grant', variant.applicationId, 'android.permission.READ_ACCOUNTS'
}
}
}
}
My plan is to enable permissions before tests are launched. I don't know which task is proper one. It looks like connectedVariantAndroidTest doesn't depend on installVariant, though they both call adb install.
I try to run the pm grant from test cases. It fails as expected.
I will accept other solutions to run the android tests well.
I think that you need to create your own task depending on installDebug and then make connectedDebugAndroidTest depend on your task.
People does it to disable animations and works, you force the app installation and grant your specific permission before the android tests are executed like this:
def adb = android.getAdbExe().toString()
task nameofyourtask(type: Exec, dependsOn: 'installDebug') { // or install{productFlavour}{buildType}
group = 'nameofyourtaskgroup'
description = 'Describe your task here.'
def mypermission = 'android.permission.READ_ACCOUNTS'
commandLine "$adb shell pm grant ${variant.applicationId} $mypermission".split(' ')
}
tasks.whenTaskAdded { task ->
if (task.name.startsWith('connectedDebugAndroidTest')) { // or connected{productFlavour}{buildType}AndroidTest
task.dependsOn nameofyourtask
}
}
You can add this code to a new yourtask.gradle file and add the next line at the bottom of the build.gradle file:
apply from: "yourtask.gradle"
And declare your permission in the proper manifest
<uses-permission android:name="android.permission.READ_ACCOUNTS" />
Update:
Fixed commandLine command like you did on your version for multiple variants, thanks.
android.applicationVariants.all { variant ->
if (variant.getBuildType().name == "debug") {
task "configDevice${variant.name.capitalize()}" (type: Exec){
dependsOn variant.install
group = 'nameofyourtaskgroup'
description = 'Describe your task here.'
def adb = android.getAdbExe().toString()
def mypermission = 'android.permission.READ_ACCOUNTS'
commandLine "$adb shell pm grant ${variant.applicationId} $mypermission".split(' ')
}
variant.testVariant.connectedInstrumentTest.dependsOn "configDevice${variant.name.capitalize()}"
}
}
I have a custom script which runs on the source (.java) and AndroidManifest.xml.
I'd like to execute this script as part of the gradle build process at the start of the assembleRelease task (maybe just after app:preBuild).
Any idea of how I can go about doing this?
I know I can do something like this to exec the script:
task DoStuff(type:Exec) {
workingDir 'path/to/script'
commandLine 'python3', 'do_stuff.py'
}
But I'm not sure where to put that, etc...
You can add a custom action to assembleRelease with doFirst method:
assembleRelease.doFirst {
//invoke python script but there's no access to `workingDir` or `commandLine`
//because it's an action not a task
}
or define dependency:
assembleRelease.dependsOn DoStuff
assembleRelease.mustRunAfter DoStuff //this might be redundant