Skip to content

Commit

Permalink
Test task up-to-date fix (#137)
Browse files Browse the repository at this point in the history
## Description
Unity Test task inputs weren't taking as input anything related to the project file structure, so it ended up being up-to-date even when there were relevant changes to the project. the `inputFiles` Input was create on the task  in order to resolve this. 

## Changes
* ![FIX] Test task being up-to-date in wrong situations
  • Loading branch information
Joaquimmnetto authored Jan 18, 2022
1 parent 9ad3429 commit 384823e
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 5 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

plugins {
id 'net.wooga.plugins' version '2.2.2'
id 'net.wooga.plugins' version '2.2.3'
}

group 'net.wooga.gradle'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
package wooga.gradle.unity.tasks

import com.wooga.spock.extensions.unity.UnityPluginTestOptions
import kotlin.Unit
import nebula.test.functional.ExecutionResult
import spock.lang.Shared
import spock.lang.Unroll
import wooga.gradle.unity.UnityPlugin
import wooga.gradle.unity.UnityTaskIntegrationSpec
Expand Down Expand Up @@ -256,7 +256,7 @@ class TestTaskIntegrationSpec extends UnityTaskIntegrationSpec<Test> {
enableTestCodeCoverage.set(false)
}
"""

and: "a mocked unity project with enabled playmode tests"
setProjectSettingsFile(ProjectSettingsFile.TEMPLATE_CONTENT_ENABLED)

Expand All @@ -270,6 +270,132 @@ class TestTaskIntegrationSpec extends UnityTaskIntegrationSpec<Test> {
!editModeResult.args.contains("-enableCodeCoverage")
}

@Shared
def mockProjectFiles = [
[new File("Assets/Plugins.meta"), false],
[new File("Library/SomeCache.asset"), true],
[new File("ProjectSettings/SomeSettings.asset"), false],
[new File("UnityPackageManager/manifest.json"), false],
[new File("Assets/Plugins/iOS.meta"), true],
[new File("Assets/Plugins/iOS/somefile.m"), true],
[new File("Assets/Plugins/iOS/somefile.m.meta"), true],
[new File("Assets/Nested.meta"), false],
[new File("Assets/Nested/Plugins.meta"), false],
[new File("Assets/Nested/Plugins/iOS.meta"), true],
[new File("Assets/Nested/Plugins/iOS/somefile.m"), true],
[new File("Assets/Nested/Plugins/iOS/somefile.m.meta"), true],
[new File("Assets/Plugins/WebGL.meta"), true],
[new File("Assets/Plugins/WebGL/somefile.ts"), true],
[new File("Assets/Plugins/WebGL/somefile.ts.meta"), true],
[new File("Assets/Nested/Plugins/WebGL.meta"), true],
[new File("Assets/Nested/Plugins/WebGL/somefile.ts"), true],
[new File("Assets/Nested/Plugins/WebGL/somefile.ts.meta"), true],
[new File("Assets/Editor.meta"), false],
[new File("Assets/Editor/somefile.cs"), false],
[new File("Assets/Editor/somefile.cs.meta"), false],
[new File("Assets/Nested/Editor/somefile.cs"), false],
[new File("Assets/Source.cs"), false],
[new File("Assets/Source.cs.meta"), false],
[new File("Assets/Nested/LevelEditor.meta"), false],
[new File("Assets/Nested/LevelEditor/somefile.cs"), false],
[new File("Assets/Nested/LevelEditor/somefile.cs.meta"), false],
[new File("Assets/Plugins/Android.meta"), false],
[new File("Assets/Plugins/Android/somefile.java"), false],
[new File("Assets/Plugins/Android/somefile.java.meta"), false],
[new File("Assets/Nested/Plugins/Android.meta"), false],
[new File("Assets/Nested/Plugins/Android/s.java"), false],
[new File("Assets/Nested/Plugins/Android/s.java.meta"), false],
]

@Unroll
def "task #statusMessage up-to-date when #file changed with default inputFiles"() {
given: "a mocked unity project"
//need to convert the relative files to absolute files
def (_, File testFile) = prepareMockedProject(projectDir, files as Iterable<File>, file as File)
and: "a unity test task"
buildFile << """
tasks.register("unityTest", wooga.gradle.unity.tasks.Test) {
it.buildTarget = "${buildTarget}"
}
""".stripIndent()

and: "a up-to-date project state"
def result = runTasksSuccessfully("unityTest")
assert !result.wasUpToDate('unityTest')

result = runTasksSuccessfully("unityTest")
assert result.wasUpToDate('unityTest')

when: "change content of one source file"
testFile.text = "new content"

result = runTasksSuccessfully("unityTest")

then:
result.wasUpToDate('unityTest') == upToDate

where:
files = mockProjectFiles.collect { it[0] }
[file, upToDate] << mockProjectFiles
buildTarget = "android"
statusMessage = upToDate ? "is" : "is not"
}

@Unroll
def "can set custom inputFiles for up-to-date check #type"() {
given: "a mocked unity project"
//need to convert the relative files to absolute files
def (_, File testFile) = prepareMockedProject(projectDir, files as Iterable<File>, file as File)

and: "a custom inputCollection"
buildFile << """
tasks.register("unityTest", wooga.gradle.unity.tasks.Test) {
it.inputFiles.setFrom(${value})
}
""".stripIndent()

and: "a up-to-date project state"
def result = runTasksSuccessfully("unityTest")
assert !result.wasUpToDate('unityTest')

result = runTasksSuccessfully("unityTest")
assert result.wasUpToDate('unityTest')

when: "change content of one source file"
testFile.text = "new content"

result = runTasksSuccessfully("unityTest")

then:
result.wasUpToDate('unityTest') == upToDate

where:
file | upToDate | type | value
new File("Assets/Plugins/iOS/somefile.m") | true | 'FileTree' | 'project.fileTree(project.projectDir){include("Assets/**"); exclude("**/Plugins/iOS/**")}'
new File("Assets/Plugins/Android/somefile.m") | false | 'FileTree' | 'project.fileTree(project.projectDir){include("Assets/**"); exclude("**/Plugins/iOS/**")}'
new File("Assets/Source.cs") | false | 'FileTree' | 'project.fileTree(project.projectDir){include("Assets/**"); exclude("**/Plugins/iOS/**")}'
new File("Assets/Plugins/iOS/somefile.m") | false | 'FileTree' | 'project.fileTree(project.projectDir){include("Assets/**"); exclude("**/Plugins/Android/**")}'
new File("Assets/Plugins/Android/somefile.m") | true | 'FileTree' | 'project.fileTree(project.projectDir){include("Assets/**"); exclude("**/Plugins/Android/**")}'
new File("Assets/Source.cs") | false | 'FileTree' | 'project.fileTree(project.projectDir){include("Assets/**"); exclude("**/Plugins/Android/**")}'
new File("Assets/Editor/somefile.cs") | true | 'FileCollection' | 'project.files("Assets/Editor/anyfile.cs","Assets/Source.cs")'
new File("Assets/Source.cs") | false | 'FileCollection' | 'project.files("Assets/Editor/anyfile.cs","Assets/Source.cs")'

files = mockProjectFiles.collect { it[0] }
statusMessage = (upToDate) ? "is" : "is not"
}

Tuple prepareMockedProject(File projectDir, Iterable<File> files, File testFile) {
files = files.collect { new File(projectDir, it.path) }
testFile = new File(projectDir, testFile.path)

//create directory structure
files.each { f ->
f.parentFile.mkdirs()
f.text = "some content"
}
new Tuple(files, testFile)
}

boolean matchesExpectedCoverageArgs(GradleRunResult taskResult) {
return taskResult.args.contains("-enableCodeCoverage") &&
taskResult.args.contains("-coverageResultsPath") &&
Expand Down
5 changes: 4 additions & 1 deletion src/main/groovy/wooga/gradle/unity/UnityPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ package wooga.gradle.unity
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.FileTreeElement
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.ReportingBasePlugin
import org.gradle.api.provider.Provider
import org.gradle.api.reporting.ReportingExtension
import org.gradle.api.specs.Spec
import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin
import wooga.gradle.unity.internal.InputFileTreeFactory
import wooga.gradle.unity.models.APICompatibilityLevel
import wooga.gradle.unity.models.DefaultUnityAuthentication
import wooga.gradle.unity.internal.DefaultUnityPluginExtension
Expand Down Expand Up @@ -197,7 +199,6 @@ class UnityPlugin implements Plugin<Project> {

// Override the batchmode property for edit/playmode test tasks by that of the extension
project.tasks.withType(Test.class).configureEach({ Test t ->

Provider<Boolean> testBatchModeProvider = project.provider {
def tp = t.testPlatform.getOrNull()
try {
Expand All @@ -215,6 +216,8 @@ class UnityPlugin implements Plugin<Project> {
}
true
}

t.inputFiles.from({ -> InputFileTreeFactory.inputFilesForUnityTask(project, extension, t) })
t.batchMode.set(testBatchModeProvider)
t.reports.xml.outputLocation.convention(extension.reportsDir.file(t.name + "/" + t.name + "." + reports.xml.name))
t.enableCodeCoverage.convention(extension.enableTestCodeCoverage)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package wooga.gradle.unity.internal

import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.Directory
import org.gradle.api.file.FileTreeElement
import org.gradle.api.provider.Provider
import wooga.gradle.unity.UnityPluginExtension
import wooga.gradle.unity.UnityTask

class InputFileTreeFactory {

private final Project project
private final UnityPluginExtension extension

InputFileTreeFactory(Project project, UnityPluginExtension extension) {
this.project = project
this.extension = extension
}

static ConfigurableFileCollection inputFilesForUnityTask(Project project, UnityPluginExtension extension, UnityTask task) {
return new InputFileTreeFactory(project, extension).inputFilesForUnityTask(task)
}

ConfigurableFileCollection inputFilesForBuildTarget(Provider<Directory> projectDirectory, Provider<String> buildTarget) {
def assetsDir = extension.assetsDir
def assetsFileTree = project.fileTree(assetsDir)
assetsFileTree.include { FileTreeElement elem ->
def path = elem.getRelativePath().getPathString().toLowerCase()
def name = elem.name.toLowerCase()
if (path.contains("plugins") && !((name == "plugins") || (name == "plugins.meta"))) {
return isPluginElemFromBuildTarget(elem, buildTarget)
} else {
return true
}
}

def projectSettingsDir = projectDirectory.map {it.dir("ProjectSettings") }
def projectSettingsFileTree = project.fileTree(projectSettingsDir)

def packageManagerDir = projectDirectory.map{it.dir("UnityPackageManager") }
def packageManagerDirFileTree = project.fileTree(packageManagerDir)

return project.files(assetsFileTree, projectSettingsFileTree, packageManagerDirFileTree)
}

ConfigurableFileCollection inputFilesForUnityTask(UnityTask unityTask) {
return inputFilesForBuildTarget(unityTask.projectDirectory, unityTask.buildTarget)
}

private static boolean isPluginElemFromBuildTarget(FileTreeElement element, Provider<String> buildTarget) {
/*
Why can we use / here? Because {@code element} is a {@code FileTreeElement} object.
The getPath() method is not the same as {@code File.getPath()}
From the docs:
* Returns the path of this file, relative to the root of the containing file tree. Always uses '/' as the hierarchy
* separator, regardless of platform file separator. Same as calling <code>getRelativePath().getPathString()</code>.
*
* @return The path. Never returns null.
*/
def path = element.getRelativePath().getPathString().toLowerCase()
if (buildTarget.isPresent()) {
return path.contains("plugins/" + buildTarget.get())
} else {
return true
}
}
}
1 change: 0 additions & 1 deletion src/main/groovy/wooga/gradle/unity/tasks/Test.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package wooga.gradle.unity.tasks

import org.gradle.api.provider.Property
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.StopExecutionException
import org.gradle.internal.reflect.Instantiator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@

package wooga.gradle.unity.traits

import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.tasks.InputFiles

trait UnityTestSpec extends UnityBaseSpec {

private final ConfigurableFileCollection inputFiles = objects.fileCollection()

@InputFiles
ConfigurableFileCollection getInputFiles() {
return inputFiles
}

}

0 comments on commit 384823e

Please sign in to comment.