How to find Robolectric version at runtime? - android

Is there a way to retrieve Robolectric's version at runtime? My environment is under controlled library versioning and would like to make sure I run a certain test only if Robolectric has an specific version (for example, to avoid runtime known issues with it). For example:
#Test
public void should_do_x() {
if (Robolectric.version >= supported_version) {
//do something
//validate something
}
}

Related

Filtering Android Connected Tests

I have a few "connected" tests that are only relevant to run on a specific device model or on a specific brand and should be skipped on other brands/models.
I may be missing something, but this kind of filtering seems not possible out-of-the-box with AndroidJUnitRunner (by using annotation and/or passing appropriate arguments to it).
So, I was thinking to extend the AndroidX test framework to support this kind of filtering. In the end, I would like to be able to filter test with something like this
#TargetDeviceFilter(brand="SAMSUNG",model="XCover3")
#Test
public void myTestToRunOnSamsungXCover3DeviceOnly(){
...
}
My question: is there any way to accomplish this kind of filtering without extending AndroidX test framework? And if writing my own AndroidJUnitRunner and/or my own annotations is required, how should I start ?
I found a few interesting base classes that I may need to extend like :
androidx.test.internal.runner.TestRequestBuilder
androidx.test.internal.runner.TestRequestBuilder.DeviceBuild
but as those classes are in a "internal" package: attempting to extend them is probably not a good idea?
Any advice on how to deal with that problem is welcome.
I think, you may use org.junit.Assume.
Create a helper class DeviceHelper to detect mobile device informations for convenience.
Your test logic will be executed only if the assumption is correct.
#Test
public void myTestToRunOnSamsungXCover3DeviceOnly() {
// adapt this part to your business need
org.junit.Assume.assumeTrue(
DeviceHelper.isBrand("SAMSUNG") &&
DeviceHelper.isModel("XCover3")
);
// i.e. you can filter whatever you want test's according to device sdk_int
assumeTrue(SomeHelper.getDeviceSdk() >= 21);
// your test code
}

JUnit4 parameterized test with different Android SDK versions

I'd like to test a function having a conditional statement for different Android SDK build versions with some parameters. The function looks like:
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) {
// do something with parameter(s) and return it;
} else {
// do something with parameter(s) and return it;
}
I can use Parameterized.class runner or JUnitParams to get it run with a set of parameters. I can also create a single test function and can change the SDK_INT field and call the function to test. But I was not able to combine both approaches into the same unit test class.
Is there any annotation to set the SDK for a particular test method? Or what is the best approach to test functions like the above?
A simple yet not elegant solution is to use the Enclosed runner and inner classes changing the SDK_INT field in a function annotated with #BeforeClass.

Is it safe to use #SuppressLint("RestrictedApi") with AndroidX libraries?

I would like to now whether it is safe or not to use #SuppressLint("RestrictedApi") annotation. I am pretty sure that the answer is NO, so I want to ask why as well.
I guess that the development team wanted to hide such restricted code from the API users. Probably due to changes in the future or because the code is intended to work as internal functionality
Example code with androidx.preference:preference:1.1.1:
public abstract class CustomAdapterPreferenceFragment extends PreferenceFragmentCompat {
#Override
protected PreferenceGroupAdapter onCreateAdapter(PreferenceScreen preferenceScreen) {
// this annotation removes warning that says PreferenceGroupAdapter can only be called from package names that start with androidx.preference
#SuppressLint("RestrictedApi")
final PreferenceGroupAdapter adapter = new PreferenceGroupAdapter(preferenceScreen) {
#Override
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
}
};
return adapter;
}
Link to annotation that restricts code usage in this example: AOSP AndroidX
You're correct: this is not safe. Symbols marked with #RestrictTo are not considered public API, and may change behavior or signature arbitrarily between releases. The only guarantee is that they won't break behavior that other AndroidX libraries rely on internally, and what that behavior is is not defined.
This is especially true for symbols that restrict to a single library or a library group that requires all libraries within the group to be pinned to the same version, as there is no need to maintain compatibility with different versions of other AndroidX libraries.
I guess that the development team wanted to hide such restricted code from the API users. Probably due to changes in the future or because the code is intended to work as internal functionality
This is exactly correct. There is sometimes a need to expose functionality internally that would not make sense as public API, though we try to avoid it in general, because it makes it harder to copy a part of the code out and customize your own version of it. This is especially true with Java code, which doesn't have Kotlin's internal modifier to expose classes to the entire library (package-private doesn't really cut it).

How to force Jacoco to use a specific version in Gradle

Based on the release notes in version 0.8.3 the not-null assertion operator is filtered out, I'm using Jacoco version 0.8.5 like this:
jacoco {
toolVersion = "0.8.5"
}
But it's telling me that Not covered by tests (8 conditions)
I'm using com.dicedmelon.gradle:jacoco-android Github link
I think toolVersion = "0.8.5" not working or something like that, so for that I need a way to force Jacoco version.
Is there any way to fix this issue?
Without seeing your code and your tests I can't say with 100% confidence but it looks like Jacoco is working fine and there are cases not covered there.
You're using the !! 3 times. When you use this operator, in reality, you're creating 2 flows, for when the variable is null and another for not-null. If you add tests for the cases where the variables are null, you should reach 100% coverage.
Just to make explicit, if you would handle your nullable with safe calls, you would have something like this:
val token = authResult.user?.let {
authenticationDataSource.getIdToken(true, it)
?.let { it.token }
?: throw GetIdTokenResultIsNullException()
} ?: throw UserIsNullException()
token?.let {
authenticationDataSource.loginBy(AuthenticationPayload(it))
} ?: throw TokenIsNullException()
Wherever, I'm throwing exceptions you should handle that case as desired, and this is the alternate branch that is created by the nullability.
If you're sure that your values won't be null then you should change your types to make it clear and avoid extra checks.
On a side note, jacoco-android doesn't seem to be maintained anymore here and it's not compatible with newer gradle versions, so I would recommend using Jacoco directly. Migrating from jacoco-android to jacoco shouldn't be that hard.

Android free/paid app using compiler trick

I was wondering whether you could use a trick of the compiler to include different functions for a free and paid version of the app. For instance:
public static final boolean paid = false;
if (paid){
runPaidMethod();
}
else {
runFreeMethod();
}
The compiler will look at that and say that it doesn't need the first branch of the if statement so it won't compile it. Furthermore, it should look at the program and see that runPaidMethod() is no longer referenced from anywhere and remove it.
Therefore the question is: is it feasible to just have this flag, compile it once for free, swap the flag then compile it again for paid?
Using a final boolean variable is good because the Java compiler is smart enough to see that your condition is always false. If you decompile the compiled class (you can try it, with the javap -c command) you will see that your code :
public static final boolean paid = false;
if (paid) {
runPaidMethod();
}
else {
runFreeMethod();
}
will be compiled to a single call to :
runFreeMethod();
The compiler removes any unreachable code, so nobody will be able to reverse engineer your app. But be careful, you have to declare runPaidMethod() as a private method, or its content will still appear in the compiled class.
However from a maintenance point of view, it is still better to use Library Projects to handle multiple app versions.
The concept you are trying to express is known as conditional compilation. In a language like C or C++ you would accomplish this with a combination of preprocessor directives and compiler flags. A rather crude example:
#ifdef PAID
runPaidMethod();
#else
runFreeMethod();
#endif
Good, bad or indifferent, this sort of conditional compilation does not exist in Java. But thats not too say what you are trying to do cannot be accomplished, you just need to think in a more object oriented fashion. One such way of implementing what you are seeking would be to define your major service providers as interfaces, and provide implementations for the paid and free versions. Something like:
public interface UsefulService {
public void someMethod();
public void otherMethod();
}
public class BaseUsefulService {
// Common functionality here
public void otherMethod() {
}
}
public class FreeUsefulService {
public void someMethod() {
}
}
public class PaidUsefulService {
public void someMethod() {
}
}
With this kind of breakdown you can actually build the paid version of the application into an entirely separate application (by putting all its service providers in a separate project).

Categories

Resources