Skip to content

Commit

Permalink
Fixing Windows Unit Tests (fixes #402)
Browse files Browse the repository at this point in the history
- Fix all Windows unit tests (fixes #402)
- Small number of unit tests skipped on Windows, marked with TODOs

Testing:
- Fake OS specific file and path separators
- Explicit setFakeOS methods, e.g. setFakeOSWindows()
- Default to using native OS rather than fakeOSName for most unit tests
- Small number of unit tests use fakeOSName
- ‘inTestMustFakeOS’ setting removed
  • Loading branch information
brunobowden committed Aug 25, 2015
1 parent 14f82ba commit 948d20e
Show file tree
Hide file tree
Showing 21 changed files with 543 additions and 208 deletions.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ test {
// of just pointing to an HTML file.
exceptionFormat = 'full'
}
systemProperty 'inTestMustFakeOS', 'true'
}

pluginBundle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class CycleFinderTask extends DefaultTask {

@TaskAction
void cycleFinder() {
String cycleFinderExec = "${getJ2objcHome()}/cycle_finder"
String cycleFinderExec = getJ2objcHome() + Utils.fileSeparator() + 'cycle_finder'
List<String> windowsOnlyArgs = new ArrayList<String>()
if (Utils.isWindows()) {
cycleFinderExec = 'java'
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> getTestNames(Project proj, FileCollection srcFiles, Properties packagePrefixes) {

List<String> 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
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,37 +60,73 @@ 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 String fakeOSName = ''
static void setFakeOSWindows() {
fakeOSName = 'Windows'
}

// Unset fake os, should be needed for @Before method
@VisibleForTesting
static String getLowerCaseOSName() {
static void setFakeOSNone() {
fakeOSName = ''
}

@VisibleForTesting
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')
}

@VisibleForTesting
static final String windowsAbsolutePathPrefix = 'C:'

static boolean isWindowsNoFakeAbsolutePath(String path) {
return isWindowsNoFake() && path.startsWith(windowsAbsolutePathPrefix)
}

static void requireMacOSX(String taskName) {
if (!isMacOSX()) {
throw new InvalidUserDataException(
Expand All @@ -99,6 +135,29 @@ class Utils {
}
}

// Same as File.separator but can mocked using fakeOSName
static String fileSeparator() {
if (isWindows()) {
return '\\'
} else {
return '/'
}
}

// Same as File.pathSeparator but can mocked using fakeOSName
static String pathSeparator() {
if (isWindows()) {
return ';'
} else {
return ':'
}
}

static String osTestCompatPath(String path) {
// Replace the native OS separator char with the possibly faked separator
return path.replace(File.separator, fileSeparator()).replace('/', fileSeparator())
}

static void throwIfNoJavaPlugin(Project proj) {
if (!proj.plugins.hasPlugin('java')) {
String message =
Expand Down Expand Up @@ -172,8 +231,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" +
Expand All @@ -186,15 +245,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)
Expand Down Expand Up @@ -297,7 +354,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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -208,10 +208,12 @@ class XcodeTask extends DefaultTask {
if (path.endsWith('/')) {
throw new InvalidUserDataException("Path shouldn't end with '/': $path")
}
if (relative && path.startsWith('/')) {
// This allows the unit tests to run on Windows using their absolute paths
boolean relativePath = path.startsWith('/') || Utils.isWindowsNoFakeAbsolutePath(path)
if (relative && relativePath) {
throw new InvalidUserDataException("Path shouldn't be absolute: $path")
}
if (!relative && !path.startsWith('/')) {
if (!relative && !relativePath) {
throw new InvalidUserDataException("Path shouldn't be relative: $path")
}
if (path.endsWith('*')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 948d20e

Please sign in to comment.