Include resources from Android library project

2,672 views
Skip to first unread message

Oliver

unread,
Aug 18, 2011, 5:30:13 AM8/18/11
to Robolectric
I am trying to test a project that is based on an Android library
project [1]. Both the project itself and the included library project
contain resources (drawables, strings and other values) that are
referenced from the project to test.

How can I instruct Robolectric to look for resources in multiple 'res'
directories (i.e., ProjectToTest/res and LibraryProject)? By default,
Robolectric does not seem to be able to locate resources that reside
in the library project.

Thanks,
Oliver


[1] http://developer.android.com/guide/developing/projects/projects-eclipse.html

smallsouldier

unread,
Aug 30, 2011, 9:35:56 AM8/30/11
to Robolectric
I'd be very interested in an answer to this too. Is it possible to use
library project resources out-of-the-box somehow?

Br, Andi

On 18 Aug., 11:30, Oliver <oliver.trach...@gmail.com> wrote:
> I am trying to test a project that is based on an Androidlibrary
> project [1]. Both the project itself and the includedlibraryproject
> containresources(drawables, strings and other values) that are
> referenced from the project to test.
>
> How can I instruct Robolectric to look forresourcesin multiple 'res'
> directories (i.e., ProjectToTest/res and LibraryProject)? By default,
> Robolectric does not seem to be able to locateresourcesthat reside
> in thelibraryproject.
>
> Thanks,
> Oliver
>
> [1]http://developer.android.com/guide/developing/projects/projects-eclip...

Tyler Schultz

unread,
Aug 30, 2011, 10:32:33 AM8/30/11
to robol...@googlegroups.com
Robolectric does not currently support loading resources from library projects. This would not be a trivial change to Robolectric, but I don't see why it couldn't be made to work.

--Tyler

smallsouldier

unread,
Aug 30, 2011, 2:44:22 PM8/30/11
to Robolectric
Ok sorry, didn't see this post...

Moritz Post

unread,
Mar 2, 2012, 10:23:14 AM3/2/12
to robol...@googlegroups.com
Has there been any progress on that subject? I am currently experimenting with the Actionbarsherlock 4.0 which comes a library project.

Currently i can not run any robolectric tests because robolectric is trying to load the resources for the actionbarsherlock from the actionbarsherlock library project. That is correct behaviour but it ends with a RuntimeException Could not find layout layout/abs_screen_simple.

Regards
Moritz Post

Johannes

unread,
Jul 27, 2012, 7:08:58 AM7/27/12
to robol...@googlegroups.com
+1

Aaron VonderHaar

unread,
Jul 27, 2012, 12:15:47 PM7/27/12
to robol...@googlegroups.com

It would be great if someone is able to implement this correctly, but in the meantime, I've worked around this in the past by making a Shadow for the class that is trying to load those library resources so that the resources are no longer required.

--Aaron V.

Phil Plante

unread,
Aug 28, 2012, 4:26:07 PM8/28/12
to robol...@googlegroups.com
Aaron,

I ran into this problem today and started poking around Robolectic's internals to see how I might get a slightly better easier solution than maintaining a bunch of shadow classes.  I came up with this really crude method: https://gist.github.com/3503788  I would love any feedback you or other Pivots might have regarding this.  I haven't submitted a pull request because I am not entirely sure this is such a great idea yet.

Best,
Phil

Aaron VonderHaar

unread,
Aug 30, 2012, 3:28:17 AM8/30/12
to robol...@googlegroups.com
Without having a chance to try it out, that looks pretty nice! It'd
probably be worth adding
Robolectric.loadLibraryProjectResources(String) as a shortcut. Not
being too familiar with the internals of ResourceLoader, I wonder what
would happen if the library has resources that conflict with the
app's, and there are probably some other edge cases to consider--but
the code looks simple enough, and if you're actually using it on a
real app, it's probably worth merging in. My only other question
would be about the lifecycle of ResourceLoader--is there a new one for
every test, or is it cached between tests somehow?

