./gradlew test connectedAndroidTest erases my getFilesDir() folder
Asked Answered
P

1

6

I'm writing a Room database into my getFilesDir() folder. (Writing the database onto the removable SD card is apparently going to require some major research!)

When I manually run my app, I want to write some records and leave them in the database, so I don't need to keep writing them again. When I run my tests, I switch the database name from "*.dat" to "_test.dat" (think Ruby on Rails's or Django's "environments" system). The tests are free to erase records.

This system works when I manually tweezer each test in Android Studio to run it. But when I run everything in a batch, in gradlew, something erases the "*.dat" version of the database. This means I must constantly manually repopulate the database, each time I manually test.

What inside gradlew is erasing the contents of my getFilesDir() folder? And how (without figuring out how to use "external" storage), do I defeat it?

Code samples available on request, but it's all just generic stuff. Switching to getExternalFilesDir() does not fix the problem. StackOverflow said to try ./gradlew test connectedAndroidTest -x uninstallAndroidTest, but there's no uninstallAndroidTest task.


The top level build.gradle:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

tasks.withType(Test) {
  testLogging {
    exceptionFormat "full"
    events "started", "skipped", "passed", "failed"
    showStandardStreams true
  }
}

The app/build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.allflat.planarinfinity"
        minSdkVersion 26
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
//    packagingOptions {
//        exclude 'META-INF/DEPENDENCIES'
//        exclude 'META-INF/LICENSE'
//        exclude 'META-INF/LICENSE.txt'
//        exclude 'META-INF/license.txt'
//        exclude 'META-INF/NOTICE'
//        exclude 'META-INF/NOTICE.txt'
//        exclude 'META-INF/notice.txt'
//        exclude 'META-INF/ASL2.0'
//        exclude 'META-INF/INDEX.LIST'
//    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.google.android.material:material:1.0.0'
    testImplementation 'junit:junit:4.12'
   // testImplementation 'org.mockito:mockito-core:2.19.0'
    testImplementation 'androidx.arch.core:core-testing:2.1.0'
    androidTestImplementation 'org.powermock:powermock-core:2.0.2'
    androidTestImplementation 'org.powermock:powermock-module-junit4:2.0.2'
    androidTestImplementation 'org.powermock:powermock-api-easymock:2.0.2'
    androidTestImplementation 'androidx.test:core:1.2.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2-alpha02'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02'
    androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.2.0'
    androidTestImplementation 'androidx.test:rules:1.2.0'
//    androidTestImplementation 'androidx.test.platform.app:'
    implementation 'androidx.room:room-runtime:2.2.0'
    annotationProcessor 'androidx.room:room-compiler:2.2.0'
    testImplementation 'androidx.test:core:1.2.0'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test:rules:1.0.2'
    androidTestImplementation 'org.easymock:easymock:4.0.2'
//    androidTestImplementation 'org.mockito:mockito-core:2.19.0'
}

It's the 'uninstall' in here:

https://android.googlesource.com/platform/tools/base/+/8d969614b7beca74e4d17f2d1c5956e75053f7ce/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java

Could someone take it out? It's annoying everyone.

Placable answered 9/8, 2019 at 16:24 Comment(6)
Two things: 1. Even though it's generic, please add build.gradle. 2. You said that you are running tests in batch. I wonder if there is a problematic test. Try to determine that by omitting all other tests and just running a single test, but as a batch. So, you want to copy all test classes/scripts into another folder somewhere so that they don't get written. Then, you can isolate whether the test code, itself, is deleting the folder, or whether it's your test infrastructure via Gradle that's doing it.Berl
I'd say that it's not gradlew that is deleting the getFilesDir() since it is basically a build system that carries out tasks like connectedAndroidTest and doesn't have any intentions of deleting directories within your device. Either the test runner (AndroidJUnitRunner which is part of the testing library used in most projects but I doubt its doing that) or some code running could be deleting your directory. Another suggestion (other than what @Berl said) is to try finding all traces of calls to getFilesDir() in the code first and see if there's any delete() being called with.Rainmaker
After I run ./gradlew ... test, the app icon is no longer in my app list. It is uninstalled. I will try the suggestions, but right now I'm just happy that #58477257 appears fixed!Placable
When I run the tests from Android Studio, they don't uninstall the app or erase its home folder, like I said. So it's not the test doing it. This is a feature, not a bug, inside Gradle's list of tasks.Placable
does all files are erased or just db ?Finny
I don't have any other files. I could check, but I really think it's the uninstall line I found. The app icon and settings go away.Placable
P
0

Per the winning entry here...

Run connectedAndroidTest and skip uninstall

...the answer is to write a Rakefile containing:

task default: [ :unit_test_and_install, :espresso_test ]

task :unit_test_and_install do
    sh './gradlew --console=verbose test installDebug installDebugAndroidTest'
end

devices = `adb devices`
serial_numbers = devices.split("\n")[1..-1].map{|q| q.split("\t")[0] }

task :espresso_test do

    threads = serial_numbers.map do |sn|
        Thread.new do
            sh "adb -s #{sn} shell am instrument -w -r -e package com.mycorp.myapp -e disableAnalytics true " + 
                    "com.mycorp.myapp.test/androidx.test.runner.AndroidJUnitRunner | tee #{sn}.txt"
        end
    end

    threads.each &:join

    serial_numbers.each do |sn|
        grop = `grep "^OK .*tests" #{sn}.txt`

        if grop == ''
            sh "cat #{sn}.txt"
            abort 'FAILURES on ' + sn
        end
    end

end

Then just enter $ rake. All the tests run, while my manual-testing data and configurations all survive.

We need a Ruby Rakefile, not a Makefile, because we need to process the output and look for errors to successfully return either 0 or 1 to the environment. But adb etc fails the rule "test faults are syntax errors," and does not propagate the correct exit value at fault time. I want my command line to abend and not commit broken code, so we stash the test outputs into SERIALNUMBER.txt files and then grep them.

And we need threads, so many devices can test at the same time without waiting for each other.

Correctly detecting errors allows one to integrate in one line, $ rake && git commit -am 'the same comment every time' && git push. If there were any errors, they display on the console, and the git commit does not happen.

And we need Makefile-style processing, because if any command fails we need to stop processing, following the rule "test faults are syntax errors."

(A new bug is at Espresso test fault time we no longer get the message saying where is the HTML output file, but because the error itself is in the spew this is less of a problem.)

Another bug: If I have two tablets hooked up via USB debugging, I get error: more than one device/emulator, which is utterly bogus because ./gradlew test connectedAndroidTest naturally works correctly on all devices... That's why the Rakefile must find all the devices and then dispatch adb shell -s to each one.

Please write any and all complaints about using a lowly Rakefile to call gradlew here: [__].

Placable answered 11/2, 2020 at 17:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.