import groovy.io.FileType import org.apache.tools.ant.taskdefs.condition.Os import org.gradle.api.tasks.Exec buildscript { repositories { mavenCentral() jcenter() } dependencies { classpath 'com.eriwen:gradle-js-plugin:1.8.0' classpath 'com.moowork.gradle:gradle-grunt-plugin:0.2' } } apply plugin: 'js' apply plugin: 'grunt' repositories { mavenCentral() } configurations { rhino } dependencies { rhino 'org.mozilla:rhino:1.7R4' } project.ext { packageProps = new groovy.json.JsonSlurper().parseText(new File("package.json").toURL().text) failures = 0; rhinoTestSrc = "out/rhino-test-${packageProps.version}.js" testSrc = 'test/less' testOut = 'out/test' } task runGruntRhino(type: GruntTask) { gruntArgs = "rhino" } combineJs { dependsOn runGruntRhino source = ["dist/less-rhino-${packageProps.version}.js", "test/rhino/test-header.js","dist/lessc-rhino-${packageProps.version}.js"] dest = file(rhinoTestSrc) } task testRhino(type: AllRhinoTests) { // dependsOn 'testRhinoBase' dependsOn 'testRhinoBase', 'testRhinoErrors', 'testRhinoLegacy', 'testRhinoStaticUrls', 'testRhinoCompression', 'testRhinoDebugAll', 'testRhinoDebugComments', 'testRhinoDebugMediaquery', 'testRhinoNoJsError', 'testRhinoSourceMap' } task testRhinoBase(type: RhinoTest) { options = [ '--strict-math=true', '--relative-urls' ] } task testRhinoDebugAll(type: DebugRhinoTest) { options = [ '--strict-math=true', '--line-numbers=all' ] testDir = 'debug' + fs suffix = "-all" } task testRhinoDebugComments(type: DebugRhinoTest) { options = [ '--strict-math=true', '--line-numbers=comments' ] testDir = 'debug' + fs suffix = "-comments" } task testRhinoDebugMediaquery(type: DebugRhinoTest) { options = [ '--strict-math=true', '--line-numbers=mediaquery' ] testDir = 'debug' + fs suffix = "-mediaquery" } task testRhinoErrors(type: RhinoTest) { options = [ '--strict-math=true', '--strict-units=true' ] testDir = 'errors/' expectErrors = true } task testRhinoChyby(type: RhinoTest) { options = [ '--strict-math=true', '--strict-units=true' ] testDir = 'chyby/' // expectErrors = true } task testRhinoNoJsError(type: RhinoTest) { options = [ '--strict-math=true', '--strict-units=true', '--no-js' ] testDir = 'no-js-errors/' expectErrors = true } task testRhinoLegacy(type: RhinoTest) { testDir = 'legacy/' } task testRhinoStaticUrls(type: RhinoTest) { options = [ '--strict-math=true', '--rootpath=folder (1)/' ] testDir = 'static-urls/' } task testRhinoCompression(type: RhinoTest) { options = [ '--compress=true' ] testDir = 'compression/' } task testRhinoSourceMap(type: SourceMapRhinoTest) { options = [ '--strict-math=true', '--strict-units=true'] testDir = 'sourcemaps/' } task setupTest { dependsOn combineJs doLast { file(testOut).deleteDir() } } task clean << { file(rhinoTestSrc).delete() file(testOut).deleteDir() } class SourceMapRhinoTest extends RhinoTest { // helper to get the output map file def getOutputMap(lessFile) { def outFile = project.file(lessFile.path.replace('test/less', project.testOut).replace('.less', '.css')) return project.file(outFile.path + ".map"); } // callback to add SourceMap options to the options list def postProcessOptions(options, lessFile) { def outFile = getOutputMap(lessFile) project.file(outFile.parent).mkdirs() options << "--source-map=${testDir}${lessFile.name.replace('.less','.css')}" options << "--source-map-basepath=${lessRootDir}" options << "--source-map-rootpath=testweb/" options << "--source-map-output-map-file=${outFile}" options } // Callback to validate output def handleResult(exec, out, lessFile) { def actualFile = getOutputMap(lessFile) def expectedFile = project.file(projectDir + fs + "test" + fs + testDir + fs + lessFile.name.replace(".less", ".json")) assert actualFile.text == expectedFile.text } } class DebugRhinoTest extends RhinoTest { def escapeIt(it) { return it.replaceAll("\\\\", "\\\\\\\\").replaceAll("/", "\\\\/").replaceAll(":", "\\\\:").replaceAll("\\.", "\\\\."); } def globalReplacements(input, directory) { def pDirectory = toPlatformFs(directory) def p = lessRootDir + fs + pDirectory def pathimport = p + toPlatformFs("import/") def pathesc = escapeIt(p) def pathimportesc = escapeIt(pathimport) def result = input.replace("{path}", p).replace("{pathesc}", pathesc).replace("{pathimport}", pathimport) return result.replace("{pathimportesc}", pathimportesc).replace("\r\n", "\n") } } class RhinoTest extends DefaultTask { RhinoTest() { dependsOn 'setupTest' } def suffix = "" def testDir = '' def options = [] def expectErrors = false def fs = File.separator; def projectDir = toUpperCaseDriveLetter(System.getProperty("user.dir")); def lessRootDir = projectDir + fs + "test" + fs + "less" def toUpperCaseDriveLetter(path) { if (path.charAt(1)==':' && path.charAt(2)=='\\') { return path.substring(0,1).toUpperCase() + path.substring(1); } return path; } def toPlatformFs(path) { return path.replace('\\', fs).replace('/', fs); } def expectedCssPath(lessFilePath) { lessFilePath.replace('.less', "${suffix}.css").replace("${fs}less${fs}", "${fs}css${fs}"); } def globalReplacements(input, directory) { return input; } def stylize(str, style) { def styles = [ reset : [0, 0], bold : [1, 22], inverse : [7, 27], underline : [4, 24], yellow : [33, 39], green : [32, 39], red : [31, 39], grey : [90, 39] ]; return '\033[' + styles[style][0] + 'm' + str + '\033[' + styles[style][1] + 'm'; } // Callback for subclasses to make any changes to the options def postProcessOptions(options, lessFile) { options } // Callback to validate output def handleResult(exec, out, lessFile) { def actual = out.toString().trim() def actualResult = project.file(lessFile.path.replace('test/less', project.testOut).replace('.less', '.css')) project.file(actualResult.parent).mkdirs() actualResult << actual def expected if (expectErrors) { assert exec.exitValue != 0 expected = project.file(lessFile.path.replace('.less', '.txt')).text.trim(). replace('{path}', lessFile.parent + '/'). replace('{pathhref}', ''). replace('{404status}', '') } else { assert exec.exitValue == 0 def expectedFile = expectedCssPath(lessFile.path) expected = project.file(expectedFile).text.trim() expected = globalReplacements(expected, testDir) } actual=actual.trim() actual = actual.replace('\r\n', '\n') expected = expected.replace('\r\n', '\n') actual = actual.replace("/","\\") expected = expected.replace("/","\\") // println "* actual *" // println actual // new File("actual.txt").write(actual) // println "* expected *" // println expected // new File("expected.txt").write(expected) assert actual == expected actualResult.delete() } @TaskAction def runTest() { int testSuccesses = 0, testFailures = 0, testErrors = 0 project.file('test/less/' + testDir).eachFileMatch(FileType.FILES, ~/.*\.less/) { lessFile -> println "lessfile: $lessFile" if (!project.hasProperty('test') || lessFile.name.startsWith(project.test)) { def out = new java.io.ByteArrayOutputStream() def processedOptions = postProcessOptions([project.rhinoTestSrc, lessFile] + options, lessFile) def execOptions = { main = 'org.mozilla.javascript.tools.shell.Main' // main = 'org.mozilla.javascript.tools.debugger.Main' classpath = project.configurations.rhino args = processedOptions standardOutput = out ignoreExitValue = true } println "rhinoTestSrc: ${project.rhinoTestSrc}" try { def exec = project.javaexec(execOptions) handleResult(exec, out, lessFile) testSuccesses++ println stylize(' ok', 'green') } catch (ex) { println ex println() testErrors++; } catch (AssertionError ae) { println stylize(' failed', 'red') println ae testFailures++ } } else { println stylize(' skipped', 'yellow') } } println stylize(testSuccesses + ' ok', 'green') println stylize(testFailures + ' assertion failed', testFailures == 0 ? 'green' : 'red') println stylize(testErrors + ' errors', testErrors == 0 ? 'green' : 'red') if (testFailures != 0 || testErrors != 0) { project.failures++; } } } class AllRhinoTests extends DefaultTask { AllRhinoTests() { } @TaskAction def runTest() { println stylize(project.failures + ' test suites failed', project.failures == 0 ? 'green' : 'red') } def stylize(str, style) { def styles = [ reset : [0, 0], bold : [1, 22], inverse : [7, 27], underline : [4, 24], yellow : [33, 39], green : [32, 39], red : [31, 39], grey : [90, 39] ]; return '\033[' + styles[style][0] + 'm' + str + '\033[' + styles[style][1] + 'm'; } } class GruntTask extends Exec { private String gruntExecutable = Os.isFamily(Os.FAMILY_WINDOWS) ? "grunt.cmd" : "grunt" private String switches = "--no-color" String gruntArgs = "" public GruntTask() { super() this.setExecutable(gruntExecutable) } public void setGruntArgs(String gruntArgs) { this.args = "$switches $gruntArgs".trim().split(" ") as List } }