Android studio: automatically open html test result file generated by './gradlew test' - android

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()
}
}
}
}

Related

How can I reduce the size of xml assets when building my apk

In my app I use the assets directory to store some XML files (some are large).
In my design time I want the files to use indentation and also put some comments in it.
This is enlarging my xml files and can add up to a large size.
Is it possible to add a task to the gradle build to remove all indentation and comments for the xml files before packaging it in the apk? If so how?
This will not only shrink my apk, but will also assist at run time with the xml processing.
EDIT
The answer by fhomovc was correct, but was missing some part.
I will mark it as correct but if anyone else will need it, here are the details:
In general I need a task that will run the minify utility and it should look like:
task minifyAssets(type:Exec) {
workingDir dirName // the directory of the merged assets under the build directory
commandLine 'minify', '-r', '-o', '.', '.'
doFirst {
println 'minifyAssets...'
}
}
This task should only be executed after the merged assets task is executed and before the package task is executed.
The main problem is that there should be a dedicated task for each variant, so I needed to do it dynamically:
First create the exec task and make it dependent on the merge task
applicationVariants.all { variant ->
// dynamically add minify task for specific variant
def dirName = new File("app\\build\\intermediates\\merged_assets\\" + variant.name + "\\out\\levels").getAbsolutePath()
def minifyTaskName = "minifyAssets" + variant.name
def mergeAssetsTaskName = "merge" + variant.name + "Assets"
def myTask = tasks.register(minifyTaskName, Exec) {
workingDir dirName
// require that minify utility will be in the path. Download from https://github.com/tdewolff/minify/tree/master/cmd/minify
commandLine 'minify', '-r', '-o', '.', '.'
doFirst {
println 'minifyAssets...' + workingDir
}
}
// set the minify task dependant on the merge assets task
myTask.get().dependsOn mergeAssetsTaskName
}
Now we need to make the specific package task depend on the minify task:
// when the package task is added make it dependant on the minify task
tasks.whenTaskAdded { theTask ->
if (theTask.name.startsWith("package") && (theTask.name.endsWith("Debug") || theTask.name.endsWith("Release"))) {
def minifyTaskName = theTask.name.replace("package", "minifyAssets")
theTask.dependsOn minifyTaskName
}
}
You can run a custom script with an xml minifier.
For the minifier: you can install minify following the installation steps provided.
For the script: you can refer to this answer. Essentially your task would look something like this
task executeScript(type:Exec) {
println 'Minifying xmls...'
//on linux
commandLine 'minify -r -o ./ --match=\.xml ./values' // ./values should be the path to your resources directory
}
Check the documentation to understand better how minify works. I haven't tested this solution myself, so it may need a few adjustments but you get the general idea. If you are using a Windows machine then the Script (commandLine) should be different, if I can find any examples online I'll add them.

copy files to sdcard before executing instrumentation tests

what's the best approach to copy data to sdcard before all instrumentation tests.
So far, my idea is to create Gradle task to do that. Do you have an idea how to do it more robust and simple?
I did something similar with a gradle task :
//Copy file form device to project
task copyFileForTest{
group = "My_tasks"
description ="Copy auth token from device to project"
doFirst {
println 'Getting file for Unit tests'
def resFolder = new File(projectDir.absolutePath+'/src/test/res')
exec {
commandLine android.getAdbExecutable(),'pull', '-a', '/storage/emulated/0/Android/data/org.myproject.debug/files/file.txt', "${resFolder.absolutePath}"
}
}
}
And then to be sure it will run after my instrumentation test:
copyFileForTest.mustRunAfter 'connectedAndroidTest'

How to copy debug assets for unit tests

I have an android library gradle project. And I need to copy some files to assets folder for robolectric unit tests.
To do it I've defined a copy task:
task copyDebugAssets(type: Copy) {
from "${projectDir}/somewhere"
into "${buildDir}/intermediates/bundles/debug/assets"
}
but I can't add this task as a dependency for processDebugResources task:
processDebugResources.dependsOn copyDebugAssets
because of this error:
Could not get unknown property 'processDebugResources' for object of type com.android.build.gradle.LibraryExtension.
Now I have to manually execute this task before unit test:
./gradlew clean copyDebugAssets test
How can I solve it?
The android plugin adds several tasks dynamically. Your .dependsOn line doesn't work because at the time gradle is trying to process this line, processDebugResources task yet available. You should tell gradle to add the dependency as soon as the upstream task is available:
tasks.whenTaskAdded { task ->
if (task.name == 'processDebugResources') {
task.dependsOn copyDebugAssets
}
}
Why copy? Configure where the assets should be pulled from:
android {
// other cool stuff here
sourceSets {
androidTest {
assets.srcDirs = ['../testAssets']
}
}
}
(replacing ../testAssets with a path to where the assets should come from)
I have used this successfully with androidTest for instrumentation testing. AFAIK, it should work for test or any other source set.

How to enable read/write contacts permission when run connected Android test?

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()}"
}
}

Execute .bat from Android gradle after APK build

I`m moving my project to Gradle build system. After APK build I need to sign it with manufacturer certificate.
How to execute .bat file by Gradle after APK was built?
task runSign(type:Exec) {
println "Sign apk..."
commandLine = ['cmd','/c','sign.bat']
}
I know just how to run .bat before build (but I need after):
preBuild.doLast {
runSign.execute()
}
I've found the solution.
Go to Run -> Edit Configurations...
Choose module where you want to run task after APK build. Add new configuration after "Gradle-aware Make".
Click on icon at picture below to choose module where task is implemented and write name of it.
After this steps your custom Gradle task will be executed after APK build.
I needed to perform something similar, but additionally to that I needed to know with which product favour was built, and with what configuration.
I've ended up adding following line into build.gradle:
android {
applicationVariants.all { variant -> variant.assemble.doLast { signAndInstall.execute() } }
...
And with following helper function:
//
// Returns array for CommandLine, path, variant (arm7), configuration (debug / release)
//
def getCommandLine(path)
{
String taskReqStr = getGradle().getStartParameter().getTaskRequests().toString()
Pattern pattern = Pattern.compile("(assemble|generate)(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher(taskReqStr)
if (!matcher.find())
return [ path ]
String flavor = matcher.group(2).toLowerCase() + " " + matcher.group(3).toLowerCase()
return [ path, matcher.group(2).toLowerCase(), matcher.group(3).toLowerCase() ]
}
task signAndInstall(type: Exec) {
def batch = projectDir.toString() + '\\postbuild.bat'
commandLine = getCommandLine(batch)
}
With following postbuild.bat:
#echo off
rem echo %0 %*
if %1. == . exit /b 0
if %2. == . exit /b 0
set InPath=%~dp0build\outputs\apk\%1\%2\app-%1-%2.apk
set OutPath=%~dp0build\outputs\apk\app-%1-%2.apk
copy /y %InPath% %OutPath% 1>NUL
You can of course configure this batch to perform anything what you like, %1 receives your product favour (e.g. arm7, arm8, fat...), and %2 receives 'debug' or 'release' as configuration.

Categories

Resources