Gradle: detect if there are #Test methods in a particular folder - android

I would like to create a gradle method that would return me a boolean saying if a project passed as input has some tests defined in a particular sub folder. Particularly, I am interested in knowing if the project contains android instrumentation tests.
So far, this is what I have (my root project has multiple sub-projects):
def doesProjectContainsInstrumentationTests(project) {
def rootDir = new File(rootProject.rootDir, "")
def projectPath = project.getPath().replaceAll(':', '/')
def fullPath = new File(rootDir, projectPath)
def androidTestFolder = new File(fullPath, "src/androidTest")
//TODO: find a better way to detect if the module has instrumentation tests to run (using some annotation like #Test or #RunWith?)
def containsInstrumentationTests = androidTestFolder.exists()
return containsInstrumentationTests
}
The problem with this implementation is that the folder might exist but no tests are in it. In that situation, the code above would return true while it should return false.
Ideally I would like to reuse the same logic described here, but not sure I can do it.
Any suggestion on how I could achieve that? I'm ok if there is a completly different way to achieve that, as long as it works :)

Related

Replace word in package names and variables in code

In my android project I got very intericting task
My company wants to hide all mintions about her in code (variables names, packages and etc)
But only for one flavour, so I cannot do it once for all project.
My first idea, was writing a simple gradle task, that will replace all strings that fit in code, but in this case package names will remain unchanged.
Secondly, since we have ci on Jenkins, I thought about jenkins script, that will rename all files and its content, if it has keyword. But this solution looks very bulky for me.
Maybe there is another, elegant way?
Replacing the package name/variable names blindly seems a bit risky, as it could replace other overlapping strings as well, which may lead to various issues. Assuming your package name is unique and you don't have any overlapping names and it doesn't result in any directory name changes, you can use different methods to replace the package name.
Option 1
Using shell to achieve this. There are plenty of different ways to do this, following is one option with grep and sed
sh '''
grep -rl ${PACKAGE_NAME_TO_REPLACE} ${DESTINATION_DIR} | xargs sed -i "s&${PACKAGE_NAME_TO_REPLACE}&${PACKAGE_NAME_NEW}&g"
'''
You can take a look at this and this to understand different methods you can use.
Option 2
If you want a more controlled approach, you can achieve this with some groovy code as well. Simply run the following within your Pipeline.
def dirToSearchIn = "/where/to/replace"
// Change the content on specific files. You can improve the regex pattern below to fine-tune it. With the following pattern only files with extensions .java and .md will be changed.
def filterFilePattern = ~/.*\.java|.*\.md$/
def oldString = "replaceme"
def newString = "newme"
new File(dirToSearchIn).traverse(type: groovy.io.FileType.FILES, nameFilter: filterFilePattern) { file ->
println "Processing file: " + file.getPath()
def fileContent = file.text;
if (fileContent.contains(oldString)) {
println "Replacing the content of the file: " + file.getPath()
file.write(fileContent.replaceAll(oldString, newString));
} else {
println "Skipping file: " + file.getPath()
}
}
Adding something like the following to your top-level build.gradle file should do the trick (assuming your company is called “ACME” and you rather want it to be called “foobar”):
def duplicateProjDirName = 'duplicateProj'
def duplicateProjDir = project.layout.buildDirectory.dir(duplicateProjDirName)
def duplicateProj = tasks.register('createDuplicateProject', Copy) {
enabled = (projectDir.name != duplicateProjDirName)
from(project.layout.projectDirectory)
into(duplicateProjDir)
exclude('build', '.gradle')
def acmePattern = /(?i)acme/
def newCompanyName = 'foobar'
eachFile { it.path = it.sourcePath.replaceAll(acmePattern, newCompanyName) }
filter { it.replaceAll(acmePattern, newCompanyName) }
includeEmptyDirs = false
}
def duplicateBuild = tasks.register('buildDuplicateProject', GradleBuild) {
enabled = (projectDir.name != duplicateProjDirName)
dependsOn(duplicateProj)
dir = duplicateProjDir
tasks = ['build']
}
tasks.named('build').configure {
dependsOn(duplicateBuild)
}
This essentially adds two tasks to the project:
createDuplicateProject duplicates the project under build/duplicateProj/ with all mentions of “ACME” replaced with “foobar”. It also takes care of renaming files/directories (in contrast to the solutions in other answers so far).
buildDuplicateProject builds the duplicate project.
While this may work in basic scenarios (I’ve successfully tested it with a small dummy Java project and Gradle 7.6), there are some edge cases to think about:
There may be dependencies (libraries, services, etc.) which contain the company name and which won’t work anymore after they’ve been renamed.
This way of replacing
may not work well for binary files.
does not catch occurrences in code such as "AC" + "ME".
case-insensitively may lead to weird-looking names that don’t follow common conventions. In the worst case, this could lead to different behavior, too.
There may be downstream projects which depend on package names or the like that are renamed here.
Your company may not only be identifiable by name, e.g., there may also be logos in image files, etc.
and probably others