--Aaron
--
--Aaron V.
[ http://github.com/avh4 ]

Phil Plante

unread,
Aug 30, 2012, 3:22:44 PM8/30/12
to robol...@googlegroups.com
I am indeed using it in an active project.  There doesn't seem to be any negative side effects at this point.

As for resource loading, that is actually done on every each test execution, at least from what I could tell.  There could be a caching layer I wasn't aware of somewhere though.

Hopefully it continues working.

Christopher Perry

unread,
Aug 30, 2012, 5:27:56 PM8/30/12
to robol...@googlegroups.com
Hey, some familiar faces!

I just ran into this today when I included Jake Wharton's ViewPagerIndicator library project. Thanks for the workaround Phil.

Johannes

unread,
Sep 4, 2012, 12:01:34 PM9/4/12
to robol...@googlegroups.com
Hey!

I like your approach. I tried to do the same, but Robolectric it doesn't seem to work.
I have a library project which has its own strings file. 

When I write tests for my main project it executes code in the library project and tries to load strings, but fails.

I implement your approach, it loads the resources (So it seems at least), but when it tries to get them the returned strings are null.

Christopher Perry

unread,
Sep 4, 2012, 2:05:37 PM9/4/12
to robol...@googlegroups.com
I'm having a similar issue. It appears that the convertRawValue(String) method in IntegerResourceLoader in Robolectric throws a NumberFormatException when it gets hex color values, i.e. 0xFF000000. 

I tried to catch the exception, then use Integer.decode, but it also throws a NumberFormatException:

Christopher Perry

unread,
Sep 4, 2012, 5:13:43 PM9/4/12
to robol...@googlegroups.com
My solution was to use Long.decode(String) to parse the value because it's larger than Integer.MAX_VALUE.

Phil Plante

unread,
Sep 4, 2012, 5:14:59 PM9/4/12
to robol...@googlegroups.com
I haven't used this for strings.xml values, only layouts/drawables.  Johannes, are you adding this to Robolectric core?

Phil Plante

unread,
Sep 6, 2012, 6:58:24 PM9/6/12
to robol...@googlegroups.com
I ran into a somewhat interesting problem today with this hack.  I have a library project as a git submodule which does not include all of the possible directories in res.  So when it looks for res/values/ a RuntimeException is thrown in ResourceLoader.getValueResourceDir:255 because the dir does not exist.

Anyone have any thoughts regarding this?  I can add the directories, but it seems thats sort of a pain to create empty directories rather than making the code more resilient?


On Tuesday, September 4, 2012 2:15:00 PM UTC-7, Phil Plante wrote:
I haven't used this for strings.xml values, only layouts/drawables.  Johannes, are you adding this to Robolectric core?

Johannes

unread,
Sep 7, 2012, 8:49:26 AM9/7/12
to robol...@googlegroups.com
I resolved my issue.

I couldn't get the resources or let's say the right resources, because there where conflicts with the ids.

When I wanted to get a string from the libray project it returned another string from the main project. Somehow it had a duplicate id.

I googled a bit and found another way of getting resource ids. So instead of writing:

int resourceId = com.my.package.R.string.some_text;
getString(resourceId); 
 
I get the strings by calling this.

int resourceId = getResources().getIdentifier("some_text","string","com.my.package")
getString(resourceId); 
 
And then it gets the right id and everything works fine. :)
 

On Friday, September 7, 2012 12:58:24 AM UTC+2, Phil Plante wrote:
I ran into a somewhat interesting problem today with this hack.  I have a library project as a git submodule which does not include all of the possible directories in res.  So when it looks for res/values/ a RuntimeException is thrown in ResourceLoader.getValueResourceDir:255 because the dir does not exist.

Anyone have any thoughts regarding this?  I can add the directories, but it seems thats sort of a pain to create empty directories rather than making the code more resilient?

On Tuesday, September 4, 2012 2:15:00 PM UTC-7, Phil Plante wrote:
I haven't used this for strings.xml values, only layouts/drawables.  Johannes, are you adding this to Robolectric core?

Tyler Schultz

unread,
Sep 7, 2012, 9:42:45 AM9/7/12
to robol...@googlegroups.com
Hi Phil, 
Agreed, this has long been a problem. Robolectric should be detecting when the directories are not present instead of blowing up.

--Tyler
Message has been deleted

Christopher Perry

unread,
Sep 8, 2012, 12:28:17 AM9/8/12
to robol...@googlegroups.com
You shouldn't have to code around your testing framework. It's a better idea to make Robolectric work correctly. 

Matt Wolfe

unread,
Dec 1, 2012, 5:42:50 PM12/1/12
to robol...@googlegroups.com
I would love to see this feature implemented into Robolectric as well. I have several projects in need of good testing and Robolectric seems the best, however these projects all use a shared libarary. I am thinking of ways to work around the issue but haven't come up with anything that I feel is acceptable.

Tyler Schultz

unread,
Dec 1, 2012, 6:31:35 PM12/1/12
to robol...@googlegroups.com
Phil Plante made a gist a while back: https://gist.github.com/3503788

This is crude and manual, but it's a start. This could be improved by the parsing manifests instead of manually configuring.

