Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove restrictions on resource directory names. #744

Merged
merged 1 commit into from
Oct 18, 2013

Conversation

roman-mazur
Copy link
Contributor

We can point robolectric to use gradle output directory (with a system property) which does not necessarily have a name ending with /res (like build/res/dev/debug).

if (!resourcePath.resourceBase.toString().endsWith(separator + "res"))
{
throw new IllegalArgumentException("Resource path must end in \"" + separator + "res\"");
if (resourcePath.packageName.equals("android") && !resourcePath.resourceBase.toString().endsWith("/res")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be File.separator + "res"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Android package resources are loaded from a jar. And jars always have "/" separator.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, whoops. I didn't even look at that package name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the android resource path should never end in anything other than res if it is indeed an android jar. This check was mostly due to app resources declared by gradle that didn't end in res. maybe the whole check can be removed now.

@JurgenCruz
Copy link
Contributor

No, We can't remove the restriction! The restriction has a purpose. The res name MUST end in res to avoid conflicts with something like this:

ProjectWithvaluesInItsName/xml/some.xml

because the project name has values or xml or anything that may confuse robolectric into thinking the folder is a resources folder it will start behaving wrong. enforcing the folder to end in res makes it possible to guarantee this will not happen. See this #648 #647 for more info on this restriction.

@JurgenCruz
Copy link
Contributor

Gradle can have an additional task to copy the resources to a folder ending in res and will make it through the restriction. better to have it that way.

This plugin already copies the resources to a folder ending in res and such passes robolectric restriction:

https://github.com/JCAndKSolutions/android-unit-test

@JakeWharton
Copy link
Member

Ew, that's gross. Forcing people to copy? No thanks. We need a better solution than that.

@roman-mazur
Copy link
Contributor Author

Couldn't we just replace contains condition with endsWith? Wouldn't it be sufficient?

@roman-mazur
Copy link
Contributor Author

Or checking for getBaseName().startsWith(resourceTypeFolderName)

@roman-mazur
Copy link
Contributor Author

I've pushed the change that I believe covers the case described in #648.
I removed DirectoryMatchingFilterTest since the filter does not deal with separators now. Tomorrow I'll add a test illustrating #648.

@JurgenCruz
Copy link
Contributor

I think that directory matching filter is used in more places than just resource loading. changing the logic might affect other parts. its not as simple as that. The directory matching filter is there to filter if ANY part of the path contains a filter. changing the logic will work for the resource loading but as I said. there are more parts of robolectric that use this. I'm not sure how would this affect.

@JurgenCruz
Copy link
Contributor

Maybe a new ResourceDirectoryMatchingFilter that is used only in resource loading. then it would work I guess.

@roman-mazur
Copy link
Contributor Author

I've checked usage before making the change. And you can see also that I made the filter to be package private. Perhaps it's even better to make it an inner class.

@JurgenCruz
Copy link
Contributor

If It is not used anywhere else, then I guess that works and the enforcing can be removed.

@roman-mazur
Copy link
Contributor Author

ok, it seems to be finished now.
Please take a look once more and let me know if anything else is needed to get it merged.

@tokunbo
Copy link

tokunbo commented Oct 10, 2013

Hello all, does anyone know how to get past this in gradle? I'm using Gradle 1.6 from AndroidStudio's android-studio/sdk/tools/templates/gradle/wrapper/gradlew . My build.gradle has

 res {
          srcDir './res'
      }

I know that it's working because if I change it to './blah', I get a bunch of...

 /AndroidManifest.xml:16: error: Error: No resource found that matches the given name (

So as far as I can tell, gradle knows it ends in /res, but yet I keep getting this Ex thrown at me. As far as I can see, my resource dir truly ends in "/res". I don't understand what to put in my build.gradle that will make robolectric happy.

@roman-mazur
Copy link
Contributor Author

@tokunbo It does not depend on you source directory name. Gradle puts all the merged resources to a separate output directory located at build/source/res/$flavorName/$buildType. And, assuming you are using https://github.com/JakeWharton/gradle-android-test-plugin , robolectric is instructed to use that directory in order to pick resources.
And this PR is to change Robolectric behavior so that it can use such locations.

@tokunbo
Copy link

tokunbo commented Oct 10, 2013

Oh... so there's no way for me to solve this now other than to wait for this pull request to make it into a new robolectric.jar at https://oss.sonatype.org/index.html#nexus-search;quick~org.robolectric ? Can I alter gradle's output directory to something that robolectric-2.2.jar would be happy with? ...even risking the conflicts that SuperJugy talks about?

EDIT : I'm trying to change the source code of the plugin to output to a dir that this robolectric-2.2.jar is happy with.

@JurgenCruz
Copy link
Contributor

You can use my gradle plugin and it will copy the resources to a dir that ends in res. https://github.com/JCAndKSolutions/android-unit-test

@tokunbo
Copy link

tokunbo commented Oct 10, 2013

I kinda want to be on robolectric-2.2, which I'm told uses PhoneWindow allowing ActionBarSherlock to work easily.
But, I am looking through your plugin's code to see where the copying happens and if I can modify the squareup version to do the same.

@JakeWharton
Copy link
Member

Needs rebase.

@tokunbo
Copy link

tokunbo commented Oct 10, 2013

The only context I know of in regards to "rebase", is "git rebase". :-)

Is this a hint on what I need to change to make squareup plugin's do the copy to "/res"? ...or is this advice that what I'm trying to do is a lot more work than some quick hack I'm trying to pull off? :-)

@JakeWharton
Copy link
Member

Copying to a folder ending in /res is dumb. It's needless arbitrary restriction we must remove.

Rebase was towards the PR author, not you.

@tokunbo
Copy link

tokunbo commented Oct 10, 2013

Oh dear, okay... I'll slowly walk backwards out of this debate ^_^;
In the meanwhile, since I want Robolectric 2.2... I'll just fiddle with the source code until I figure it out, or an agreement is made and robolectric2.3.+ has this all ....squared away ;)

@tokunbo
Copy link

tokunbo commented Oct 11, 2013

Okay, I did THEE DIRTIEST hack ever. Everyone on this thread will hate me for it, but I'm posting it here for anyone else who is foolish enough to do what I just did.

  • First, I git cloned https://github.com/JakeWharton/gradle-android-test-plugin

  • Then I modified src/main/groovy/com/squareup/gradle/android/AndroidTestPlugin.groovy such that the line of code that had "testRunTask.systemProperties.put('android.resources', processedResourcesPath)" ......is now showing...
    testRunTask.systemProperties.put('android.resources', '/home/toks/theresources/res')

  • Then, I did "./gradlew clean install" to install this plugin locally.

  • Then, I compiled my project normally so it'd failed on the /res requirement.

  • Then manually copied the resulting contents of $MYprojectDir/build/res/all/debug to /home/toks/theresources/res

  • Then I changed my build.gradle file to use the local copy of the plugin. The build.gradle of the JakeWharton plugin shows the following info: group = 'com.squareup.gradle' version = '0.9.1-SNAPSHOT' , So in the build.gradle of my project I did this stuff... notice I commented out the 0.9.+ version and told it to get the SNAPSHOT version that's local on my machine.

    buildscript {
           repositories {
               mavenCentral()
               mavenLocal()
           }
           dependencies {
               classpath 'com.android.tools.build:gradle:0.5.6'
               //classpath 'com.squareup.gradle:gradle-android-test-plugin:0.9.+'
               classpath 'com.squareup.gradle:gradle-android-test-plugin:0.9.1-SNAPSHOT'
           }
       }
    
  • Then tried running the tests again, this time I was able to pass the /res requirement, so now I can just keep on writing my tests. ^_^;;; But now....

    about_activity = new AboutActivity();
    shadowOf(about_activity).callOnCreate(null);

Caused by: java.lang.IllegalStateException: System services not available to Activities before onCreate()
at android.app.Activity.getSystemService(Activity.java:4410)
at android.view.LayoutInflater.from(LayoutInflater.java:210)
at org.robolectric.shadows.ShadowActivity.getLayoutInflater(ShadowActivity.java:258)
at android.app.Activity.getLayoutInflater(Activity.java)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:185)
at com.rackspace.cloudmobile.ui.FlurryFragmentActivity.onCreate(FlurryFragmentActivity.java:15)
at com.rackspace.cloudmobile.ui.AboutActivity.onCreate(AboutActivity.java:19)
at org.robolectric.shadows.ShadowActivity.invokeReflectively(ShadowActivity.java:158)
at org.robolectric.shadows.ShadowActivity.callOnCreate(ShadowActivity.java:103)
at com.rackspace.cloudmobile.ui.AboutActivityTest.testClickBackButton(AboutActivityTest.java:49)

@tokunbo
Copy link

tokunbo commented Oct 11, 2013

Okay again... so I switched to trying...

about_activity = Robolectric.buildActivity(AboutActivity.class).create().get();

But that failed because of ActionBar stuff, but the following stackoverflow got me past that:
http://stackoverflow.com/questions/17993239/getsupportactionbar-returns-null-with-robolectric

So now:

java.lang.RuntimeException: Couldn't find content container view
at com.actionbarsherlock.internal.ActionBarSherlockCompat.generateLayout(ActionBarSherlockCompat.java:1014)
at com.actionbarsherlock.internal.ActionBarSherlockCompat.installDecor(ActionBarSherlockCompat.java:901)
at com.actionbarsherlock.internal.ActionBarSherlockCompat.setContentView(ActionBarSherlockCompat.java:835)
at com.actionbarsherlock.app.SherlockFragmentActivity.setContentView(SherlockFragmentActivity.java:261)

@roman-mazur
Copy link
Contributor Author

I'll rebase as soon as get to the working desk.
On Oct 11, 2013 5:07 AM, "Tokunbo George" notifications@github.com wrote:

Okay again... so I switched to trying...

about_activity =
Robolectric.buildActivity(AboutActivity.class).create().get();

But that failed because of ActionBar stuff, but the following
stackoverflow got me past that:

http://stackoverflow.com/questions/17993239/getsupportactionbar-returns-null-with-robolectric

So now:

java.lang.RuntimeException: Couldn't find content container view
at
com.actionbarsherlock.internal.ActionBarSherlockCompat.generateLayout(ActionBarSherlockCompat.java:1014)
at
com.actionbarsherlock.internal.ActionBarSherlockCompat.installDecor(ActionBarSherlockCompat.java:901)
at
com.actionbarsherlock.internal.ActionBarSherlockCompat.setContentView(ActionBarSherlockCompat.java:835)
at
com.actionbarsherlock.app.SherlockFragmentActivity.setContentView(SherlockFragmentActivity.java:261)


Reply to this email directly or view it on GitHubhttps://github.com//pull/744#issuecomment-26108344
.

…tric to use gradle output directory which does not necessarily have a name ending with /res.

change directories matcher logic

turn directories filter into inner class, add tests for tricky resource path loading

remove path checks depending on file separator

remove all the checks on resources path
@roman-mazur
Copy link
Contributor Author

Rebased and squashed! 💥

@JurgenCruz
Copy link
Contributor

@tokunbo you ended up doing manually what my plugin would have done automatically, lol. The problem with your approach is that if you change your resources, you'll have to manually copy them again. Also, if you have flavors with different resources, you won't be able to handle those cases. My plugin solves all that automatically. Until this PR gets merged and 2.3 released, the only way that I know of to automatically handle robolectric 2.2 with gradle 1.8 is with my plugin or a very big build.gradle file. Even if Robolectric 2.3 is released, I would still have to support copying of files since many people might still use robolectric 2.2. The only other approach would be to create a symlink between a dir that ends in res and the dir that android plugin uses. avoiding the copy completely.

@tokunbo
Copy link

tokunbo commented Oct 11, 2013

@SuperJugy
I don't disagree :) . I just really want to be on Robolectric2.2 and it appears your plugin is for Robolectric-2.1; that's the main reason. My horribly-hacked-mess of a solution works for me because I will never change the resources or have multiple flavors; at least not for a long time. So for the purposes of just writing my unittests and quickly validating my choice of Robolectric, I'm willing to manually update resources on the few times I'll need to. Also, I guess since this PR is in... I assume it won't be too long until 2.3.jar shows up and I just start using that. I can support my disaster-environment until EOY. ^_^;

....but I really need a solution to the Runtime Exception

  java.lang.RuntimeException: Couldn't find content container view at com.actionbarsherlock.internal.ActionBarSherlockCompat.generateLayout(ActionBarSherlockCompat.java:1014)

I can write other unittests for things not needing an Activity, but if this wasn't blocking me... I could write some very useful unittests really fast, throw 'em into JenkinsCI and everything would be great.... especially since the "GUI in JVM" thing is the main selling-point that I talk about ^_^;

So... I guess this issue is going to be closed soon since the actual topic of "/res" has been decided. Where do I open an issue for my RuntimeException? Is this a ActionSherlock issue or Robolectric or a bit of both? ....or I'm doing something wrong?

@tokunbo
Copy link

tokunbo commented Oct 11, 2013

Whoa! Hey there! If I do this:

  auth_activity = Robolectric.buildActivity(AuthenticatorActivity.class).create().start().get();
  auth_activity.findViewById(R.id.fragment_authenticator_log_in).performClick();

....it works! ...but only for one of my activities.... the other activity still gives the container view error.... how odd.
It could be because this failing Activity should only be reached after clicking other buttons on other screens. The AuthenticatorActivity is actually designed to be the very first Activity of the app, so maybe it knows how to start itself up from scratch.

@JurgenCruz
Copy link
Contributor

@tokunbo My plugin works for 2.1 and 2.2 and even 2.3-snapshot of robolectric. the difference between them doesn't mather. The plugin doesn't even need robolectric, all it does is to launch JUnit tests. if the tests happen to be robolectric related that is other thing.

About your problem: What's the full stacktrace? at what point does it crash? in the onCreate, onStart or onResume? (which I think you aren't calling).