Test classes doesn't work correctly on android?

I'm a newbie in unit testing in android and I follow this repository on implementing it but when I done it and run it my test it passes always even if I make a different clear assertion
like that
MatcherAssert.assertThat(25, CoreMatchers.`is`(20))
so I thought I made something wrong and I tried to clone the repository and try it and found the repository also passed in all cases , I tried to change the MainCoroutineRule from this to this and now the tests work but not passed
for example in Repository test it gives me this error
junit.framework.AssertionFailedError:
Expected :com.fevziomurtekin.deezer.core.data.ApiResult$Loading#5c7933ad
Actual :Success(data=[AlbumData(diskNumber=1, duration=320, explicitContentCover=0, explicitContentLyrics=0,.......
and the method for that test case
#ExperimentalTime
#Test
fun fetchAlbumTracksFromNetworkTest() = runBlocking {
val mockData = AlbumDetailsResponse(data = listOf(MockUtil.album), total = 12)
val returnData = Response.success(mockData)
whenever(service.fetchAlbumDetails(MockUtil.albumID)).thenReturn(returnData)
repository.fetchAlbumTracks(MockUtil.albumID).test {
val result = expectItem()
assertEquals(result, ApiResult.Success(listOf(MockUtil.album)))
Assert.assertEquals((result as ApiResult.Success<List<AlbumData>>).data[0].title, "Alo")
Assert.assertEquals(result.data[0].id, "425605922")
Assert.assertEquals(result.data[1].title, "Geceler")
Assert.assertEquals(result.data[1].id, "425605932")
expectComplete()
}
verify(service, atLeastOnce()).fetchAlbumDetails(MockUtil.albumID)
verifyNoMoreInteractions(service)
}
and for Viewmodel it gives me this error
Argument(s) are different! Wanted:
observer.onChanged(
Success(data=[ArtistAlbumData(cover=https://api.deezer.com/album/51174732/image, cov
for this test case
#Test
fun fetchAlbumListTest() = runBlocking {
val mockList = listOf(MockUtil.album)
val observer : Observer<ApiResult<List<AlbumData>>> = mock()
val fetchedData : LiveData<ApiResult<List<AlbumData>>> = repository.fetchAlbumTracks("302127").asLiveData()
fetchedData.observeForever(observer)
viewModel.fetchingAlbumDatas(MockUtil.albumID)
delay(500L)
verify(observer).onChanged(ApiResult.Success(mockList))
fetchedData.removeObserver(observer)
}
and for Api test here it also fails to read input stream from input json files
Did I setup anything wrong on test configuration which makes the tests fails or the tests itself is wrong,the weird is the repository which I used there are many test classes for the whole project and I wonder if it didn't work it won't be pushed!
update: I open an issue on the repository and it has more details of what happens here
hope anyone can help with that?

Cucumber steps implementation in kotlin isn't recognized in feature file

I have this feature file :
Given I launch google using chrome as browser
When I search by kotlin
Then I will see 32 results per page
where my properties file has the content :
e1=www.google.com
field= kotlin
nr=32
However, the third steps from my feature file appears as if they aren't implemented : https://www.screencast.com/t/fP9vjdToSI
I have implemented the steps in a kotlin class :
init {
Given("^I launch \"(.*)\" using chrome as browser$") { element: String ->
// load the properties file
// prop.load(file)
prop.load(StepsDefinition::class.java!!.getClassLoader()
.getResourceAsStream("application-test.properties"))
// set the properties
prop.getProperty("el1")
val element= prop.getProperty("el1")
println(prop.getProperty("el1"))
driver.get(element)
When("^I search by \"(.*)\"$") { field: String ->
val button = driver.findElement(By.xpath("//input[#value=\"" + field+ "\"]" +
"| //button[contains(text(),\"" + field+ "\")]"))
button.isDisplayed
button.click()
}
Then("^I see \"(.*)\" results per page") { nr: String ->
val wait = WebDriverWait(driver, 10)
Assert.assertTrue(driver.getTitle().contains(nr))
}
Can someone please help me?
Thanks.
I don't have cucumber currently set up on my machine so I can't really verify what I'm saying, however I'd say it's just a mismatch between the step definition and the step in the feature.
Your step definition expects the keyword to be wrapped in quotes, while in your feature file you're not using any quotes. The same should apply to the When step (same error) and to the Then step, where you put an extra will.

Titanium Hyperloop access to android.os.SystemProperties

I have been trying a ( i hope) simple bit of Android hyperloop code directly within a titanium project (using SDK 7.0.1.GA and hyperloop 3).
var sysProp = require('android.os.SystemProperties');
var serialNumber = sysProp.get("sys.serialnumber", "none");
But when the app is run it reports
Requested module not found:android.os.SystemProperties
I think this maybe due to the fact that when compiling the app (using the cli) it reports
hyperloop:generateSources: Skipping Hyperloop wrapper generation, no usage found ...
I have similar code in a jar and if I use this then it does work, so I am wondering why the hyperloop generation is not being triggered, as I assume that is the issue.
Sorry should have explained better.
This is the jar source that I use, the extraction of the serial number was just an example (I need access to other info manufacturer specific data as well), I wanted to see if I could replicate the JAR functionality using just hyperloop rather that including the JAR file. Guess if it's not broke don't fix it, but was curious to see if it could be done.
So with the feedback from #miga and a bit of trial and error, I have come up with a solution that works really well and will do the method reflection that is required. My new Hyperloop function is
function getData(data){
var result = false;
var Class = require("java.lang.Class");
var String = require("java.lang.String");
var c = Class.forName("android.os.SystemProperties");
var get = c.getMethod("get", String.class, String.class);
result = get.invoke(c, data, "Error");
return result;
}
Where data is a string of the system property I want.
I am using it to extract and match a serial number from a Samsung device that is a System Property call "ril.serialnumber" or "sys.serialnumber". Now I can use the above function to do what I was using the JAR file for. Just thought I'd share in case anyone else needed something similar.
It is because android.os.SystemProperties is not class you can import. Check the android documentation at https://developer.android.com/reference/android/os/package-summary.html
You could use
var build = require('android.os.Build');
console.log(build.SERIAL);
to access the serial number.

different onPrepare based on capabilities

I'd like to run different functions in my onPrepare function based on what capabilities I am running. I'd also like to be able to set capabilities from the command line. For example I run every test suite by typing "protractor" at the moment. Something like "protract -android" or "protractor directConnect = true" or anything that functions in a similar manner is what I am looking for.
Then in the onPrepare I have a function that maximizes my window which will not work when I run my android tests. I'm looking for a solution so that when I run the tests on android it just ignores that block of code.
In protractor you can use global variables using params object in the config.js file. Try the below solution to solve your problem.
In config.js file create a params object with new variable as android.Following will be a demo config file.
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
capabilities: {
'browserName': 'chrome'
},
onPrepare: function () {
if(browser.params.android == 'true'){
//do whatever code you need to execute
}else{
}
},
params: {
android: 'false',
}
}
You can now pass the value for android variable as a commond line argument when you run the protractor test.
protractor config.js --params.android true

Categories

Resources