--Tyler

Matt Wolfe

unread,
Dec 2, 2012, 2:06:49 AM12/2/12
to robol...@googlegroups.com
It seems to me like this will only work if your application project references resources defined in your library project. While my project does that, I also reference resources in my library project that are defined in my library project. I am pretty sure these will not work using the method provided. For example, lets take the following setup:
you have application com.example.app and library of com.example.lib.

Each of these projects will have its own android generated R class (com.example.lib.R). However, the application project will have an R class and the resource definition ID's won't line up with the corresponding one that is generated for the library project. It looks like android system generates all of the resource identifiers from the library projects into your application project, however the values aren't the same. 
As an example, in my lib project i have a String resource named
error_file_not_found. In the Library project's R class, it has an id of 0x7f040058. The value does not exist in the xml resources of my application project, but yet its definition does exist in the com.example.app.R class, and its value is 0x7f050058. I'm not sure if it's a coincidence but all the ID's in my com.example.app.R class are exactly 0x10000 (65536 decimal) greater than the corresponding definition in the com.example.lib.R class. When I debug the code being run from Robolectric's jUnit test runner, the value defined in com.example.lib.R is the one passed to resources.getString(R.string.something). However, when debugging the actual application running, the value of the Resource ID is the one that gets defined in the application project's R class as opposed to the one in the library project (which is what I would expect).

The Robolectric resource loader fails to find this definition though and returns null. Even if the resources had been loaded it wouldn't have worked though because the values wouldn't line up (the ID wouldn't map to the name). 

This seems like a pretty tough problem to solve. If we at least pull in the library resources as mentioned that would help. Unfortunately the android manifest is not where library project references are defined (maybe in the past).. What i've seen is that the library projects are defined in the project.properties file in the application root folder so we would need to look there.

Once we have the resources it's just a matter of getting the mapping right. I'm not sure that this 0x1000 increment is useful but perhaps if no definition is found we could check for a resource definition 0x1000 greater. Seems like quite a hack and it may be specific to the version of the android sdk tools installed though (or may break in the future).

An alternate solution may be to simply not use the standard resource loader from the library project and instead use another that we pass in a string value rather than an ID. This however has the downside that we lose out on static type analysis done here and could end up trying to reference resources that weren't defined (if we don't test thoroughly enough!).


On Thursday, August 18, 2011 2:30:13 AM UTC-7, Oliver wrote:

Matt Wolfe

unread,
Dec 7, 2012, 1:39:34 PM12/7/12
to robol...@googlegroups.com
Ok so I attempted an implementation that would load all library projects that were found. LIbrary projects are defined in property files (project.properties is where android stores them but perhaps that could be manipulated by your build system). However the problem is that I don't know of a good way at runtime to differentiate between the ID's that are being looked up from within a library project and those being looked up from within your application project. 
Does anyone have any good ideas for a solution for this? I'm willing to put in the work I just can't seem to come up with a good solution.

I've though of:
1. Load application projects R classes/resources. For each name in the library R class, if it also exists in the application R class, map the library R class value to the application project R value. 
For all calls that take a resource ID, Interrogate the stack to find the caller, to determine if caller came from the application project or a  library project. We also have to figure out which library project they came from so we can figure out which value the input ID maps to. This would be slow (definitely would need to be cached) and would be pretty error prone since the stack level to search will change as the projects code changes. Even then, I'm not totally sure how to determine what code is from a library project and what is from the main project.  Does this seem reasonable? Has anyone done something like this before?

2. Somehow doing a runtime replacement of the R class being used in a library project. Replace with the values from the application project. Not sure if this is possible/feasible.


3. As someone else mentioned, just do the lookup by name within the library projects. This is less than ideal because you may be working with someone elses library project who hasn't followed the convention.

4. Perhaps instead of leaving it all up the the Robolectric framework, we implement some kind of shadow resource loader that uses the idea from step 1 except we explicitly tell the resource extractor if we are doing the lookup from a library project (and if so which project).. Anyone have input here?

5. There must be someone else smarter than me that has an idea. Let me know what it is and i'll implement the dirty work so long as it's no more than couple days of work.

On Thursday, August 18, 2011 2:30:13 AM UTC-7, Oliver wrote:
Message has been deleted

Christopher Perry

unread,
Dec 20, 2012, 6:12:08 PM12/20/12
to robol...@googlegroups.com
I wrote a test runner that loads library resources (using Phil's solution) with a custom annotation. Here's the gist of it:

Reply all
Reply to author
Forward
0 new messages