You really need to provide more info and in a separate Ticket since this has nothing to do with this PR.

@tokunbo
Copy link

tokunbo commented Oct 11, 2013

@SuperJugy
Yeah, definitely some other ticket for all this. I just don't know where the ticket belongs because I don't know exactly what the problem is. Robolectric or ActionBarSherlock or plugin or what....

Though, good to know that I can use your plugin the moment my current setup becomes a pain and this PR isn't in a useable jar yet.

The code that fails is: Robolectric.buildActivity(AboutActivity.class).create().start().get();

I'm using @RunWith(RobolectricTestRunner.class)

The trace is:

java.lang.RuntimeException: Couldn't find content container view
at com.actionbarsherlock.internal.ActionBarSherlockCompat.generateLayout(ActionBarSherlockCompat.java:1014)
at com.actionbarsherlock.internal.ActionBarSherlockCompat.installDecor(ActionBarSherlockCompat.java:901)
at com.actionbarsherlock.internal.ActionBarSherlockCompat.setContentView(ActionBarSherlockCompat.java:835)
at com.actionbarsherlock.app.SherlockFragmentActivity.setContentView(SherlockFragmentActivity.java:261)
at com.rackspace.cloudmobile.ui.AboutActivity.onCreate(AboutActivity.java:21)
at android.app.Activity.performCreate(Activity.java:5008)
at org.fest.reflect.method.Invoker.invoke(Invoker.java:112)
at org.robolectric.util.ActivityController$1.run(ActivityController.java:116)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:256)
at org.robolectric.util.ActivityController.create(ActivityController.java:111)
at org.robolectric.util.ActivityController.create(ActivityController.java:123)
at com.rackspace.cloudmobile.ui.AboutActivityTest.testClickBackButton(AboutActivityTest.java:47)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:234)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:175)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:80)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:47)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:49)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at $Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:103)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:66)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)

