better way to do Debug only assert code
Asked Answered
A

3

9

I am writing my first Android application and I am liberally using asserts() from junit.framework.Assert

I would like to find a way to ensure that the asserts are only compiled into the debug build, not in the release build.

I know how to query the android:debuggable attribute from the manifest so I could create a variable and accomplish this in the following fashon:

static final boolean mDebug = ...

if (mDebug) Assert.assertNotNull(view);

Is there a better way to do this? i.e. I would prefer not to use an if() with each assert.

thanks

Arcane answered 18/2, 2011 at 16:12 Comment(0)
T
22

I think the Java language's assert keyword is likely what you want. Under the covers, the assert keyword essentially compiles into Dalvik byte code that does two things:

  1. Checks whether the static variable assertionsDisabled (set in the class' static constructor via a call to java.lang.Class.desiredAssertionStatus()) is != 0 and if so, does nothing
  2. If it is 0, then it checks the assertion expression and throws a java.lang.AssertionError if the expression resolves to false, effectively terminating your application.

The Dalvik runtime by default has assertions turned off, and therefore desiredAssertionStatus always returns 1 (or more precisely, some non-zero value). This is akin to running in "retail" mode. In order to turn on "debug" mode, you can run the following command against the emulator or the device:

adb shell setprop debug.assert 1

and this should do the trick (should work on the emulator or any rooted debugging-ready device).

Note however that the aforementioned Dalvik code that checks the value of assertionsDisabled and throws an AssertionError if the expression is false is always included in your byte code and liberal sprinkling of asserts in your code may lead to byte code bloat.

Please see this for a bit more detail: Can I use assert on Android devices?

Twocolor answered 8/4, 2011 at 4:19 Comment(3)
This can also be done from a terminal running on the device if you are root. First su, then setprop debug.assert 1.Pau
Actually, things like assert !BuildConfig.DEBUG || (expr) : "msg"; can be optimized away by javac, which works because the read of $assertionsDisabled has no side-effects (not even failing to load the class). A little ugly and unclear, but it gets rid of them in release builds.Boman
Does adb shell setprop debug.assert 1 turn on asserts globally (in all apps)? Is this setting retained on device restart?Altimeter
C
9

If you're concerned about shipping code with the JUnit asserts in (or any other class path), you can use the ProGuard config option 'assumenosideeffects', which will strip out a class path on the assumption that removing it does nothing to the code.

Eg.

-assumenosideeffects class junit.framework.Assert {
*;
}

I have a common debug library I put all my testing methods in, and then use this option to strip it from my released apps.

This also removes the hard to spot problem of strings being manipulated that are never used in release code. For example if you write a debug log method, and in that method you check for debug mode before logging the string, you are still constructing the string, allocating memory, calling the method, but then opting to do nothing. Stripping the class out then removes the calls entirely, meaning as long as your string is constructed inside the method call, it goes away as well.

Make sure it is genuinely safe to just strip the lines out however, as it is done with no checking on ProGuard's part. Removing any void returning method will be fine, however if you are taking any return values from whatever you are removing, make sure you aren't using them for actual operational logic.

Chinkapin answered 15/9, 2011 at 7:1 Comment(0)
B
1

I mean if you were using a language feature, like assert(), the compiler should be able to strip that out. But this is an actual class and if a class is referenced by executable code it will be included or assumed included in the final product by the compiler.

However there is nothing stopping you from creating a script that removes all the references to the Assert class in all of your code base before compilation.

Another approach would be to make a test project that targets your application and within JUnit tests actually calls the Assert on the areas which you want to make sure work. I personally like this approach because it is a nice and clean separation of test and application.

If you are just worried about the having an if-statement everywhere, then just wrap Assert with your own class, DebuggableAssert which does that check before each call to Assert.X. It will be sort of less performant because of the method entry/exit and the conditionals but if you can maintain your code better then it might be worth it.

Bechance answered 18/2, 2011 at 16:32 Comment(5)
Okay after reading a bit more about this stuff I decided to use the Java language assert. In the debug version I want to break into the debugger when an assert fails. And I want to have all the asserts disabled in the release version. To get this to work in Eclipse I did the following: Added exception breakpoints to AssertionError (caught and uncaught) in eclipse I turned assertions on by Windows/Preference/Java/Installed JREs/(edit the JRE)/set default arguments to -eaArcane
I must still be missing some step because when I try to test it by causing an assert to fail, nothing happens. The program just runs through the assert. So this makes me believe that the Java language asserts are not really enabled, otherwise either I would break on the exception as intended or if I did not set the breakpoint correctly the uncaught exception would halt the program. Any idea what additional step I am missing? I am running my IDE on Windows. thanksArcane
Oh no I didn't mean to lead you on to using that. I was saying that's how normal java would approach this. I beleive the dex compiler will strip out the assert keyword. Usually the javac program always does unless a flag is enabled. I was just saying why there was going to be the use of the class since you had it in your code and a language keyword would be able to be stripped by the compuler. Sorry wasn't saying use it.Bechance
Actually I did understand you. It just seemed that the simplest and cleanest aproach would be to use the java lang version of assert so that is what I am trying to do. It still leaves me with the problem that I am missing some step or steps that will enable assert.Arcane
Ah ok. Yeah I think Android strips out keyword assert in the runtime. So this solution won't work in Android, at least I don't think.Bechance

© 2022 - 2024 — McMap. All rights reserved.