Closed dimrsilva closed 2 weeks ago
I'm experiencing the same issue on Mac OS Sonoma 14.1.1 and Robolectric 4.11.1.
Issue persists with Robolectric 4.12.1
Interestingly, this test only fails when executed on command line, when executed in the IDE things work for me. This is unrelated whether all tests or just a single test (via --tests
on command line) are executed.
This is a more complete stack trace btw.
android.view.InflateException: Binary XML file line #14 in my.app:layout/dialog_loading_with_msg: Binary XML file line #14 in my.app:layout/dialog_loading_with_msg: Error inflating class android.widget.ProgressBar
Suppressed: org.robolectric.android.internal.AndroidTestEnvironment$UnExecutedRunnablesException: Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(Looper.getMainLooper()).idle() call.
Caused by: android.view.InflateException: Binary XML file line #14 in my.app:layout/dialog_loading_with_msg: Error inflating class android.widget.ProgressBar
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at android.view.LayoutInflater.$$robo$$android_view_LayoutInflater$createView(LayoutInflater.java:858)
at android.view.LayoutInflater.createView(LayoutInflater.java)
...
Caused by: android.content.res.Resources$NotFoundException: Drawable android:drawable/progress_medium_material with resource ID #0x1080725
Caused by: java.lang.IllegalArgumentException: The Path must start at (0,0) and end at (1,1)
at android.view.animation.PathInterpolator.initPath(PathInterpolator.java:168)
at android.view.animation.PathInterpolator.parseInterpolatorFromTypeArray(PathInterpolator.java:119)
at android.view.animation.PathInterpolator.__constructor__(PathInterpolator.java:104)
at android.view.animation.PathInterpolator.<init>(PathInterpolator.java:97)
at android.view.animation.AnimationUtils.createInterpolatorFromXml(AnimationUtils.java:429)
at android.view.animation.AnimationUtils.loadInterpolator(AnimationUtils.java:372)
at android.animation.AnimatorInflater.loadAnimator(AnimatorInflater.java:1057)
at android.animation.AnimatorInflater.loadObjectAnimator(AnimatorInflater.java:1011)
at android.animation.AnimatorInflater.createAnimatorFromXml(AnimatorInflater.java:667)
at android.animation.AnimatorInflater.createAnimatorFromXml(AnimatorInflater.java:680)
at android.animation.AnimatorInflater.createAnimatorFromXml(AnimatorInflater.java:642)
at android.animation.AnimatorInflater.loadAnimator(AnimatorInflater.java:126)
at android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState$PendingAnimator.newInstance(AnimatedVectorDrawable.java:888)
at android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState.inflatePendingAnimators(AnimatedVectorDrawable.java:864)
at android.graphics.drawable.AnimatedVectorDrawable.applyTheme(AnimatedVectorDrawable.java:683)
at android.graphics.drawable.DrawableContainer$DrawableContainerState.applyTheme(DrawableContainer.java:983)
at android.graphics.drawable.DrawableContainer.applyTheme(DrawableContainer.java:630)
at com.android.internal.graphics.drawable.AnimationScaleListDrawable.applyTheme(AnimationScaleListDrawable.java:240)
at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:683)
at jdk.internal.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.robolectric.shadows.ShadowArscResourcesImpl$ResourcesImplReflector$$Reflector25.loadDrawable(Unknown Source)
at org.robolectric.shadows.ShadowArscResourcesImpl.loadDrawable(ShadowArscResourcesImpl.java:169)
at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java)
at android.content.res.Resources.loadDrawable(Resources.java:993)
at android.content.res.TypedArray.getDrawableForDensity(TypedArray.java:1007)
at android.content.res.TypedArray.getDrawable(TypedArray.java:982)
at android.widget.ProgressBar.__constructor__(ProgressBar.java:322)
at android.widget.ProgressBar.<init>(ProgressBar.java:274)
at android.widget.ProgressBar.<init>(ProgressBar.java:270)
at android.widget.ProgressBar.<init>(ProgressBar.java:266)
Issue seems to be the last path component, it's (1.1, 1) instead of (1,1).
I'd say the path.approximate(PRECISION)
returns (implementation-wise) two completely different values. The input path string is always C0.2,0 0.1,1 0.5, 1 L 1,1
, so when this Path is read via XML property, this results in the following (errornous) point list (22 points in total, all with x,y,z coordinates):
[0.0, 0.0, 0.0, 0.001366025, 0.0, 0.0028686523, 0.0053478424, 0.0, 0.011230469, 0.011771066, 0.0, 0.024719238, 0.02046131, 0.0, 0.04296875, 0.031244189, 0.0, 0.06561279, 0.043945316, 0.0, 0.092285156, 0.07440477, 0.0, 0.15625, 0.110444576, 0.0, 0.2319336, 0.15066965, 0.0, 0.31640625, 0.23809525, 0.0, 0.5, 0.32552084, 0.0, 0.68359375, 0.36574593, 0.0, 0.7680664, 0.40178573, 0.0, 0.84375, 0.4322452, 0.0, 0.90771484, 0.44494632, 0.0, 0.9343872, 0.4557292, 0.0, 0.95703125, 0.46441942, 0.0, 0.97528076, 0.47084266, 0.0, 0.98876953, 0.47482446, 0.0, 0.99713135, 0.4761905, 0.0, 1.0, 1.0, 1.1, 1.0]
but if one manually parses the string by the same means, i.e.
val path = PathParser.createPathFromPathData("C0.2,0 0.1,1 0.5, 1 L 1,1")
PathInterpolator(path)
one receives the following valid point list (23 points in total):
[0.0, 0.0, 0.0, 0.0106859375, 0.017895509, 0.0028686523, 0.021478953, 0.034179688, 0.011230469, 0.033293966, 0.048999026, 0.024719238, 0.046678346, 0.0625, 0.04296875, 0.06188002, 0.0748291, 0.06561279, 0.07896006, 0.08613282, 0.092285156, 0.1184951, 0.10625, 0.15625, 0.16433229, 0.12402344, 0.2319336, 0.2150903, 0.140625, 0.31640625, 0.32521868, 0.17500001, 0.5, 0.3814707, 0.19511719, 0.5932617, 0.43652323, 0.21875, 0.68359375, 0.48905304, 0.24707031, 0.7680664, 0.5380158, 0.28125, 0.84375, 0.58287925, 0.32246095, 0.90771484, 0.6038808, 0.34606934, 0.9343872, 0.624123, 0.371875, 0.95703125, 0.6439027, 0.4000244, 0.97528076, 0.66364104, 0.43066406, 0.98876953, 0.68387085, 0.46394044, 0.99713135, 0.7051988, 0.5, 1.0, 1.0, 1.0, 1.0]
This is even in the same (debugged) test run started via Gradle. I have no idea what's going on here.
Thanks for the debugging @realdadfish . I wonder if this could be related to reading the path values in binary resources?
I cloned the repo https://github.com/lgmro/Roboeletric_linux_issue and ran gradlew test
but everything passed. I am running Linux 6.6.13-1rodete3-amd64
.
Looks similar to this issue in LayoutLib (which uses the same underlying native libraries): https://stackoverflow.com/questions/67202559/progressbar-in-android-studio-the-path-must-start-at-0-0-and-end-at-1-1
Thanks for the debugging @realdadfish . I wonder if this could be related to reading the path values in binary resources?
That is what bugs me - I got the original string (C0.2,0 0.1,1 0.5, 1 L 1,1
) by debugging through the code, this is the value I copied from the debugger after it was read from the XML file. And that same value des not trigger the issue when I run the path parsing code manually.
I have no idea of the inner workings of the native layoutlib code, eventually you could contact the team internally and ask for help?
@realdadfish yeah we have debugged and fixed these types of issues before. The native code for Path.approximate is here: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android12-hostruntime-dev/libs/hwui/jni/Path.cpp#408. Are you 100% sure the path string is always C0.2,0 0.1,1 0.5, 1 L 1,1
?
@realdadfish is this issue 100% reproducible for you? Can it be reproduced in a place like GitHub CI?
@jgaillard85 from LayoutLib provided a great insight that may be the root cause of this issue. In LayoutLib there is a snippet of code:
// Use English locale for number format to ensure correct parsing of floats when using strtof
setlocale(LC_NUMERIC, "en_US.UTF-8");
If the LC_NUMERIC locale is set to e.g. German, perhaps the comma is interpreted as part of a float.
It looks like PathParser does use strtof: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/hwui/PathParser.cpp;l=103
We should probably set the LC_NUMERIC locale to en_US.UTF-8
in Robolectric native graphics.
Looks like this was the issue indeed:
https://github.com/robolectric/robolectric/actions/runs/8652528011/job/23726339628#step:7:4667
That tests fails when Linux is set to a German locale.
I can make a point release that fixes this issue.
Wow, awesome! Thanks for finding and fixing it! Maybe, just maybe, PathParser should check and output a warning if LC_NUMERIC does not have the expected value? Or, at least, it should document that on the CPP implementation?
Description
The ProgressBar is failing to be inflated when running tests on Linux. The same tests run correctly on Mac OS.
The tests fails with IllegalArgumentException exception when trying to load progress_medium_material drawable.
Steps to Reproduce
Just create a simple activity with a ProgressBar and GraphicsMode NATIVE
Robolectric & Android Version
Robolectric: 4.10.3 Linux Kernel: 5.15.12-051512-generic MacOS (works correctly): Ventura 13.2.1
Link to a public git repo demonstrating the problem:
https://github.com/lgmro/Roboeletric_linux_issue