@JakeWharton
Copy link
Member

Ask on the mailing list. Your runtime failure is unrelated to this ticket.

@tokunbo
Copy link

tokunbo commented Oct 11, 2013

Will do.

Okay, that ends my stdout on this thread. Thanks & seee y'all around -^_^-

@roman-mazur
Copy link
Contributor Author

Hasn't anyone taken a look at the PR?

@coreydowning
Copy link
Collaborator

@roman-mazur taking a look now!

coreydowning added a commit that referenced this pull request Oct 18, 2013
Remove restrictions on resource directory names.
@coreydowning coreydowning merged commit c3294c4 into robolectric:master Oct 18, 2013
@jrgonzalezg
Copy link
Contributor

Thanks for this!. It is the first time in a long time that i can run my robolectric tests on the latest snapshot without custom "tricks" :)

@innocarpe
Copy link

@SuperJugy Man, you saved my days 👍 I'm a kinda newbie in Android platform, and I'm digging on this Android studio, gradle, Robolectric(2.2) for more than 1 weeks. and finally, your github project https://github.com/JCAndKSolutions/android-unit-test comes out :)
I followed your guide and I could see 'BUILD SUCCESSFUL' with Robolectric, JUnit tests. Thank you.

@JurgenCruz
Copy link
Contributor

@kwosu87 I'm glad I could help you 😄

@innocarpe
Copy link

@Superjugy Anyway, I have a question.Do you know why testCompile libraries don't show in External Libraries in Android Studio after syncinh with buld.gradle?I still can't figure out why :(

@JurgenCruz
Copy link
Contributor

Oh that's because test compile is defined in my plugin not in android
plugin so android studio won't see it. You can manually add them to the
*.iml file of your project though. Sadly, every synch will undo your
changes. But if you have it under version control you can redo the changes.

I know it's annoying but there is no other easy way currently.

@innocarpe
Copy link

@SuperJugy Oh i see! I'll try i and thank you for the direction!! :)

@roman-mazur roman-mazur deleted the feature/gradle-support branch October 31, 2013 16:26
@paulvi paulvi mentioned this pull request Jun 6, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants