From a3cb3e2092796fd379962ebd000c9e48da9ae073 Mon Sep 17 00:00:00 2001 From: Bruno Bowden Date: Tue, 25 Aug 2015 16:31:06 -0700 Subject: [PATCH] Fixing Windows Unit Tests (fixes #402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major: - Fix #402 - Windows 37 unit tests failures - Fix #422 - echo command fails on Windows - Disabled only failing test on Windows: #374 Minor: - Fake OS specific file and path separators - Explicit setFakeOS methods, e.g. setFakeOSWindows() - Default to using native OS rather than fake OS for most unit tests - Small number of unit tests use fakeOSName - ‘inTestMustFakeOS’ setting removed --- build.gradle | 1 - .../j2objcgradle/J2objcConfig.groovy | 6 +- .../j2objcgradle/tasks/CycleFinderTask.groovy | 4 +- .../tasks/PackLibrariesTask.groovy | 2 +- .../j2objcgradle/tasks/TestTask.groovy | 28 +- .../j2objcgradle/tasks/TranslateTask.groovy | 2 +- .../j2objcgradle/tasks/Utils.groovy | 93 +++++-- .../j2objcgradle/tasks/XcodeTask.groovy | 17 +- .../j2objcgradle/J2objcConfigTest.groovy | 64 +++-- .../j2objcgradle/MultiProjectTest.groovy | 11 +- .../tasks/AssembleLibrariesTaskTest.groovy | 18 +- .../tasks/AssembleResourcesTaskTest.groovy | 16 +- .../tasks/AssembleSourceTaskTest.groovy | 12 +- .../tasks/CycleFinderTaskTest.groovy | 37 ++- .../j2objcgradle/tasks/MockProjectExec.groovy | 95 +++++-- .../tasks/PackLibrariesTaskTest.groovy | 9 +- .../j2objcgradle/tasks/TestTaskTest.groovy | 33 +-- .../j2objcgradle/tasks/TestingUtils.groovy | 47 +++- .../tasks/TranslateTaskTest.groovy | 50 ++-- .../j2objcgradle/tasks/UtilsTest.groovy | 246 ++++++++++++++---- .../j2objcgradle/tasks/XcodeTaskTest.groovy | 37 +-- 21 files changed, 595 insertions(+), 233 deletions(-) diff --git a/build.gradle b/build.gradle index 91b63a46..de1b48f1 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,6 @@ test { // of just pointing to an HTML file. exceptionFormat = 'full' } - systemProperty 'inTestMustFakeOS', 'true' } pluginBundle { diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy index 8ac80e4b..47f28262 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy @@ -66,9 +66,9 @@ class J2objcConfig { nativeCompilation = new NativeCompilation(project) // Provide defaults for assembly output locations. - destSrcMainDir = "${project.buildDir}/j2objcOutputs/src/main" - destSrcTestDir = "${project.buildDir}/j2objcOutputs/src/test" - destLibDir = "${project.buildDir}/j2objcOutputs/lib" + destSrcMainDir = new File(project.buildDir, 'j2objcOutputs/src/main').absolutePath + destSrcTestDir = new File(project.buildDir, 'j2objcOutputs/src/test').absolutePath + destLibDir = new File(project.buildDir, 'j2objcOutputs/lib').absolutePath } /** diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTask.groovy index 4b248e7f..bca32ab7 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTask.groovy @@ -93,7 +93,7 @@ class CycleFinderTask extends DefaultTask { @TaskAction void cycleFinder() { - String cycleFinderExec = "${getJ2objcHome()}/cycle_finder" + String cycleFinderExec = getJ2objcHome() + Utils.fileSeparator() + 'cycle_finder' List windowsOnlyArgs = new ArrayList() if (Utils.isWindows()) { cycleFinderExec = 'java' @@ -125,7 +125,7 @@ class CycleFinderTask extends DefaultTask { ]) // TODO: comment explaining ${project.buildDir}/classes String classpathArg = Utils.joinedPathArg(classpathFiles) + - File.pathSeparator + "${project.buildDir}/classes" + Utils.pathSeparator() + "${project.buildDir}/classes" ByteArrayOutputStream stdout = new ByteArrayOutputStream() ByteArrayOutputStream stderr = new ByteArrayOutputStream() diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy index 8ab711bc..59d2de87 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy @@ -72,7 +72,7 @@ class PackLibrariesTask extends DefaultTask { args 'lipo' args '-create' - args '-output', "${outputLibDirFile}/lib${project.name}-j2objc.a" + args '-output', project.file("${outputLibDirFile}/lib${project.name}-j2objc.a").absolutePath getLibrariesFiles().each { File libFile -> args libFile.absolutePath diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy index d924206f..086acbb5 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy @@ -222,25 +222,23 @@ class TestTask extends DefaultTask { } } - // Generate Test Names - // Generate list of tests from the source java files - // e.g. src/test/java/com/example/dir/ClassTest.java => "com.example.dir.ClassTest" + // Generate list of test names from the source java files // depends on --prefixes dir/prefixes.properties in translateArgs + // Before: src/test/java/com/example/dir/SomeTest.java + // After: com.example.dir.SomeTest or PREFIXSomeTest static List getTestNames(Project proj, FileCollection srcFiles, Properties packagePrefixes) { List testNames = srcFiles.collect { File file -> - // Overall goal is to take the File path to the test name: - // src/test/java/com/example/dir/SomeTest.java => com.example.dir.SomeTest - // Comments show the value of the LHS variable after assignment - - String testName = proj.relativePath(file) // com.example.dir.SomeTest - .replace('src/test/java/', '') - .replace('/', '.') - .replace('.java', '') + // Comments indicate the value at the end of that statement + String testName = proj.relativePath(file) // src/test/java/com/example/dir/SomeTest.java + .replace('\\', '/') // Windows backslashes converted to forward slash + .replace('src/test/java/', '') // com/example/dir/SomeTest.java + .replace('.java', '') // com/example/dir/SomeTest + .replace('/', '.') // com.example.dir.SomeTest // Translate test name according to prefixes.properties - // Prefix Property: com.example.dir: Prefix - // Test Name: com.example.dir.SomeTest => PrefixSomeTest + // Prefix Property: com.example.dir: PREFIX + // Test Name: com.example.dir.SomeTest => PREFIXSomeTest // First match against the set of Java packages, excluding the filename Matcher matcher = (testName =~ /^(([^.]+\.)+)[^.]+$/) // (com.example.dir.)SomeTest @@ -249,10 +247,10 @@ class TestTask extends DefaultTask { String namespaceChopped = namespace[0..-2] // com.example.dir if (packagePrefixes.containsKey(namespaceChopped)) { String prefix = packagePrefixes.getProperty(namespaceChopped) - testName = testName.replace(namespace, prefix) // PrefixSomeTest + testName = testName.replace(namespace, prefix) // PREFIXSomeTest } } - return testName // com.example.dir.SomeTest or PrefixSomeTest + return testName // com.example.dir.SomeTest or PREFIXSomeTest } return testNames } diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTask.groovy index a86544e0..14174b2f 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTask.groovy @@ -235,7 +235,7 @@ class TranslateTask extends DefaultTask { ]) // TODO: comment explaining ${project.buildDir}/classes String classpathArg = Utils.joinedPathArg(classpathFiles) + - File.pathSeparator + "${project.buildDir}/classes" + Utils.pathSeparator() + "${project.buildDir}/classes" ByteArrayOutputStream stdout = new ByteArrayOutputStream() ByteArrayOutputStream stderr = new ByteArrayOutputStream() diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy index 568cd8bd..ef1ad73e 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy @@ -60,37 +60,66 @@ class Utils { } } + private static String fakeOSName = '' + + // This allows faking of is(Linux|Windows|MacOSX) methods but misses java.io.File separators + // One of the following four methods should be called in @Before method to isolate test state + @VisibleForTesting + static void setFakeOSLinux() { + fakeOSName = 'Linux' + } + + @VisibleForTesting + static void setFakeOSMacOSX() { + fakeOSName = 'Mac OS X' + } + + @VisibleForTesting + static void setFakeOSWindows() { + fakeOSName = 'Windows' + } + + // Unset fake os, should be needed for @Before method @VisibleForTesting - static String fakeOSName = '' + static void setFakeOSNone() { + fakeOSName = '' + } @VisibleForTesting - static String getLowerCaseOSName() { + static String getLowerCaseOSName(boolean ignoreFakeOSName) { String osName = System.getProperty('os.name') - if (!fakeOSName.isEmpty()) { - osName = fakeOSName - } else { - if (System.getProperty('inTestMustFakeOS') == 'true') { - throw new InvalidUserDataException( - "Tests must set Utils.fakeOSName = 'OS NAME'\n" + - "This ensure that tests don't depend on the system environment") + if (!ignoreFakeOSName) { + if (!fakeOSName.isEmpty()) { + osName = fakeOSName } } osName = osName.toLowerCase() return osName } - static boolean isWindows() { - String osName = getLowerCaseOSName() - return osName.contains('windows') + static boolean isLinux() { + String osName = getLowerCaseOSName(false) + // http://stackoverflow.com/a/18417382/1509221 + return osName.contains('nux') } static boolean isMacOSX() { - String osName = getLowerCaseOSName() - // From: http://stackoverflow.com/a/18417382/1509221 + String osName = getLowerCaseOSName(false) + // http://stackoverflow.com/a/18417382/1509221 return osName.contains('mac') || osName.contains('darwin') } + static boolean isWindows() { + String osName = getLowerCaseOSName(false) + return osName.contains('windows') + } + + static boolean isWindowsNoFake() { + String osName = getLowerCaseOSName(true) + return osName.contains('windows') + } + static void requireMacOSX(String taskName) { if (!isMacOSX()) { throw new InvalidUserDataException( @@ -99,6 +128,24 @@ class Utils { } } + // Same as File.separator but can be faked using setFakeOSXXXX() + static String fileSeparator() { + if (isWindows()) { + return '\\' + } else { + return '/' + } + } + + // Same as File.pathSeparator but can be faked using setFakeOSXXXX() + static String pathSeparator() { + if (isWindows()) { + return ';' + } else { + return ':' + } + } + static void throwIfNoJavaPlugin(Project proj) { if (!proj.plugins.hasPlugin('java')) { String message = @@ -172,8 +219,8 @@ class Utils { // MUST be used only in @Input getJ2objcHome() methods to ensure up-to-date checks are correct // @Input getJ2objcHome() method can be used freely inside the task action static String j2objcHome(Project proj) { - String result = getLocalProperty(proj, 'home') - if (result == null) { + String j2objcHome = getLocalProperty(proj, 'home') + if (j2objcHome == null) { String message = "J2ObjC Home not set, this should be configured either:\n" + "1) in a 'local.properties' file in the project root directory as:\n" + @@ -186,15 +233,13 @@ class Utils { "https://github.com/google/j2objc/releases" throw new InvalidUserDataException(message) } - if (!proj.file(result).exists()) { - String message = "J2OjcC Home directory not found, expected location: ${result}" + File j2objcHomeFile = new File(j2objcHome) + if (!j2objcHomeFile.exists()) { + String message = "J2OjcC Home directory not found, expected location: ${j2objcHome}" throw new InvalidUserDataException(message) } - // Trailing slashes cause problems with string concatenation logic. - if (result != null && result.endsWith('/')) { - result = result.substring(0, result.length() - 1) - } - return result + // File removes trailing slashes cause problems with string concatenation logic + return j2objcHomeFile.absolutePath } // Reads properties file and arguments from translateArgs (last argument takes precedence) @@ -297,7 +342,7 @@ class Utils { paths += file.path } // OS specific separator, i.e. ":" on OS X and ";" on Windows - return paths.join(File.pathSeparator) + return paths.join(pathSeparator()) } static String trimTrailingForwardSlash(String path) { diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTask.groovy index 9b820d51..7d6bd18c 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTask.groovy @@ -148,7 +148,7 @@ class XcodeTask extends DefaultTask { try { logger.debug('XcodeTask - projectExec - pod install:') Utils.projectExec(project, stdout, stderr, null, { - setWorkingDir getXcodeProjectDir() + setWorkingDir project.file(getXcodeProjectDir()) executable 'pod' args 'install' setStandardOutput stdout @@ -201,22 +201,25 @@ class XcodeTask extends DefaultTask { } @VisibleForTesting - static void validatePodspecPath(String path, boolean relative) { + static void validatePodspecPath(String path, boolean relativeRequired) { if (path.contains('//')) { throw new InvalidUserDataException("Path shouldn't have '//': $path") } if (path.endsWith('/')) { throw new InvalidUserDataException("Path shouldn't end with '/': $path") } - if (relative && path.startsWith('/')) { + if (path.endsWith('*')) { + throw new InvalidUserDataException("Only genPodspec(...) should add '*': $path") + } + // Hack to recognize absolute path on Windows, only relevant in unit tests run on Windows + boolean absolutePath = path.startsWith('/') || + (path.startsWith('C:\\') && Utils.isWindowsNoFake()) + if (relativeRequired && absolutePath) { throw new InvalidUserDataException("Path shouldn't be absolute: $path") } - if (!relative && !path.startsWith('/')) { + if (!relativeRequired && !absolutePath) { throw new InvalidUserDataException("Path shouldn't be relative: $path") } - if (path.endsWith('*')) { - throw new InvalidUserDataException("Only genPodspec(...) should add '*': $path") - } } // Podspec references are relative to project.buildDir diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfigTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfigTest.groovy index 318f76cd..85b2d913 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfigTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfigTest.groovy @@ -43,37 +43,40 @@ class J2objcConfigTest { @Before void setUp() { + // Default to native OS except for specific tests + Utils.setFakeOSNone() proj = ProjectBuilder.builder().build() } @Test void testConstructor() { J2objcConfig ext = new J2objcConfig(proj) - String projectDir = proj.projectDir.absolutePath - assert ext.destSrcMainDir == projectDir + '/build/j2objcOutputs/src/main' - assert ext.destSrcTestDir == projectDir + '/build/j2objcOutputs/src/test' - assert ext.destLibDir == projectDir + '/build/j2objcOutputs/lib' + assert proj.file('build/j2objcOutputs/src/main').absolutePath == ext.destSrcMainDir + assert proj.file('build/j2objcOutputs/src/test').absolutePath == ext.destSrcTestDir + assert proj.file('build/j2objcOutputs/lib').absolutePath == ext.destLibDir } @Test // All variations of main/test and objc/resources void testGetDestDirFile_AllVariations() { J2objcConfig ext = new J2objcConfig(proj) - String pDir = proj.projectDir.absolutePath - assert pDir + '/build/j2objcOutputs/lib' == ext.getDestLibDirFile().absolutePath - assert pDir + '/build/j2objcOutputs/src/main/objc' == ext.getDestSrcDirFile('main', 'objc').absolutePath - assert pDir + '/build/j2objcOutputs/src/test/objc' == ext.getDestSrcDirFile('test', 'objc').absolutePath - assert pDir + '/build/j2objcOutputs/src/main/resources' == ext.getDestSrcDirFile('main', 'resources') - .absolutePath - assert pDir + '/build/j2objcOutputs/src/test/resources' == ext.getDestSrcDirFile('test', 'resources') - .absolutePath + assert proj.file('build/j2objcOutputs/lib').absolutePath == + ext.getDestLibDirFile().absolutePath + assert proj.file('build/j2objcOutputs/src/main/objc').absolutePath == + ext.getDestSrcDirFile('main', 'objc').absolutePath + assert proj.file('build/j2objcOutputs/src/test/objc').absolutePath == + ext.getDestSrcDirFile('test', 'objc').absolutePath + assert proj.file('build/j2objcOutputs/src/main/resources').absolutePath == + ext.getDestSrcDirFile('main', 'resources').absolutePath + assert proj.file('build/j2objcOutputs/src/test/resources').absolutePath == + ext.getDestSrcDirFile('test', 'resources').absolutePath } @Test void testFinalConfigure_MacOSX() { - Utils.fakeOSName = 'Mac OS X' + Utils.setFakeOSMacOSX() J2objcConfig ext = new J2objcConfig(proj) assert !ext.finalConfigured @@ -82,8 +85,8 @@ class J2objcConfigTest { } @Test - void testFinalConfigure_nonMacOSX_translateOnlyMode() { - Utils.fakeOSName = 'Windows' + void testFinalConfigure_LinuxTranslateOnlyMode() { + Utils.setFakeOSLinux() J2objcConfig ext = new J2objcConfig(proj) assert !ext.finalConfigured ext.translateOnlyMode = true @@ -92,10 +95,35 @@ class J2objcConfigTest { assert ext.finalConfigured } - // This protect against trying the native compile on a nonMacOSX system @Test - void testFinalConfigure_nonMacOSX_unsupported() { - Utils.fakeOSName = 'Windows' + void testFinalConfigure_WindowsTranslateOnlyMode() { + Utils.setFakeOSWindows() + J2objcConfig ext = new J2objcConfig(proj) + assert !ext.finalConfigured + ext.translateOnlyMode = true + + ext.finalConfigure() + assert ext.finalConfigured + } + + // This warns against doing a native compile on a nonMacOSX system + @Test + void testFinalConfigure_LinuxIsUnsupported() { + Utils.setFakeOSLinux() + J2objcConfig ext = new J2objcConfig(proj) + + expectedException.expect(InvalidUserDataException.class) + expectedException.expectMessage('Mac OS X is required for Native Compilation of translated code') + + assert !ext.finalConfigured + ext.finalConfigure() + assert ext.finalConfigured + } + + // This warns against doing a native compile on a nonMacOSX system + @Test + void testFinalConfigure_WindowsIsUnsupported() { + Utils.setFakeOSWindows() J2objcConfig ext = new J2objcConfig(proj) expectedException.expect(InvalidUserDataException.class) diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/MultiProjectTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/MultiProjectTest.groovy index f32ac7b4..0117af0b 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/MultiProjectTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/MultiProjectTest.groovy @@ -22,7 +22,7 @@ class MultiProjectTest { @Before void setUp() { - Utils.fakeOSName = 'Mac OS X' + Utils.setFakeOSMacOSX() // We can't use _ for unused slots because the other variables have already been declared above. Object unused @@ -49,8 +49,15 @@ class MultiProjectTest { @Test void twoProjectsWithDependsOnJ2objcLib_Works() { + // TODO: fix this to run on Windows + // https://github.com/j2objc-contrib/j2objc-gradle/issues/374 + // org.gradle.api.UnknownTaskException: Task with path 'releaseTestJ2objcExecutable' not found in project ':testProject8' + if (Utils.isWindowsNoFake()) { + return + } + proj1.pluginManager.apply(J2objcPlugin) - j2objcConfig1 = proj1.extensions.getByName('j2objcConfig') + j2objcConfig1 = J2objcConfig.from(proj1) j2objcConfig1.finalConfigure() // Will force evaluation of proj1. diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleLibrariesTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleLibrariesTaskTest.groovy index 895270a0..85903c3f 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleLibrariesTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleLibrariesTaskTest.groovy @@ -44,18 +44,18 @@ class AssembleLibrariesTaskTest { MockProjectExec mockProjectExec = new MockProjectExec(proj, '/J2OBJC_HOME') mockProjectExec.demandCopyAndReturn({ includeEmptyDirs = true - from "$proj.projectDir/build/binaries/$proj.name-j2objcStaticLibrary" - from "$proj.projectDir/build/packedBinaries/$proj.name-j2objcStaticLibrary" - into "$proj.projectDir/build/j2objcOutputs/lib" + from proj.file("build/binaries/$proj.name-j2objcStaticLibrary").absolutePath + from proj.file("build/packedBinaries/$proj.name-j2objcStaticLibrary").absolutePath + into proj.file('build/j2objcOutputs/lib').absolutePath include '*Debug/*.a' }) - AssembleLibrariesTask j2objcAssembleLibraries = - (AssembleLibrariesTask) proj.tasks.create(name: 'j2objcAL', type: AssembleLibrariesTask) { - buildType = 'Debug' - srcLibDir = proj.file("$proj.buildDir/binaries/$proj.name-j2objcStaticLibrary") - srcPackedLibDir = proj.file("$proj.buildDir/packedBinaries/$proj.name-j2objcStaticLibrary") - } + AssembleLibrariesTask j2objcAssembleLibraries = (AssembleLibrariesTask) proj.tasks.create( + name: 'j2objcAssembleLibraries', type: AssembleLibrariesTask) { + buildType = 'Debug' + srcLibDir = new File(proj.buildDir, "binaries/$proj.name-j2objcStaticLibrary".toString()) + srcPackedLibDir = new File(proj.buildDir, "packedBinaries/$proj.name-j2objcStaticLibrary".toString()) + } j2objcAssembleLibraries.assembleLibraries() diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleResourcesTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleResourcesTaskTest.groovy index 2aa9f007..b697e087 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleResourcesTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleResourcesTaskTest.groovy @@ -45,21 +45,21 @@ class AssembleResourcesTaskTest { // Main mockProjectExec.demandDeleteAndReturn( - "${proj.projectDir}/build/j2objcOutputs/src/main/resources") + proj.file('build/j2objcOutputs/src/main/resources').absolutePath) mockProjectExec.demandMkDirAndReturn( - "${proj.projectDir}/build/j2objcOutputs/src/main/resources") + proj.file('build/j2objcOutputs/src/main/resources').absolutePath) mockProjectExec.demandCopyAndReturn( - "${proj.projectDir}/build/j2objcOutputs/src/main/resources", - "${proj.projectDir}/src/main/resources") + proj.file('build/j2objcOutputs/src/main/resources').absolutePath, + proj.file('src/main/resources').absolutePath) // Test mockProjectExec.demandDeleteAndReturn( - "${proj.projectDir}/build/j2objcOutputs/src/test/resources") + proj.file('build/j2objcOutputs/src/test/resources').absolutePath) mockProjectExec.demandMkDirAndReturn( - "${proj.projectDir}/build/j2objcOutputs/src/test/resources") + proj.file('build/j2objcOutputs/src/test/resources').absolutePath) mockProjectExec.demandCopyAndReturn( - "${proj.projectDir}/build/j2objcOutputs/src/test/resources", - "${proj.projectDir}/src/test/resources") + proj.file('build/j2objcOutputs/src/test/resources').absolutePath, + proj.file('src/test/resources').absolutePath) AssembleResourcesTask j2objcAssembleResources = (AssembleResourcesTask) proj.tasks.create(name: 'j2objcAR', type: AssembleResourcesTask) diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleSourceTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleSourceTaskTest.groovy index 910bdb57..cdb5899f 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleSourceTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/AssembleSourceTaskTest.groovy @@ -43,20 +43,20 @@ class AssembleSourceTaskTest { // Expected Activity MockProjectExec mockProjectExec = new MockProjectExec(proj, '/J2OBJC_HOME') mockProjectExec.demandDeleteAndReturn( - "$proj.projectDir/build/j2objcOutputs/src/main/objc") + proj.file('build/j2objcOutputs/src/main/objc').absolutePath) mockProjectExec.demandCopyAndReturn({ includeEmptyDirs = false - from "$proj.projectDir/build/j2objcSrcGen" - into "$proj.projectDir/build/j2objcOutputs/src/main/objc" + from proj.file('build/j2objcSrcGen').absolutePath + into proj.file('build/j2objcOutputs/src/main/objc').absolutePath exclude "**/*Test.h" exclude "**/*Test.m" }) mockProjectExec.demandDeleteAndReturn( - "$proj.projectDir/build/j2objcOutputs/src/test/objc") + proj.file('build/j2objcOutputs/src/test/objc').absolutePath) mockProjectExec.demandCopyAndReturn({ includeEmptyDirs = false - from "$proj.projectDir/build/j2objcSrcGen" - into "$proj.projectDir/build/j2objcOutputs/src/test/objc" + from proj.file('build/j2objcSrcGen').absolutePath + into proj.file('build/j2objcOutputs/src/test/objc').absolutePath include "**/*Test.h" include "**/*Test.m" }) diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTaskTest.groovy index 7e43b7f3..5e4d22a7 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/CycleFinderTaskTest.groovy @@ -34,7 +34,8 @@ class CycleFinderTaskTest { @Before void setUp() { - Utils.fakeOSName = 'Mac OS X' + // Default to native OS except for specific tests + Utils.setFakeOSNone() (proj, j2objcHome, j2objcConfig) = TestingUtils.setupProject(new TestingUtils.ProjectConfig( applyJavaPlugin: true, createReportsDir: true, @@ -62,6 +63,12 @@ class CycleFinderTaskTest { '-sourcepath', '/PROJECT_DIR/src/main/java:/PROJECT_DIR/src/test/java', '-classpath', '/J2OBJC_HOME/lib/j2objc_annotations.jar:/J2OBJC_HOME/lib/j2objc_guava.jar:/J2OBJC_HOME/lib/j2objc_junit.jar:/J2OBJC_HOME/lib/jre_emul.jar:/J2OBJC_HOME/lib/javax.inject-1.jar:/J2OBJC_HOME/lib/jsr305-3.0.0.jar:/J2OBJC_HOME/lib/mockito-core-1.9.5.jar:/PROJECT_DIR/build/classes', ], + // expectedWindowsExecutableAndArgs + [ + 'java', + '-jar', + '/J2OBJC_HOME/lib/cycle_finder.jar', + ], 'IGNORE\n40 CYCLES FOUND\nIGNORE', // stdout null, // stderr new ExecException('Non-Zero Exit')) @@ -73,7 +80,7 @@ class CycleFinderTaskTest { @Test void cycleFinder_Windows() { - Utils.fakeOSName = 'Windows' + Utils.setFakeOSWindows() // Expected number of cycles when using simple method assert 40 == j2objcConfig.cycleFinderExpectedCycles @@ -85,12 +92,16 @@ class CycleFinderTaskTest { MockProjectExec mockProjectExec = new MockProjectExec(proj, j2objcHome) mockProjectExec.demandExecAndReturn( null, + [ + 'INVALID-NEEDS-WINDOWS-SUBSTITUTION', + '-sourcepath', '/PROJECT_DIR/src/main/java;/PROJECT_DIR/src/test/java', + '-classpath', '/J2OBJC_HOME/lib/j2objc_annotations.jar;/J2OBJC_HOME/lib/j2objc_guava.jar;/J2OBJC_HOME/lib/j2objc_junit.jar;/J2OBJC_HOME/lib/jre_emul.jar;/J2OBJC_HOME/lib/javax.inject-1.jar;/J2OBJC_HOME/lib/jsr305-3.0.0.jar;/J2OBJC_HOME/lib/mockito-core-1.9.5.jar;/PROJECT_DIR/build/classes', + ], + // expectedWindowsExecutableAndArgs [ 'java', '-jar', '/J2OBJC_HOME/lib/cycle_finder.jar', - '-sourcepath', '/PROJECT_DIR/src/main/java:/PROJECT_DIR/src/test/java', - '-classpath', '/J2OBJC_HOME/lib/j2objc_annotations.jar:/J2OBJC_HOME/lib/j2objc_guava.jar:/J2OBJC_HOME/lib/j2objc_junit.jar:/J2OBJC_HOME/lib/jre_emul.jar:/J2OBJC_HOME/lib/javax.inject-1.jar:/J2OBJC_HOME/lib/jsr305-3.0.0.jar:/J2OBJC_HOME/lib/mockito-core-1.9.5.jar:/PROJECT_DIR/build/classes', ], 'IGNORE\n40 CYCLES FOUND\nIGNORE', // stdout null, // stderr @@ -117,6 +128,12 @@ class CycleFinderTaskTest { '-sourcepath', '/PROJECT_DIR/src/main/java:/PROJECT_DIR/src/test/java', '-classpath', '/J2OBJC_HOME/lib/j2objc_annotations.jar:/J2OBJC_HOME/lib/j2objc_guava.jar:/J2OBJC_HOME/lib/j2objc_junit.jar:/J2OBJC_HOME/lib/jre_emul.jar:/J2OBJC_HOME/lib/javax.inject-1.jar:/J2OBJC_HOME/lib/jsr305-3.0.0.jar:/J2OBJC_HOME/lib/mockito-core-1.9.5.jar:/PROJECT_DIR/build/classes', ], + // expectedWindowsExecutableAndArgs + [ + 'java', + '-jar', + '/J2OBJC_HOME/lib/cycle_finder.jar', + ], // NOTE: '50' cycles instead of expected 40 '50 CYCLES FOUND', // stdout null, // stderr @@ -152,6 +169,12 @@ class CycleFinderTaskTest { '--whitelist', '/J2OBJC_REPO/jre_emul/cycle_whitelist.txt', '--sourcefilelist', '/J2OBJC_REPO/jre_emul/build_result/java_sources.mf' ], + // expectedWindowsExecutableAndArgs + [ + 'java', + '-jar', + '/J2OBJC_HOME/lib/cycle_finder.jar', + ], '0 CYCLES FOUND', // stdout null, // stderr null) @@ -182,6 +205,12 @@ class CycleFinderTaskTest { '--whitelist', '/J2OBJC_REPO/jre_emul/cycle_whitelist.txt', '--sourcefilelist', '/J2OBJC_REPO/jre_emul/build_result/java_sources.mf' ], + // expectedWindowsExecutableAndArgs + [ + 'java', + '-jar', + '/J2OBJC_HOME/lib/cycle_finder.jar', + ], 'IGNORE\n2 CYCLES FOUND\nIGNORE', // stdout null, // stderr new ExecException('Non-Zero Exit')) diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/MockProjectExec.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/MockProjectExec.groovy index 4f4a8200..a780f8e5 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/MockProjectExec.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/MockProjectExec.groovy @@ -44,8 +44,11 @@ class MockProjectExec { private Project project private String j2objcHome - private static final String j2objcHomeStd = '/J2OBJC_HOME' - private static final String projectDirStd = '/PROJECT_DIR' + + // Windows backslashes are converted to forward slashes for easier verification + private String initWorkingDir = '/INIT_WORKING_DIR' + private static final String j2objcHomeCanonicalized = '/J2OBJC_HOME' + private static final String projectDirCanonicalized = '/PROJECT_DIR' private MockFor mockForProj = new MockFor(Project) private ExecSpec execSpec = new ExecHandleBuilder() @@ -55,8 +58,10 @@ class MockProjectExec { this.project = project this.j2objcHome = j2objcHome + initWorkingDir = TestingUtils.windowsNoFakeAbsolutePath(initWorkingDir) + // This prevents the test error: "Cannot convert relative path . to an absolute file." - execSpec.setWorkingDir('/INIT_WORKING_DIR') + execSpec.setWorkingDir(initWorkingDir) // TODO: find a more elegant way to do this that doesn't require intercepting all methods // http://stackoverflow.com/questions/31129003/mock-gradle-project-exec-using-metaprogramming @@ -124,7 +129,7 @@ class MockProjectExec { if (proxyInstance == null) { proxyInstance = mockForProj.proxyInstance() } - return ( Project ) proxyInstance + return (Project) proxyInstance } void verify() { @@ -146,7 +151,7 @@ class MockProjectExec { void demandCopyAndReturn( @ClosureParams(value = SimpleType.class, options = "org.gradle.api.file.CopySpec") @DelegatesTo(CopySpec) - Closure expectedClosure) { + Closure expectedClosure) { mockForProj.demand.copy { Closure closure -> @@ -219,12 +224,33 @@ class MockProjectExec { } } - void demandExecAndReturn(List expectedCommandLine) { - demandExecAndReturn(null, expectedCommandLine, null, null, null) + void demandExecAndReturn( + List expectedCommandLine, + List expectedWindowsExecutableAndArgs) { + demandExecAndReturn( + null, expectedCommandLine, expectedWindowsExecutableAndArgs, null, null, null) + } + + void demandExecAndReturn( + List expectedCommandLine) { + demandExecAndReturn( + null, expectedCommandLine, null, null, null, null) + } + + void demandExecAndReturn( + String expectWorkingDir, + List expectedCommandLine, + String stdout, String stderr, + Exception exceptionToThrow) { + demandExecAndReturn( + // Empty expectedWindowsExecutableAndArgs + expectWorkingDir, expectedCommandLine, null, stdout, stderr, exceptionToThrow) } void demandExecAndReturn( - String expectWorkingDir, List expectedCommandLine, + String expectWorkingDir, + List expectedCommandLine, + List expectedWindowsExecutableAndArgs, String stdout, String stderr, Exception exceptionToThrow) { @@ -234,29 +260,54 @@ class MockProjectExec { ExecSpec execSpec = new ExecHandleBuilder() // This prevents the test error: "Cannot convert relative path . to an absolute file." - execSpec.setWorkingDir('/INIT_WORKING_DIR') + + execSpec.setWorkingDir(initWorkingDir) ConfigureUtil.configure(closure, execSpec) - assert expectedCommandLine[0] == execSpec.getExecutable().replace(j2objcHome, j2objcHomeStd) - expectedCommandLine.remove(0) + // Substitute first entry in command line with Windows specific executable and args + // Example for translateTask: + // Before: ['j2objc', expectedArgs...] + // After: ['java', '-jar', '/J2OBJC_HOME/lib/j2objc.jar', expectedArgs...] + if (Utils.isWindows() && expectedWindowsExecutableAndArgs != null) { + expectedCommandLine.remove(0) + expectedCommandLine.addAll(0, expectedWindowsExecutableAndArgs) + } - List canonicalizedArgs = execSpec.getArgs().collect { String arg -> - return arg + String expectedExecutable = expectedCommandLine.remove(0) + assert TestingUtils.windowsToForwardSlash(expectedExecutable) == + TestingUtils.windowsToForwardSlash(execSpec.getExecutable()) + .replace(j2objcHome, j2objcHomeCanonicalized) + + // Actual Arguments => canonicalize to simplify writing unit tests + List actualArgsCanonicalized = execSpec.getArgs().collect { String arg -> + return TestingUtils.windowsToForwardSlash(arg) // Use '/J2OBJC_HOME' in unit tests - .replace(j2objcHome, j2objcHomeStd) + .replace(TestingUtils.windowsToForwardSlash(j2objcHome), j2objcHomeCanonicalized) // Use '/PROJECT_DIR' in unit tests - .replace(project.projectDir.path, projectDirStd) - // Use ':' as path separator in unit tests, converted to ';' for Windows - .replace(':', File.pathSeparator) + .replace(TestingUtils.windowsToForwardSlash(project.projectDir.absolutePath), projectDirCanonicalized) } - assert expectedCommandLine == canonicalizedArgs + // Expected Arguments => Windows replacement + // 1) pathSeparator replace, switching ':' for ';' + // 2) separator replace, switching '\\' for '/' + List expectedArgsCanonicalized = expectedCommandLine.collect { String arg -> + assert !arg.contains('\\') + if (Utils.isWindows()) { + assert 'C:' == TestingUtils.windowsAbsolutePathPrefix + // Hacky way of preserving 'C:' prefix for absolute paths + arg = TestingUtils.windowsToForwardSlash(arg).replace(':', ';').replace('C;', 'C:') + } + return arg + } + assert expectedArgsCanonicalized == actualArgsCanonicalized + + // WorkingDir + String actualWorkingDir = + TestingUtils.windowsToForwardSlash(execSpec.getWorkingDir().absolutePath) if (expectWorkingDir == null) { - // Check that it wasn't modified unexpectedly - assert '/INIT_WORKING_DIR' == execSpec.getWorkingDir().absolutePath - } else { - assert expectWorkingDir == execSpec.getWorkingDir().absolutePath + expectWorkingDir = initWorkingDir } + assert TestingUtils.windowsToForwardSlash(expectWorkingDir) == actualWorkingDir if (stdout) { execSpec.getStandardOutput().write(stdout.getBytes('utf-8')) diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTaskTest.groovy index 020a2d5a..ebd86fba 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTaskTest.groovy @@ -39,7 +39,8 @@ class PackLibrariesTaskTest { @Before void setUp() { - Utils.fakeOSName = 'Mac OS X' + // Mac OS X is the only OS that can run this task + Utils.setFakeOSMacOSX() (proj, j2objcHome, j2objcConfig) = TestingUtils.setupProject(new TestingUtils.ProjectConfig( applyJavaPlugin: true, createJ2objcConfig: true @@ -47,8 +48,8 @@ class PackLibrariesTaskTest { } @Test - void testPackLibraries_nonMacOSX() { - Utils.fakeOSName = 'Windows' + void testPackLibraries_Windows() { + Utils.setFakeOSWindows() PackLibrariesTask j2objcPackLibraries = (PackLibrariesTask) proj.tasks.create(name: 'j2objcPackLibraries', type: PackLibrariesTask) { @@ -66,7 +67,7 @@ class PackLibrariesTaskTest { // Expected Activity MockProjectExec mockProjectExec = new MockProjectExec(proj, '/J2OBJC_HOME') mockProjectExec.demandDeleteAndReturn( - "$proj.projectDir/build/packedBinaries/$proj.name-j2objcStaticLibrary/iosDebug") + proj.file("build/packedBinaries/$proj.name-j2objcStaticLibrary/iosDebug").absolutePath) mockProjectExec.demandExecAndReturn( [ 'xcrun', diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy index bdc3a0bc..88977761 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy @@ -42,7 +42,8 @@ class TestTaskTest { @Before void setUp() { - Utils.fakeOSName = 'Mac OS X' + // Mac OS X is the only OS that can run this task + Utils.setFakeOSMacOSX() } @Test @@ -103,14 +104,14 @@ class TestTaskTest { )) j2objcTest = (TestTask) proj.tasks.create(name: 'j2objcTest', type: TestTask) { - testBinaryFile = proj.file("${proj.buildDir}/binaries/testJ2objcExecutable/debug/testJ2objc") + testBinaryFile = proj.file(proj.file('build/binaries/testJ2objcExecutable/debug/testJ2objc')) buildType = 'debug' } } @Test - void testTaskAction_nonMacOSX() { - Utils.fakeOSName = 'Windows' + void testTaskAction_Windows() { + Utils.setFakeOSWindows() setupTask() expectedException.expect(InvalidUserDataException.class) @@ -130,7 +131,7 @@ class TestTaskTest { mockProjectExec.demandExecAndReturn( null, [ - "${proj.buildDir}/j2objcTest/debug/testJ2objc", + proj.file('build/j2objcTest/debug/testJ2objc').absolutePath, "org.junit.runner.JUnitCore", ], 'OK (0 test)', // NOTE: 'test' is singular for stdout @@ -157,7 +158,7 @@ class TestTaskTest { mockProjectExec.demandExecAndReturn( null, [ - "${proj.buildDir}/j2objcTest/debug/testJ2objc", + proj.file('build/j2objcTest/debug/testJ2objc').absolutePath, "org.junit.runner.JUnitCore", ], 'OK (0 test)', // NOTE: 'test' is singular for stdout @@ -178,7 +179,7 @@ class TestTaskTest { mockProjectExec.demandExecAndReturn( null, [ - "${proj.buildDir}/j2objcTest/debug/testJ2objc", + proj.file('build/j2objcTest/debug/testJ2objc').absolutePath, "org.junit.runner.JUnitCore", ], 'OK (1 test)', // NOTE: 'test' is singular for stdout @@ -199,7 +200,7 @@ class TestTaskTest { mockProjectExec.demandExecAndReturn( null, [ - "${proj.buildDir}/j2objcTest/debug/testJ2objc", + proj.file('build/j2objcTest/debug/testJ2objc').absolutePath, "org.junit.runner.JUnitCore", ], 'IGNORE\nOK (2 tests)\nIGNORE', // stdout @@ -220,7 +221,7 @@ class TestTaskTest { mockProjectExec.demandExecAndReturn( null, [ - "${proj.buildDir}/j2objcTest/debug/testJ2objc", + proj.file('build/j2objcTest/debug/testJ2objc').absolutePath, "org.junit.runner.JUnitCore", ], 'OK (2 testXXXX)', // NOTE: invalid stdout fails matchRegexOutputs @@ -235,17 +236,17 @@ class TestTaskTest { private void demandCopyForJ2objcTest(MockProjectExec mockProjectExec) { // Delete test directory mockProjectExec.demandDeleteAndReturn( - "$proj.projectDir/build/j2objcTest/debug") + proj.file('build/j2objcTest/debug').absolutePath) // Copy main resources, test resources and test binary to test directory mockProjectExec.demandMkDirAndReturn( - "$proj.projectDir/build/j2objcTest/debug") + proj.file('build/j2objcTest/debug').absolutePath) mockProjectExec.demandCopyAndReturn( - "$proj.projectDir/build/j2objcTest/debug", - "$proj.projectDir/src/main/resources", - "$proj.projectDir/src/test/resources") + proj.file('build/j2objcTest/debug').absolutePath, + proj.file('src/main/resources').absolutePath, + proj.file('src/test/resources').absolutePath) mockProjectExec.demandCopyAndReturn( - "$proj.projectDir/build/j2objcTest/debug", - "$proj.projectDir/build/binaries/testJ2objcExecutable/debug/testJ2objc") + proj.file('build/j2objcTest/debug').absolutePath, + proj.file('build/binaries/testJ2objcExecutable/debug/testJ2objc').absolutePath) } // TODO: test_Simple() - with some real unit tests diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestingUtils.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestingUtils.groovy index 002459cf..66532d23 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestingUtils.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestingUtils.groovy @@ -35,6 +35,7 @@ import java.util.concurrent.atomic.AtomicInteger @Slf4j class TestingUtils { + static final String windowsAbsolutePathPrefix = 'C:' static AtomicInteger projectIndex = new AtomicInteger() static final class ProjectConfig { @@ -77,12 +78,7 @@ class TestingUtils { proj.pluginManager.apply(JavaPlugin) } - // To satisfy Utils.J2objcHome() - String j2objcHome = File.createTempDir('J2OBJC_HOME', '').path - File localProperties = proj.file('local.properties') - List localPropertiesLines = ["j2objc.home=$j2objcHome"] - localPropertiesLines.addAll(config.extraLocalProperties) - localProperties.write(localPropertiesLines.join('\n')) + String j2objcHome = createLocalPropertiesAndJ2objcHome(proj, config.extraLocalProperties) J2objcConfig j2objcConfig = null if (config.applyJ2objcPlugin) { @@ -102,6 +98,25 @@ class TestingUtils { return [proj, j2objcHome, j2objcConfig] } + private static String createLocalPropertiesAndJ2objcHome( + Project proj, List extraLocalProperties) { + + // Create fake folder for J2OBJC_HOME + String j2objcHome = File.createTempDir('J2OBJC_HOME', '').absolutePath + // Backslashes on Windows are silently dropped when loading properties: + // http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader) + j2objcHome = j2objcHome.replace('\\', '/') + + // Utils.J2objcHome() finds the path through local.properties + File localProperties = proj.file('local.properties') + List localPropertiesLines = new ArrayList<>() + localPropertiesLines.add("j2objc.home=" + j2objcHome) + localPropertiesLines.addAll(extraLocalProperties) + localProperties.write(localPropertiesLines.join('\n')) + + return j2objcHome + } + static J2objcConfig setupProjectJ2objcConfig(ProjectConfig config) { return setupProject(config)[2] as J2objcConfig } @@ -111,4 +126,24 @@ class TestingUtils { Task task = proj.tasks.getByName(name) return task.taskDependencies.getDependencies(task) } + + // Windows accepts both back and forward slashes + // This means it's ok to canonicalize both actual and expected paths before comparison + static String windowsToForwardSlash(String arg) { + if (Utils.isWindows() || Utils.isWindowsNoFake()) { + // Convert to Unix / Mac standard of forward slashes + return arg.replace('\\', '/') + } + return arg + } + + // Needed to trick Gradle methods that require an absolute path + // E.g. proj.files(...) or proj.exec workingDir + static String windowsNoFakeAbsolutePath(String path) { + if (Utils.isWindowsNoFake()) { + assert path.startsWith('/') || path.startsWith('\\') + return windowsAbsolutePathPrefix + path + } + return path + } } diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTaskTest.groovy index a8bae4ad..6ab240fb 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TranslateTaskTest.groovy @@ -35,7 +35,8 @@ class TranslateTaskTest { @Before void setUp() { - Utils.fakeOSName = 'Mac OS X' + // Default to native OS except for specific tests + Utils.setFakeOSNone() (proj, j2objcHome, j2objcConfig) = TestingUtils.setupProject( new TestingUtils.ProjectConfig(applyJavaPlugin: true, createJ2objcConfig: true)) } @@ -47,7 +48,7 @@ class TranslateTaskTest { void translate_BasicArguments() { TranslateTask j2objcTranslate = (TranslateTask) proj.tasks.create( name: 'j2objcTranslate', type: TranslateTask) { - srcGenDir = proj.file("${proj.buildDir}/j2objcSrcGen") + srcGenDir = proj.file(proj.file('build/j2objcSrcGen').absolutePath) } MockProjectExec mockProjectExec = new MockProjectExec(proj, j2objcHome) @@ -56,6 +57,12 @@ class TranslateTaskTest { '-d', '/PROJECT_DIR/build/j2objcSrcGen', '-sourcepath', '/PROJECT_DIR/src/main/java:/PROJECT_DIR/src/test/java', '-classpath', '/J2OBJC_HOME/lib/j2objc_annotations.jar:/J2OBJC_HOME/lib/j2objc_guava.jar:/J2OBJC_HOME/lib/j2objc_junit.jar:/J2OBJC_HOME/lib/jre_emul.jar:/J2OBJC_HOME/lib/javax.inject-1.jar:/J2OBJC_HOME/lib/jsr305-3.0.0.jar:/J2OBJC_HOME/lib/mockito-core-1.9.5.jar:/PROJECT_DIR/build/classes' + ], + // expectedWindowsExecutableAndArgs + [ + 'java', + '-jar', + '/J2OBJC_HOME/lib/j2objc.jar' ]) j2objcTranslate.translate(genNonIncrementalInputs()) @@ -65,20 +72,24 @@ class TranslateTaskTest { @Test void translate_Windows() { - Utils.fakeOSName = 'Windows' + Utils.setFakeOSWindows() TranslateTask j2objcTranslate = (TranslateTask) proj.tasks.create( name: 'j2objcTranslate', type: TranslateTask) { - srcGenDir = proj.file("${proj.buildDir}/j2objcSrcGen") + srcGenDir = proj.file(proj.file('build/j2objcSrcGen').absolutePath) } MockProjectExec mockProjectExec = new MockProjectExec(proj, j2objcHome) mockProjectExec.demandExecAndReturn([ - 'java', - '-jar', - '/J2OBJC_HOME/lib/j2objc.jar', + 'INVALID-NEEDS-WINDOWS-SUBSTITUTION', '-d', '/PROJECT_DIR/build/j2objcSrcGen', - '-sourcepath', '/PROJECT_DIR/src/main/java:/PROJECT_DIR/src/test/java', - '-classpath', '/J2OBJC_HOME/lib/j2objc_annotations.jar:/J2OBJC_HOME/lib/j2objc_guava.jar:/J2OBJC_HOME/lib/j2objc_junit.jar:/J2OBJC_HOME/lib/jre_emul.jar:/J2OBJC_HOME/lib/javax.inject-1.jar:/J2OBJC_HOME/lib/jsr305-3.0.0.jar:/J2OBJC_HOME/lib/mockito-core-1.9.5.jar:/PROJECT_DIR/build/classes' + '-sourcepath', '/PROJECT_DIR/src/main/java;/PROJECT_DIR/src/test/java', + '-classpath', '/J2OBJC_HOME/lib/j2objc_annotations.jar;/J2OBJC_HOME/lib/j2objc_guava.jar;/J2OBJC_HOME/lib/j2objc_junit.jar;/J2OBJC_HOME/lib/jre_emul.jar;/J2OBJC_HOME/lib/javax.inject-1.jar;/J2OBJC_HOME/lib/jsr305-3.0.0.jar;/J2OBJC_HOME/lib/mockito-core-1.9.5.jar;/PROJECT_DIR/build/classes' + ], + // expectedWindowsExecutableAndArgs + [ + 'java', + '-jar', + '/J2OBJC_HOME/lib/j2objc.jar', ]) j2objcTranslate.translate(genNonIncrementalInputs()) @@ -90,12 +101,15 @@ class TranslateTaskTest { void translate_J2objcConfig() { TranslateTask j2objcTranslate = (TranslateTask) proj.tasks.create( name: 'j2objcTranslate', type: TranslateTask) { - srcGenDir = proj.file("${proj.buildDir}/j2objcSrcGen") + srcGenDir = proj.file('build/j2objcSrcGen') } // Tests multiple values with absolute and relative paths - j2objcConfig.generatedSourceDirs('/ABS-GENPATH1', 'REL-GENPATH2') - j2objcConfig.translateSourcepaths('/ABS-SOURCEPATH1', 'REL-SOURCEPATH2') - j2objcConfig.translateClasspaths('/ABS-CLASSPATH1', 'REL-CLASSPATH2') + String absGenPath = TestingUtils.windowsNoFakeAbsolutePath('/ABS-GENPATH') + String absSourcePath = TestingUtils.windowsNoFakeAbsolutePath('/ABS-SOURCEPATH') + String absClassPath = TestingUtils.windowsNoFakeAbsolutePath('/ABS-CLASSPATH') + j2objcConfig.generatedSourceDirs('REL-GENPATH', absGenPath) + j2objcConfig.translateSourcepaths('REL-SOURCEPATH', absSourcePath) + j2objcConfig.translateClasspaths('REL-CLASSPATH', absClassPath) j2objcConfig.translateJ2objcLibs = ['J2OBJC-LIB1', 'J2OBJC-LIB2'] j2objcConfig.translateArgs('-ARG1', '-ARG2') // TODO: add testing for translatePattern @@ -107,9 +121,15 @@ class TranslateTaskTest { mockProjectExec.demandExecAndReturn([ '/J2OBJC_HOME/j2objc', '-d', '/PROJECT_DIR/build/j2objcSrcGen', - '-sourcepath', '/PROJECT_DIR/src/main/java:/PROJECT_DIR/src/test/java:/ABS-SOURCEPATH1:/PROJECT_DIR/REL-SOURCEPATH2:/ABS-GENPATH1:/PROJECT_DIR/REL-GENPATH2', - '-classpath', '/ABS-CLASSPATH1:/PROJECT_DIR/REL-CLASSPATH2:/J2OBJC_HOME/lib/J2OBJC-LIB1:/J2OBJC_HOME/lib/J2OBJC-LIB2:/PROJECT_DIR/build/classes', + '-sourcepath', "/PROJECT_DIR/src/main/java:/PROJECT_DIR/src/test/java:/PROJECT_DIR/REL-SOURCEPATH:$absSourcePath:/PROJECT_DIR/REL-GENPATH:$absGenPath", + '-classpath', "/PROJECT_DIR/REL-CLASSPATH:$absClassPath:/J2OBJC_HOME/lib/J2OBJC-LIB1:/J2OBJC_HOME/lib/J2OBJC-LIB2:/PROJECT_DIR/build/classes", '-ARG1', '-ARG2', + ], + // expectedWindowsExecutableAndArgs + [ + 'java', + '-jar', + '/J2OBJC_HOME/lib/j2objc.jar' ]) j2objcTranslate.translate(genNonIncrementalInputs()) diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy index d32f8223..4827a188 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy @@ -45,6 +45,8 @@ class UtilsTest { @Before void setUp() { + // Default to native OS except for specific tests + Utils.setFakeOSNone() proj = ProjectBuilder.builder().build() } @@ -63,26 +65,77 @@ class UtilsTest { } @Test - void testIsMacOSX_MacOSX() { - Utils.fakeOSName = 'Mac OS X' + void testGetLowerCaseOSName() { + // Redundant method call but included for clarity + Utils.setFakeOSNone() + String realOS = Utils.getLowerCaseOSName(false) + assert realOS == Utils.getLowerCaseOSName(true) + + // Fake OS must be different from current OS + if (Utils.isMacOSX()) { + Utils.setFakeOSWindows() + } else { + Utils.setFakeOSMacOSX() + } + + // OS should now change + assert realOS != Utils.getLowerCaseOSName(false) + // Ignoring fakeOS still gets realOS + assert realOS == Utils.getLowerCaseOSName(true) + } + + @Test + void test_NativeOS() { + // OS should be identified distinctively + Utils.setFakeOSNone() + // Wierd syntax is so that CompileStatic doesn't complain converting boolean to integer + assert 1 == (Utils.isLinux() ? 1 : 0) + (Utils.isMacOSX() ? 1 : 0) + (Utils.isWindows() ? 1 : 0) + } + + @Test + void testSetFakeOSLinux() { + Utils.setFakeOSLinux() + assert Utils.isLinux() + assert !Utils.isMacOSX() + assert !Utils.isWindows() + } + + @Test + void testSetFakeOSMacOSX() { + Utils.setFakeOSMacOSX() + assert !Utils.isLinux() assert Utils.isMacOSX() + assert !Utils.isWindows() } @Test - void testIsMacOSX_nonMacOSX() { - Utils.fakeOSName = 'Windows' + void testSetFakeOSWindows() { + Utils.setFakeOSWindows() + assert !Utils.isLinux() assert !Utils.isMacOSX() + assert Utils.isWindows() + } + + @Test + void testRequireMacOSX_Linux() { + Utils.setFakeOSLinux() + + expectedException.expect(InvalidUserDataException) + expectedException.expectMessage('Mac OS X is required for taskName') + + Utils.requireMacOSX('taskName') } @Test void testRequireMacOSX_MacOSX() { - Utils.fakeOSName = 'Mac OS X' + Utils.setFakeOSMacOSX() Utils.requireMacOSX('taskName') + // NOTE: no exception thrown } @Test - void testRequireMacOSX_nonMacOSX() { - Utils.fakeOSName = 'Windows' + void testRequireMacOSX_Windows() { + Utils.setFakeOSWindows() expectedException.expect(InvalidUserDataException) expectedException.expectMessage('Mac OS X is required for taskName') @@ -91,20 +144,39 @@ class UtilsTest { } @Test - void testGetLowerCaseOSName_fakeOSName() { - Utils.fakeOSName = 'FAKEOS' + void testSeparatorChar_Linux() { + Utils.setFakeOSLinux() + assert '/' == Utils.fileSeparator() + } + + @Test + void testSeparatorChar_MacOSX() { + Utils.setFakeOSMacOSX() + assert '/' == Utils.fileSeparator() + } - assert 'fakeos' == Utils.getLowerCaseOSName() + @Test + void testSeparatorChar_Windows() { + Utils.setFakeOSWindows() + assert '\\' == Utils.fileSeparator() } @Test - void testGetLowerCaseOSName_fakeOSNameEmpty() { - Utils.fakeOSName = '' + void testPathSeparator_Linux() { + Utils.setFakeOSLinux() + assert ':' == Utils.pathSeparator() + } - expectedException.expect(InvalidUserDataException) - expectedException.expectMessage("Tests must set Utils.fakeOSName = 'OS NAME'") + @Test + void testPathSeparator_MacOSX() { + Utils.setFakeOSMacOSX() + assert ':' == Utils.pathSeparator() + } - Utils.getLowerCaseOSName() + @Test + void testPathSeparator_Windows() { + Utils.setFakeOSWindows() + assert ';' == Utils.pathSeparator() } @Test(expected = InvalidUserDataException.class) @@ -172,7 +244,7 @@ class UtilsTest { expectedException.expect(InvalidUserDataException.class) expectedException.expectMessage('Invalid J2ObjC Gradle Plugin property: j2objc.written-invalid-key') - expectedException.expectMessage("From local.properties: $proj.projectDir/local.properties") + expectedException.expectMessage("From local.properties: ${proj.file('local.properties')}") // Check list of valid keys is suggested by checking for a single entry: expectedException.expectMessage('debug.enabled') @@ -183,19 +255,24 @@ class UtilsTest { @Test public void testJ2objcHome_LocalProperties() { // Write j2objc path to local.properties file within the project - String j2objcHomeWritten = File.createTempDir('J2OBJC_HOME', '').path + String j2objcHome = File.createTempDir('J2OBJC_HOME', '').path + // Backslashes on Windows are silently dropped when loading properties: + j2objcHome = j2objcHome.replace('\\', '/') + File localProperties = proj.file('local.properties') - localProperties.write("j2objc.home=$j2objcHomeWritten\n") + localProperties.write("j2objc.home=$j2objcHome\n") String j2objcHomeRead = Utils.j2objcHome(proj) - assert j2objcHomeWritten.equals(j2objcHomeRead) + assert j2objcHome.equals(TestingUtils.windowsToForwardSlash(j2objcHomeRead)) } @Test public void testJ2objcHome_LocalPropertiesWithTrailingSlash() { // Write j2objc path to local.properties file within the project - String j2objcHomePath = File.createTempDir('J2OBJC_HOME', '').path - String j2objcHomePathWithSlash = j2objcHomePath + '/' + String j2objcHomePath = File.createTempDir('J2OBJC_HOME', '').absolutePath + // Backslashes on Windows are silently dropped when loading properties: + String j2objcHomePathWithSlash = (j2objcHomePath + '/').replace('\\', '/') + File localProperties = proj.file('local.properties') localProperties.write("j2objc.home=$j2objcHomePathWithSlash\n") @@ -224,7 +301,7 @@ class UtilsTest { return file.path } - String[] expected = ["${proj.projectDir}/src/main/java"] + String[] expected = [proj.file('src/main/java').absolutePath] assert Arrays.equals(expected, srcDirsPaths) } @@ -295,11 +372,18 @@ class UtilsTest { @Test public void testJoinedPathArg() { - FileCollection fileCollection = proj.files("relative_file1", "relative_file2", "/absoluteFile") + String absolutePath = TestingUtils.windowsNoFakeAbsolutePath(File.separator + 'absoluteFile') + FileCollection fileCollection = + proj.files( + 'relative_file1', + 'relative_file2', + absolutePath) String joinedPathArg = Utils.joinedPathArg(fileCollection) - String expected = "${proj.projectDir}/relative_file1:${proj.projectDir}/relative_file2:/absoluteFile" - expected = expected.replaceAll(':', File.pathSeparator) + String expected = + proj.file('relative_file1').absolutePath + File.pathSeparator + + proj.file('relative_file2').absolutePath + File.pathSeparator + + absolutePath assert expected == joinedPathArg } @@ -361,13 +445,18 @@ class UtilsTest { stderr.write('written-stderr'.getBytes('utf-8')) // Command Succeeded - execSpec.setWorkingDir('/WORKING_DIR') + String nativeWorkingDir = '/WORKING_DIR' + if (Utils.isWindowsNoFake()) { + nativeWorkingDir = 'C:\\WORKING_DIR' + } + execSpec.setWorkingDir(nativeWorkingDir) + String execLogSuccess = Utils.projectExecLog(execSpec, stdout, stderr, true, null) String expectedLogSuccess = 'Command Line Succeeded:\n' + '/EXECUTABLE ARG_1 ARG_2 ARG_3 ARG_4\n' + 'Working Dir:\n' + - '/WORKING_DIR\n' + + nativeWorkingDir + '\n' + 'Standard Output:\n' + 'written-stdout\n' + 'Error Output:\n' + @@ -382,7 +471,7 @@ class UtilsTest { 'Command Line Failed:\n' + '/EXECUTABLE ARG_1 ARG_2 ARG_3 ARG_4\n' + 'Working Dir:\n' + - '/WORKING_DIR\n' + + nativeWorkingDir + '\n' + 'Cause:\n' + 'org.gradle.api.InvalidUserDataException: I\'m the cause of it all!\n' + 'Standard Output:\n' + @@ -397,13 +486,13 @@ class UtilsTest { proj.pluginManager.apply(JavaPlugin) MockProjectExec mockProjectExec = new MockProjectExec(proj, '/J2OBJC_HOME') mockProjectExec.demandDeleteAndReturn( - "$proj.projectDir/SYNC_DIR") + proj.file('SYNC_DIR').absolutePath) mockProjectExec.demandMkDirAndReturn( - "$proj.projectDir/SYNC_DIR") + proj.file('SYNC_DIR').absolutePath) mockProjectExec.demandCopyAndReturn( - "$proj.projectDir/SYNC_DIR", - "$proj.projectDir/src/main/resources", - "$proj.projectDir/src/test/resources") + proj.file('SYNC_DIR').absolutePath, + proj.file('src/main/resources').absolutePath, + proj.file('src/test/resources').absolutePath) Utils.syncResourcesTo(proj, ['main', 'test'], proj.file('SYNC_DIR')) @@ -471,15 +560,29 @@ class UtilsTest { TeeOutputStream stdoutTee = new TeeOutputStream(System.out, stdout) TeeOutputStream stderrTee = new TeeOutputStream(System.err, stderr) + String executableIn = 'echo' + List argsIn = ['written-stdout'] + String stdoutExpected = 'written-stdout\n' + // Windows command execution requires special care + // See: https://github.com/j2objc-contrib/j2objc-gradle/issues/422 + // SO Help: http://stackoverflow.com/questions/515309/what-does-cmd-c-mean + if (Utils.isWindowsNoFake()) { + executableIn = 'cmd' + argsIn = ['/C', 'echo', 'written-stdout'] + // Windows has carriage return and newline together + stdoutExpected = 'written-stdout\r\n' + } + Utils.projectExec(proj, stdout, stderr, null, { - executable 'echo' - args 'written-stdout' + executable executableIn + args argsIn setStandardOutput stdoutTee setErrorOutput stderrTee }) - // newline is added at end of stdout/stderr - assert stdout.toString().equals('written-stdout\n') + String stdoutActual = stdout.toString() + // URLEncoder make debugging easier by showing carriage returns and newlines + assert URLEncoder.encode(stdoutExpected, 'UTF-8').equals(URLEncoder.encode(stdoutActual, 'UTF-8')) assert stderr.toString().isEmpty() } @@ -492,16 +595,28 @@ class UtilsTest { // Tried to do "args '>/dev/stderr'" but it's passed to echo command rather than shell stderr.write('fake-stderr'.getBytes('utf-8')) + String executableIn = 'echo' + List argsIn = ['written-stdout'] + String stdoutExpected = 'written-stdout\n' + String stderrExpected = 'fake-stderr' + if (Utils.isWindowsNoFake()) { + executableIn = 'cmd' + argsIn = ['/C', 'echo', 'written-stdout'] + stdoutExpected = 'written-stdout\r\n' + } + Utils.projectExec(proj, stdout, stderr, null, { - executable 'echo' - args 'echo-stdout' + executable executableIn + args argsIn setStandardOutput stdout setErrorOutput stderr }) - // newline is added at end of stdout/stderr - assert stdout.toString().equals('echo-stdout\n') - assert stderr.toString().equals('fake-stderr') + String stdoutActual = stdout.toString() + String stderrActual = stderr.toString() + // URLEncoder make debugging easier by showing carriage returns and newlines + assert URLEncoder.encode(stdoutExpected, 'UTF-8').equals(URLEncoder.encode(stdoutActual, 'UTF-8')) + assert URLEncoder.encode(stderrExpected, 'UTF-8').equals(URLEncoder.encode(stderrActual, 'UTF-8')) } // TODO: projectExec_NonZeroExit @@ -511,14 +626,26 @@ class UtilsTest { void testProjectExec_HelpfulErrorMessage() { ByteArrayOutputStream stdout = new ByteArrayOutputStream() ByteArrayOutputStream stderr = new ByteArrayOutputStream() + // TODO: get executable passed to projectExec to write to stderr stdout.write('written-stdout'.getBytes('utf-8')) stderr.write('written-stderr'.getBytes('utf-8')) + String executableIn = 'exit' + List argsIn = ['1'] + String expectedCmdLine = 'exit 1' + String expectedExecExceptionMsg = "A problem occurred starting process 'command 'exit''" + if (Utils.isWindowsNoFake()) { + executableIn = 'cmd' + argsIn = ['/C', 'exit', '1'] + expectedCmdLine = 'cmd /C exit 1' + expectedExecExceptionMsg = "Process 'command 'cmd'' finished with non-zero exit value 1" + } + try { Utils.projectExec(proj, stdout, stderr, null, { - executable 'exit' - args '1' + executable executableIn + args argsIn setStandardOutput stdout setErrorOutput stderr }) @@ -527,16 +654,17 @@ class UtilsTest { } catch (InvalidUserDataException exception) { String expected = 'org.gradle.api.InvalidUserDataException: Command Line Failed:\n' + - 'exit 1\n' + + expectedCmdLine + '\n' + 'Working Dir:\n' + proj.projectDir.absolutePath + '\n' + 'Cause:\n' + - "org.gradle.process.internal.ExecException: A problem occurred starting process 'command 'exit''\n" + + "org.gradle.process.internal.ExecException: $expectedExecExceptionMsg\n" + 'Standard Output:\n' + 'written-stdout\n' + 'Error Output:\n' + 'written-stderr' - assert exception.toString().equals(expected) + String actual = exception.toString() + assert expected.equals(actual) } } @@ -545,12 +673,21 @@ class UtilsTest { ByteArrayOutputStream stdout = new ByteArrayOutputStream() ByteArrayOutputStream stderr = new ByteArrayOutputStream() + String executableIn = 'echo' + List argsIn = ['written-stdout'] + String expectedCmdLine = 'echo written-stdout' + if (Utils.isWindowsNoFake()) { + executableIn = 'cmd' + argsIn = ['/C', 'echo', 'written-stdout'] + expectedCmdLine = 'cmd /C echo written-stdout' + } + // String has escaped '/' and '\n' to test escaping String matchRegexOutputs = /(no\/match\n)/ try { Utils.projectExec(proj, stdout, stderr, matchRegexOutputs, { - executable 'echo' - args 'echo-stdout' + executable executableIn + args argsIn setStandardOutput stdout setErrorOutput stderr }) @@ -559,17 +696,22 @@ class UtilsTest { } catch (InvalidUserDataException exception) { String expected = 'org.gradle.api.InvalidUserDataException: Command Line Succeeded:\n' + - 'echo echo-stdout\n' + + expectedCmdLine + '\n' + 'Working Dir:\n' + proj.projectDir.absolutePath + '\n' + 'Cause:\n' + 'org.gradle.api.InvalidUserDataException: Unable to find expected expected output in stdout or stderr\n' + 'Failed Regex Match: /(no\\/match\\n)/\n' + 'Standard Output:\n' + - 'echo-stdout\n' + + 'written-stdout\n' + '\n' + 'Error Output:\n' - assert exception.toString().equals(expected) + String actual = exception.toString() + // Remove carriage returns as they complicate comparison on Windows + if (Utils.isWindowsNoFake()) { + actual = actual.replace('\r', '') + } + assert expected.equals(actual) } } diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTaskTest.groovy index 6d40c3f0..85db2fac 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/XcodeTaskTest.groovy @@ -40,7 +40,8 @@ class XcodeTaskTest { @Before void setUp() { - Utils.fakeOSName = 'Mac OS X' + // Mac OS X is the only OS that can run this task + Utils.setFakeOSMacOSX() proj = ProjectBuilder.builder().build() } @@ -55,7 +56,7 @@ class XcodeTaskTest { j2objcXcode.verifyXcodeArgs() File podFile = j2objcXcode.getPodfileFile() - String expectedPath = proj.file('../ios').absolutePath + '/Podfile' + String expectedPath = proj.file('../ios/Podfile').absolutePath assert expectedPath == podFile.absolutePath } @@ -74,8 +75,8 @@ class XcodeTaskTest { } @Test - void testXcodeConfig_nonMacOSX() { - Utils.fakeOSName = 'Windows' + void testXcodeConfig_Windows() { + Utils.setFakeOSWindows() String j2objcHome J2objcConfig j2objcConfig @@ -102,29 +103,26 @@ class XcodeTaskTest { applyJavaPlugin: true, createJ2objcConfig: true)) - // TODO: should be '../ios' but that needs temp project to be subdirectory of temp dir - // Absolute Path to avoid test error: "Cannot convert relative path ios to an absolute file." - j2objcConfig.xcodeProjectDir = "${proj.projectDir}/ios" + j2objcConfig.xcodeProjectDir = '../ios' j2objcConfig.xcodeTarget = 'IOS-APP' // Needed for podspecDebug proj.file(proj.buildDir).mkdir() // Needed for Podfile proj.file(j2objcConfig.xcodeProjectDir).mkdir() - - XcodeTask j2objcXcode = (XcodeTask) proj.tasks.create(name: 'j2objcXcode', type: XcodeTask) - // Podfile written without podspecDebug reference - File podfile = proj.file('ios/Podfile') + File podfile = proj.file('../ios/Podfile') podfile.write( "target 'IOS-APP' do\n" + "end") + XcodeTask j2objcXcode = (XcodeTask) proj.tasks.create(name: 'j2objcXcode', type: XcodeTask) + // Demands for exec and copy MockProjectExec mockProjectExec = new MockProjectExec(proj, j2objcHome) mockProjectExec.demandExecAndReturn( - "${proj.projectDir}/ios", // working directory + proj.file('../ios').absolutePath, // working directory [ "pod", "install", @@ -147,13 +145,18 @@ class XcodeTaskTest { List expectedPodfile = [ "target 'IOS-APP' do", // Newly added line - "pod '$podNameDebug', :configuration => ['Debug'], :path => '${proj.projectDir}/build'", - "pod '$podNameRelease', :configuration => ['Release'], :path => '${proj.projectDir}/build'", + "pod '$podNameDebug', :configuration => ['Debug'], :path => '$proj.buildDir'", + "pod '$podNameRelease', :configuration => ['Release'], :path => '$proj.buildDir'", "end"] List readPodFileLines = podfile.readLines() assert expectedPodfile == readPodFileLines // Debug Podspec + if (Utils.isWindowsNoFake()) { + // TestingUtils.ProjectConfig converts j2objcHome to forwards slashes on Windows, + // this is due to backslashes in local.properties being silently ignored + j2objcHome = j2objcHome.replace('/', '\\') + } List expectedPodspecDebug = [ "Pod::Spec.new do |spec|", " spec.name = '$podNameDebug'", @@ -166,7 +169,7 @@ class XcodeTaskTest { " spec.libraries = 'ObjC', 'guava', 'javax_inject', 'jre_emul', 'jsr305', 'z', 'icucore', '$libName'", " spec.xcconfig = {", " 'HEADER_SEARCH_PATHS' => '${j2objcHome}/include',", - " 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.projectDir}/build/j2objcOutputs/lib/iosDebug'", + " 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.file('build/j2objcOutputs/lib/iosDebug').absolutePath}'", " }", "end"] File podspecDebug = proj.file("build/${podNameDebug}.podspec") @@ -186,7 +189,7 @@ class XcodeTaskTest { " spec.libraries = 'ObjC', 'guava', 'javax_inject', 'jre_emul', 'jsr305', 'z', 'icucore', '$libName'", " spec.xcconfig = {", " 'HEADER_SEARCH_PATHS' => '${j2objcHome}/include',", - " 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.projectDir}/build/j2objcOutputs/lib/iosRelease'", + " 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.file('build/j2objcOutputs/lib/iosRelease').absolutePath}'", " }", "end"] File podspecRelease = proj.file("build/${podNameRelease}.podspec") @@ -220,7 +223,7 @@ class XcodeTaskTest { assert false, 'Expected Exception' } catch (InvalidUserDataException exception) { assert exception.toString().contains('The Podfile must be created with this command') - assert exception.toString().contains("(cd ${proj.projectDir}/ios && pod init)") + assert exception.toString().contains("(cd ${proj.file('ios').absolutePath} && pod init)") } // Verify no calls to project.copy, project.delete or project.exec