There are a ton questions regarding getResource or getResourceAsStream returning null and so far I understand the issue but I currently cannot properly solve it.
I have a resource file which is used by some class in production. The file is located in
app\src\main\res\raw\some.def
The class SomeManager uses this to access this file:
InputStream stream = SomeClass.class.getResourceAsStream("/res/raw/some.def");
This succeeds when running the debug variant of the application on the emulator and it also succeeds when running the debug variant of the instrumented tests. I assume because the resource is properly packaged into the jar?
However when I run some local jUnit tests in android studio this resource is not found. I did not fully understand what is exactly executed when running a local test and I am not sure how to provide the resource file in a way that can be loaded in a test.
I would like to avoid doubling this resource file because it is actually something I want to test, I also would like to not change the getResourceAsStream path because this is the production file I want to test.
I am using gradle and android studio if that matters.
I debugged this issue with Sysinternal's Process Monitor and realized that when I run code locally on my machine the resources are as streams are looked up from various different locations on disk. One of those locations is
<build_directory>/intermediate/classes/<build_type> where it is obviously missing.
The solution to this was to create a copy task that performs the copying and make it robust enough to work for all build types.
So I modified my app's gradle file and added those dynamic tasks:
android.buildTypes.all{ theBuildType ->
task "copyDebugAssets_${theBuildType.name}"(type: Copy){
from "${projectDir}/src/main/res"
into "${buildDir}/intermediates/classes/${theBuildType.name}/res"
eachFile { println it.name }
}
}
tasks.whenTaskAdded { task ->
// println "A message which is logged at QUIET level ${task.name}"
if (task.name.startsWith("process") && task.name.endsWith("Resources")) {
def partInBetween = task.name.substring("process".length(), task.name.indexOf("Resources")).toLowerCase()
if (partInBetween == "debugandroidtest") {
return
}
def dependentTask = "copyDebugAssets_${partInBetween}"
println "Registering ${dependentTask} to ${task.name} for config '${partInBetween}'"
task.dependsOn dependentTask
}
}
I have really no idea on how to properly use gradle but the first statement generates as many copyDebugAssets_xxx tasks as there are build types. After syncing you can see and execute the in the gradle projects.
To avoid calling them whenever a clean or rebuild is done manually, the second part registers the copyDebugAssets_xxx copy tasks to the various process<Configuration>Resources tasks, which are then called automatically. So far I can run local unit tests in multiple build type successfully.
Related
Hi I have a tar task that I made after looking at numerous methods and some SO posts.
task buildDist(type: Tar, dependsOn: jar) {
print 'here'
archiveName = 'xyz-' + version
destinationDir = file('build/dist')
extension = 'tar.gz'
compression = Compression.GZIP
from 'build/libs'
include 'xyz.jar'
}
buildDist.mustRunAfter jar
I have the java plugin applied and the jar task makes the xyz.jar file available under build/libs. The build/dist directory does not exist yet, but I tried new File("build/dist") as well. That did not work either - I even pointed it to the build directory that exists - doesn't work. I run the entire script with /gradlew clean build. The print in the above code does print.
I am making a few assumptions here as you didn't post the output from running Gradle.
The build task is just a normal Gradle task that doesn't do anything by itself. Instead, it depends on other tasks. If you create your own custom task and you like to have it included when executing build, you have to add a dependency to it. If this is not the problem and you have actually done this, please give some more details as to what makes it "not work" when you run build.
If you want to test your task in isolation (e.g. to make sure it works correctly without running unit tests or whatever else that is unrelated), just run gradlew cleanBuildDist buildDist.
A note about the 'print' statement - it executes doing the configuration phase, but this doesn't mean you can use it to test if the task actually executes. In fact, it will most likely print no matter what task you execute. If you wanted to print something on execution time, you would have to put it in a doLast block.
There is a few other things you should change as well:
It is not a good practice to use relative references. Instead, use the buildDir property to get an absolute reference to the build directory.
Don't use deprecated methods like archiveName and destinationDir. Use archiveFileName and destinationDirectory instead.
The extension property is also deprecated, but it is ignored if you set the full name of the archive yourself. So just remove it. This also means you are missing the extension on the full name.
The from and include is a little fragile. Just use from jar.archivePath if you only want to gzip your application jar.
Example:
task buildDist(type: Tar, dependsOn: jar) {
archiveFileName = "${jar.baseName}-${version}.tar.gz"
destinationDirectory = file("$buildDir/dist")
compression = Compression.GZIP
from jar.archivePath
}
build.dependsOn buildDist
Lastly, if your intention is to create a distribution of your application that is runnable on its own (with all required dependencies), you should consider using the distribution plugin and perhaps also the application plugin.
I'm new to gradle, trying to set up an old project (which used ant). Project setup is fine, though rather complex because of some ugly requirements where different resources need to go multiple different places, with some needing to be filtered according to different properties files etc..
Anyway, I am now trying to set up testing. The problem is that the tests require a hibernate.properties file that is different to the one used in the main project. So, I added a reference to the relevant resource folder:
sourceSets {
test {
resources {
srcDirs "${outputDir}/test/generated-src"
}
}
}
The hibernate.properties file for the main project is in:
sourceSets {
main {
resources {
srcDirs "${outputDir}/generated-src"
}
}
}
(The reason they are not just in src/main/resources etc, is that they required some special handling before they can be used.)
Using "gradle test" works (probably by luck), because it uses the hibernate.properties from the test folder.
But, running junit tests from Eclipse does not work, because in Eclipse the "${outputDir}/test/generated-src" folder gets added to the classpath AFTER the main one. So the tests find the main hibernate.properties, and fail...
I tried to mess around with reordering the classpath for Eclipse, but failed. A workaround is to manually change the order in Eclipse->project properties->Build path->Order and export, but I would like a proper solution.
I'm thinking this probably isn't an unknown problem, and there is probably a proper gradle solution to it?
I have an issue with some gradle task that should run tests. Since this is some legacy from ant tasks we do not want to include them into our test suite. Especially considering that those ant ones are in testng, and those made by us, and used on regular basis are made using spock and junit.
The problem is that those tests are using some context which works pretty well when I run those tests under eclipse IDE, but it fails if I try to do sth like:
task testNgTesting(type: Test, dependsOn: testClasses){
useTestNG()
includes = ["**/*IT*"]
}
But when I try to use that task I get errors like "org.hibernate.MappingException: Unknown entity:" or "java.lang.IllegalArgumentException: No query defined for that name"
Actually the problem is deeper. Gradle is trying to be smart and from whatever folder it has defined it puts classes files into classes folder, and all the other files into resources. When persistence.xml is loaded it scans for annotated entities starting with classpath root for folder it is present in (i.e. build/resources/main). Since all those classes are in build/classes/main it fails to find them. The workaround I've made is introduce two copy tasks. one named is copying persistence.xml into classes folder, and another is moving this file back to resources after the tests are finished. You might want to use something like
testNgTesting.finalizedBy(cleanAfterIntegrationTests) to make sure the cleanup occurs even if there are some tests that fails.
Relatively new to java and gradle -- trying to do things "right". Prior to building my application (I've added the gradle "application" plugin) I want to setup some environment and system things -- for example, I'd like to create the log/ directory and log.txt file.
So I'm doing something like:
task setup {
println 'Setup task executing ...'
File d = new File('log');
d.mkdir();
f = new File(d.getPath() + '/log.txt');
f.createNewFile();
}
Which works -- but I get a bunch of stdout warnings when running > gradle setup
Setup task executing ...
Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties.
Deprecated dynamic property: "f" on "task ':setup'", value: "log/log.txt".
:setup UP-TO-DATE
So one question: What is the correct way to leverage Gradle to perform setup / installation tasks? (This should only really be executed once, when the application is deployed)
Ah, you are mixing task configuration and execution. This:
task foo {
// Stuff
}
is not the same as this:
task foo << {
// Stuff
}
In the first, "stuff" is run at configuration time, leading to the warnings that you're seeing (because f is interpreted as a project variable during this phase). In the second, it's run at execution time.
(Gradle is great, but this very subtle syntax distinction can be the source of many infuriating bugs!)
As for how to do setup properly, as you're using the Application plugin, you should look into Including other resources in the distribution.
(You should also consider moving the directory-creation logic into your application itself, as ideally you want it to be robust against someone deleting the log directory!)
I'm trying to fiddle with Foursquare's HeapAudit, and am attempting to set it up using IntelliJ IDEA. I have managed to get it to build just fine, using the dependencies from the pom.xml.
However, when I actually try to run the JUnit tests, basically all of them fail. I'm guessing this is because using HeapAudit requires the JVM to be started with it as a -javaagent, according to the github:
$ java -javaagent:heapaudit.jar MyTest
Presumably the tests would pass if I put this line in, and referenced the heapaudit.jar i downloaded/built earlier. However, it seems to me that if I make changes the the source, I'm gonna need to re-package this silly .jar file in order to see if it works.
Is there any way of running the tests with a -javaagent without going through the whole rigmarole of compile -> package-into-jar every testing cycle? Perhaps getting IntelliJ to attached the newly-compiled .class files as a -javaagent before running the tests?
1) Have a jar just with a META-INF/MANIFEST.MF
The manifest must be properly configured with Premain-Class and other attributes. The jar doesn't need any other files. Use this jar with the -javaagent. Provided that the agent classes are in the classpath, the agent will start normally.
This might fail when using maven-surefire-plugin with forkMode=never because by default the application classes are loaded in a child ClassLoader.
Works fine with Eclipse and Intellij.
If doing this, double check the manifest syntax (once I spent a long time to figure out that a package name was wrong).
2) Use ea-agent-loader
It will allow you to load the agent (any agent) in runtime (it uses VM.attach()). However the VM.attach() sometimes disrupts debugging and breakpoints might fail to trigger.
It will have the same issues with the surefire in forkMode=never
3) Load the agent in runtime.
Write your on code to load the agent in runtime. And call it from your #BeforeClass You will still need a jar (which you can generate in runtime if you want).
Just you need to call this (only once):
AgentLoader.loadAgentClass(YourAgentClass.class.getName());