I want to use the Assert keyword in my android apps to destroy my app in some cases on the emulator, or my device during testing. Is this possible?
It seems that the emulator just ignores my asserts.
I want to use the Assert keyword in my android apps to destroy my app in some cases on the emulator, or my device during testing. Is this possible?
It seems that the emulator just ignores my asserts.
The API provides the JUnit Assert.
You can do
import static junit.framework.Assert.*;
now you can use all the functions like assertTrue, assertEquals, assertNull that are provided in the junit framework.
Be careful not to import the Junit4 framework through eclipse, that would be the org.junit package. You have to use the junit.framework package to get it working on an android device or the emulator.
See the Embedded VM Control document (raw HTML from the source tree, or a nicely formatted copy).
Basically, the Dalvik VM is set to ignore assertion checks by default, even though the .dex byte code includes the code to perform the check. Checking assertions is turned on in one of two ways:
(1) by setting the system property "debug.assert" via:
adb shell setprop debug.assert 1
which I verified works as intended as long as you reinstall your app after doing this, or
(2) by sending the command line argument "--enable-assert" to the dalvik VM which might not be something app developers are likely to be able to do (somebody correct me if I'm wrong here).
Basically, there is a flag that can be set either globally, at a package level, or at a class level which enables assertions at that respective level. The flag is off by default, as a result of which the assertion checks are skipped.
I wrote the following code in my sample Activity:
public class AssertActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int x = 2 + 3;
assert x == 4;
}
}
For this code, the dalvik byte code that is generated is (for Android 2.3.3):
// Static constructor for the class
000318: |[000318] com.example.asserttest.AssertActivity.:()V
000328: 1c00 0300 |0000: const-class v0, Lcom/example/asserttest/AssertActivity; // class@0003
00032c: 6e10 0c00 0000 |0002: invoke-virtual {v0}, Ljava/lang/Class;.desiredAssertionStatus:()Z // method@000c
000332: 0a00 |0005: move-result v0
000334: 3900 0600 |0006: if-nez v0, 000c // +0006
000338: 1210 |0008: const/4 v0, #int 1 // #1
00033a: 6a00 0000 |0009: sput-boolean v0, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000
00033e: 0e00 |000b: return-void
000340: 1200 |000c: const/4 v0, #int 0 // #0
000342: 28fc |000d: goto 0009 // -0004
:
:
// onCreate()
00035c: |[00035c] com.example.asserttest.AssertActivity.onCreate:(Landroid/os/Bundle;)V
00036c: 6f20 0100 3200 |0000: invoke-super {v2, v3}, Landroid/app/Activity;.onCreate:(Landroid/os/Bundle;)V // method@0001
000372: 1501 037f |0003: const/high16 v1, #int 2130903040 // #7f03
000376: 6e20 0500 1200 |0005: invoke-virtual {v2, v1}, Lcom/example/asserttest/AssertActivity;.setContentView:(I)V // method@0005
00037c: 1250 |0008: const/4 v0, #int 5 // #5
00037e: 6301 0000 |0009: sget-boolean v1, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000
000382: 3901 0b00 |000b: if-nez v1, 0016 // +000b
000386: 1251 |000d: const/4 v1, #int 5 // #5
000388: 3210 0800 |000e: if-eq v0, v1, 0016 // +0008
00038c: 2201 0c00 |0010: new-instance v1, Ljava/lang/AssertionError; // class@000c
000390: 7010 0b00 0100 |0012: invoke-direct {v1}, Ljava/lang/AssertionError;.:()V // method@000b
000396: 2701 |0015: throw v1
000398: 0e00 |0016: return-void
Notice how the static constructor invokes the method desiredAssertionStatus on the Class object and sets the class-wide variable $assertionsDisabled; also notice that in onCreate(), all of the code to throw java.lang.AssertionError is compiled in, but its execution is contingent upon the value of $assertionsDisabled which is set for the Class object in the static constructor.
It appears that JUnit's Assert class is what is used predominantly, so it is likely a safe bet to use that. The flexibility of the assert keyword is the ability to turn on assertions at development time and turn them off for shipping bits and instead fail gracefully.
import static junit.framework.Assert.*
and then I use one of its methods, like assertNotNull("It's null!", someObject);
, is this assertion turned off in shipping bits? –
Transpontine adb shell setprop debug.assert 1
in Eclipse? –
Stodge su
, then setprop debug.assert 1
. Note that the code that you show disassembled will stay in a release build (https://mcmap.net/q/242671/-better-way-to-do-debug-only-assert-code). I don't believe the javac compiler can be told not to emit assertions so they need to be stripped-out somehow. A simple solution to that is to wrap the keyword assert in your own function that proguard can strip for you. –
Excogitate When assertions are enabled, the assert
keyword simply throws an AssertionError
when the boolean expression is false
.
So IMO, the best alternative, esp. if you're averse to depend on junit, is to throw an AssertionError
explicitly as shown below:
assert x == 0 : "x = " + x;
An alternative to the above statement is:
Utils._assert(x == 0, "x = " + x);
Where the method is defined as:
public static void _assert(boolean condition, String message) {
if (!condition) {
throw new AssertionError(message);
}
}
The Oracle java docs recommend throwing an AssertionError
as an acceptable alternative.
I guess you can configure Proguard to strip out these calls for production code.
In "Android in Practice" it is suggested to use:
$adb shell setprop dalvik.vm.enableassertions all
if this settings is not persisted on your phone then you can create /data/local.prop file with properties like:
dalvik.vm.enableassertions=all
chmod 644
). –
Adamsen It was bugging the hell out of me, that my assertions didnt work, until I checked the issue out on google... I gave up on simple assertions and will go with junits assertion methods.
For convenience purposes I am using:
import static junit.framework.Assert.*;
Due to the static import I can later write:
assertTrue(...); instead of Assert.assertTrue(...);
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 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.
-assumenosideeffects class junit.framework.Assert { *; }
–
Aboutship You can use assertions, but it takes some work to use them reliably. System property debug.assert
is unreliable; see issues 175697, 65183, 36786 and 17324.
One method is to translate each assert
statement to something any runtime can deal with. Do this with a source preprocessor in front of the Java compiler. For example, take this statement:
assert x == 0: "Failure message";
For a debug build, your preprocessor would translate the above to an if
statement:
{ if( !(x == 0) ) throw new AssertionError( "Failure message" ); }
For a production build, to an empty statement:
;
Note that this would control assertions at build time, as opposed to run time (the usual practice).
I could find no ready-made preprocessor, so I scripted one. See the part dealing with assertions. Licence to copy is here.
To add to Zulaxia's answer on stripping out Junit - Proguard is already part of Android SDK /Eclipse and the following page tells you how to enable it.
http://developer.android.com/guide/developing/tools/proguard.html
Also the above wont work with the latest default proguard configuration because it uses the -dontoptimize flag which must be taken out and some of the optimizations turned on.
Use standard Java assert keyword, for example:
assert a==b;
For this to work, you have to add one line to /system/build.prop, and reboot phone:
debug.assert=1
This would work on rooted phone. Use some file manager capable to edit build.prop (e.g. X-plore).
Pluses: most (all?) Android phones ship with assertions disabled. Even if your code accidentally asserts to false, app won't interrupt or crash. However, on your development device you'll get assertion exception.
The API provides the JUnit Assert.
You can do
import static junit.framework.Assert.*;
now you can use all the functions like assertTrue, assertEquals, assertNull that are provided in the junit framework.
Be careful not to import the Junit4 framework through eclipse, that would be the org.junit package. You have to use the junit.framework package to get it working on an android device or the emulator.
© 2022 - 2024 — McMap. All rights reserved.