import org.gradle.plugins.ide.eclipse.model.Library
import java.security.MessageDigest
import java.util.regex.Matcher
+import java.util.concurrent.Executors
+import java.util.concurrent.Future
+import java.util.concurrent.ScheduledExecutorService
+import java.util.concurrent.TimeUnit
import groovy.transform.ExternalizeMethods
import groovy.util.XmlParser
import groovy.xml.XmlUtil
id 'application'
id 'eclipse'
id "com.diffplug.gradle.spotless" version "3.28.0"
- id 'com.github.johnrengelman.shadow' version '4.0.3'
+ id 'com.github.johnrengelman.shadow' version '6.0.0'
id 'com.install4j.gradle' version '10.0.3'
id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree
id 'com.palantir.git-version' version '0.13.0' apply false
jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
jalviewjsJ2sProps = null
jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
+ jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
eclipseWorkspace = null
eclipseBinary = string("")
eclipseVersion = string("")
eclipseDebug = false
+
+ jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}"
+ jalviewjsChromiumProfileDir = "${ext.jalviewjsChromiumUserDir}/${jalviewjs_chromium_profile_name}"
+
// ENDEXT
}
compileTestJava.dependsOn compileJava
-ext.testsFailed = false
+
+test {
+ group = "Verification"
+ description = "Runs all testTaskN tasks)"
+
+ if (useClover) {
+ dependsOn cloverClasses
+ } else { //?
+ dependsOn testClasses
+ }
+
+ // not running tests in this task
+ exclude "**/*"
+}
/* testTask0 is the main test task */
task testTask0(type: Test) {
group = "Verification"
}
}
+task testTask2(type: Test) {
+ group = "Verification"
+ description = "Tests that need to be isolated from the main test run"
+ useTestNG() {
+ includeGroups name
+ excludeGroups testng_excluded_groups.split(",")
+ preserveOrder true
+ useDefaultListeners=true
+ }
+}
+task testTask3(type: Test) {
+ group = "Verification"
+ description = "Tests that need to be isolated from the main test run"
+ useTestNG() {
+ includeGroups name
+ excludeGroups testng_excluded_groups.split(",")
+ preserveOrder true
+ useDefaultListeners=true
+ }
+}
+
+/* insert more testTaskNs here -- change N to next digit or other string */
+/*
+task testTaskN(type: Test) {
+ group = "Verification"
+ description = "Tests that need to be isolated from the main test run"
+ useTestNG() {
+ includeGroups name
+ excludeGroups testng_excluded_groups.split(",")
+ preserveOrder true
+ useDefaultListeners=true
+ }
+}
+*/
+
/*
* adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
* to summarise test results from all Test tasks
testTask.mustRunAfter "testTask0"
testTask.testLogging { logging ->
- events TestLogEvent.FAILED,
- TestLogEvent.SKIPPED,
- TestLogEvent.STANDARD_OUT,
- TestLogEvent.STANDARD_ERROR
+ events TestLogEvent.FAILED
+// TestLogEvent.SKIPPED,
+// TestLogEvent.STANDARD_OUT,
+// TestLogEvent.STANDARD_ERROR
exceptionFormat TestExceptionFormat.FULL
showExceptions true
showCauses true
showStackTraces true
+ if (test_output) {
+ showStandardStreams true
+ }
+ info.events = [ TestLogEvent.FAILED ]
}
+ if (OperatingSystem.current().isMacOsX()) {
+ testTask.systemProperty "apple.awt.UIElement", "true"
+ testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
+ }
+
+
ignoreFailures = true // Always try to run all tests for all modules
afterSuite { desc, result ->
+ if (desc.parent)
+ return // Only summarize results for whole modules
- if (desc.parent) return // Only summarize results for whole modules
+ def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
- def summary = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
-
- // Add reports in `testsResults`, keep failed suites at the end
- if (result.resultType == TestResult.ResultType.SUCCESS) {
- rootProject.ext.testsResults.add(0, summary)
- } else {
- rootProject.ext.testsResults.add(summary)
- }
-
- if (result.resultType == TestResult.ResultType.FAILURE) {
- testsFailed = true
- }
+ rootProject.ext.testsResults.add(resultsInfo)
}
// from original test task
jvmArgs += additional_compiler_args
doFirst {
+ // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
+ // testTasks that include the tests, and exclude all from the others.
+ // get --test argument
+ filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
+ // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
+ // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
+
+
if (useClover) {
println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
}
}
+
+ /* don't fail on no matching tests (so --tests will run across all testTasks) */
+ testTask.filter.setFailOnNoMatchingTests(false)
+
+ /* ensure the "test" task dependsOn all the testTasks */
+ test.dependsOn testTask
}
gradle.buildFinished {
printResults allResults
allResults.each {r ->
if (r[2].resultType == TestResult.ResultType.FAILURE)
- throw GradleException("Failed tests (buildFinished)!")
+ throw new GradleException("Failed tests!")
}
}
}
+private static String colString(styler, col, colour, text) {
+ return col?"${styler[colour](text)}":text
+}
+
+private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
+ def colour = 'black'
+ def text = rt
+ def nocol = false
+ if (rc == 0) {
+ text = "-----"
+ nocol = true
+ } else {
+ switch(rt) {
+ case TestResult.ResultType.SUCCESS:
+ colour = 'green'
+ break;
+ case TestResult.ResultType.FAILURE:
+ colour = 'red'
+ break;
+ default:
+ nocol = true
+ break;
+ }
+ }
+ StringBuilder sb = new StringBuilder()
+ sb.append("${pn}")
+ if (tn != null)
+ sb.append(":${tn}")
+ sb.append(" results: ")
+ sb.append(colString(s, col && !nocol, colour, text))
+ sb.append(" (")
+ sb.append("${rc} tests, ")
+ sb.append(colString(s, col && rs > 0, 'green', rs))
+ sb.append(" successes, ")
+ sb.append(colString(s, col && rf > 0, 'red', rf))
+ sb.append(" failures, ")
+ sb.append("${rsk} skipped) in ${t}")
+ return sb.toString()
+}
+
private static void printResults(allResults) {
+
// styler from https://stackoverflow.com/a/56139852
def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
def maxLength = 0
def failedTests = false
def summaryLines = []
- allResults.each {
+ def totalcount = 0
+ def totalsuccess = 0
+ def totalfail = 0
+ def totalskip = 0
+ def totaltime = TimeCategory.getSeconds(0)
+ // sort on project name then task name
+ allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
def projectName = it[0]
def taskName = it[1]
def result = it[2]
def time = it[3]
def report = it[4]
- def colour = 'black'
- switch(result.resultType) {
- case TestResult.ResultType.SUCCESS:
- colour = 'green'
- break;
- case TestResult.ResultType.FAILURE:
- colour = 'red'
- failedTests = true
- break;
- default:
- colour = 'yellow'
- break;
- }
- def summaryCol = "${projectName}:${taskName} results: ${styler[colour](result.resultType)} (" +
- "${result.testCount} tests, " +
- (result.successfulTestCount > 0 ? "${styler['green'](result.successfulTestCount)} successes" : "${result.successfulTestCount} successes") + ", " +
- (result.failedTestCount > 0 ? "${styler['red'](result.failedTestCount)} failures" : "${result.failedTestCount} failures") + ", " +
- "${result.skippedTestCount} skipped" +
- ") " + "in ${time}"
- def summaryPlain = "${projectName}:${taskName} results: ${result.resultType} (" +
- "${result.testCount} tests, " +
- "${result.successfulTestCount} successes, " +
- "${result.failedTestCount} failures, " +
- "${result.skippedTestCount} skipped" +
- ") " + "in ${time}"
+ def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
+ def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
def reportLine = "Report file: ${report}"
def ls = summaryPlain.length()
def lr = reportLine.length()
maxLength = m
def info = [ls, summaryCol, reportLine]
summaryLines.add(info)
+ failedTests |= result.resultType == TestResult.ResultType.FAILURE
+ totalcount += result.testCount
+ totalsuccess += result.successfulTestCount
+ totalfail += result.failedTestCount
+ totalskip += result.skippedTestCount
+ totaltime += time
+ }
+ def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
+ def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
+ def tls = totalSummaryPlain.length()
+ if (tls > maxLength)
+ maxLength = tls
+ def info = [tls, totalSummaryCol, null]
+ summaryLines.add(info)
+
+ def allSummaries = []
+ for(sInfo : summaryLines) {
+ def ls = sInfo[0]
+ def summary = sInfo[1]
+ def report = sInfo[2]
+
+ StringBuilder sb = new StringBuilder()
+ sb.append("│" + summary + " " * (maxLength - ls) + "│")
+ if (report != null) {
+ sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
+ }
+ allSummaries += sb.toString()
}
println "┌${"${"─" * maxLength}"}┐"
-
- println summaryLines.collect {info ->
- def ls = info[0]
- def summary = info[1]
- def report = info[2]
-
- return "│" + summary + " " * (maxLength - ls) + "│" + "\n" +
- "│" + report + " " * (maxLength - report.length()) + "│"
-
- }.join("\n├${"${"─" * maxLength}"}┤\n")
-
+ println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
println "└${"${"─" * maxLength}"}┘"
}
/* END of test tasks results summary */
-task verifyTestStatus {
- group = "Verification"
- description = "Task that FAILs the build if any tests failed"
- doLast {
- if (testsFailed) {
- throw new GradleException("There were failing tests!")
- }
- }
-}
-
-test {
- // from original test task
- if (useClover) {
- dependsOn cloverClasses
- } else { //?
- dependsOn testClasses
- }
- dependsOn tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}
- finalizedBy verifyTestStatus
-
-
- // not running tests in this task
- exclude "**/*"
-}
-
task compileLinkCheck(type: JavaCompile) {
options.fork = true
if (buildDist) {
dependsOn makeDist
}
- from ("${jalviewDir}/${libDistDir}") {
- include("*.jar")
- }
- manifest {
- attributes "Implementation-Version": JALVIEW_VERSION,
- "Application-Name": applicationName
+
+ def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
+ def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
+ def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
+ from groovyJars
+ from otherJars
+
+ // we need to include the groovy-swing Include-Package for it to run in the shadowJar
+ doFirst {
+ def jarFileManifests = []
+ groovyJars.each { jarFile ->
+ def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
+ if (mf != null) {
+ jarFileManifests += mf
+ }
+ }
+
+ manifest {
+ attributes "Implementation-Version": JALVIEW_VERSION, "Application-Name": applicationName
+ from (jarFileManifests) {
+ eachEntry { details ->
+ if (!details.key.equals("Import-Package")) {
+ details.exclude()
+ }
+ }
+ }
+ }
}
duplicatesStrategy "INCLUDE"
props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
- props.put("getdown_txt_ui.icon", "${getdownImagesBuildDir}/${getdown_icon}")
- props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesBuildDir}/${getdown_mac_dock_icon}")
+ props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
+ props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
}
props.put("getdown_txt_title", jalview_name)
'WRAPPER_LINK': getdownWrapperLink,
'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
+ 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
'INSTALLER_NAME': install4jInstallerName,
'INSTALL4J_UTILS_DIR': install4j_utils_dir,
into project.name
def EXCLUDE_FILES=[
+ "dist/*",
"build/*",
"bin/*",
"test-output/",
task jalviewjsTransferUnzipLib {
- def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
+ def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip").sort()
doLast {
zipFiles.each { file_zip ->
task jalviewjsTransferUnzipAllLibs {
- dependsOn jalviewjsTransferUnzipSwingJs
dependsOn jalviewjsTransferUnzipLib
+ dependsOn jalviewjsTransferUnzipSwingJs
}
+
+
task jalviewjsCreateJ2sSettings(type: WriteProperties) {
group "JalviewJS"
description "Create the alternative j2s file from the j2s.* properties"
include "**"
}
- // should this be exclude really ?
+ // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
duplicatesStrategy "INCLUDE"
outputs.files outputFiles
classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
main = "com.google.javascript.jscomp.CommandLineRunner"
jvmArgs = [ "-Dfile.encoding=UTF-8" ]
- args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
+ args = [ "--compilation_level", jalviewjs_closure_compiler_optimization_level, "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
maxHeapSize = "2g"
msg = "\nRunning '"+commandLine.join(' ')+"'\n"
}
+task jalviewjsCopyStderrLaunchFile(type: Copy) {
+ from file(jalviewjs_stderr_launch)
+ into jalviewjsSiteDir
+
+ inputs.file jalviewjs_stderr_launch
+ outputs.file jalviewjsStderrLaunchFilename
+}
+
+task cleanJalviewjsChromiumUserDir {
+ doFirst {
+ delete jalviewjsChromiumUserDir
+ }
+ outputs.dir jalviewjsChromiumUserDir
+ // always run when depended on
+ outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
+}
+
+task jalviewjsChromiumProfile {
+ dependsOn cleanJalviewjsChromiumUserDir
+ mustRunAfter cleanJalviewjsChromiumUserDir
+
+ def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
+
+ doFirst {
+ mkdir jalviewjsChromiumProfileDir
+ firstRun.text = ""
+ }
+ outputs.file firstRun
+}
+
+task jalviewjsLaunchTest {
+ group "Test"
+ description "Check JalviewJS opens in a browser"
+ dependsOn jalviewjsBuildSite
+ dependsOn jalviewjsCopyStderrLaunchFile
+ dependsOn jalviewjsChromiumProfile
+
+ def macOS = OperatingSystem.current().isMacOsX()
+ def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
+ if (chromiumBinary.startsWith("~/")) {
+ chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
+ }
+
+ def stdout
+ def stderr
+ doFirst {
+ def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
+
+ def binary = file(chromiumBinary)
+ if (!binary.exists()) {
+ throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
+ }
+ stdout = new ByteArrayOutputStream()
+ stderr = new ByteArrayOutputStream()
+ def execStdout
+ def execStderr
+ if (jalviewjs_j2s_to_console.equals("true")) {
+ execStdout = new org.apache.tools.ant.util.TeeOutputStream(
+ stdout,
+ System.out)
+ execStderr = new org.apache.tools.ant.util.TeeOutputStream(
+ stderr,
+ System.err)
+ } else {
+ execStdout = stdout
+ execStderr = stderr
+ }
+ // macOS not running properly with timeout arguments
+ def execArgs = macOS ? [] : [
+ "--virtual-time-budget=${timeoutms}",
+ ]
+ execArgs += [
+ "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
+ "--headless=new",
+ "--disable-gpu",
+ "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
+ "--profile-directory=${jalviewjs_chromium_profile_name}",
+ "--allow-file-access-from-files",
+ "--enable-logging=stderr",
+ "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
+ ]
+
+ if (true || macOS) {
+ ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
+ Future f1 = executor.submit(
+ () -> {
+ exec {
+ standardOutput = execStdout
+ errorOutput = execStderr
+ executable(chromiumBinary)
+ args(execArgs)
+ println "COMMAND: '"+commandLine.join(" ")+"'"
+ }
+ executor.shutdownNow()
+ }
+ )
+
+ def noChangeBytes = 0
+ def noChangeIterations = 0
+ executor.scheduleAtFixedRate(
+ () -> {
+ String stderrString = stderr.toString()
+ // shutdown the task if we have a success string
+ if (stderrString.contains(jalviewjs_desktop_init_string)) {
+ f1.cancel()
+ Thread.sleep(1000)
+ executor.shutdownNow()
+ }
+ // if no change in stderr for 10s then also end
+ if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
+ executor.shutdownNow()
+ }
+ if (stderrString.length() == noChangeBytes) {
+ noChangeIterations++
+ } else {
+ noChangeBytes = stderrString.length()
+ noChangeIterations = 0
+ }
+ },
+ 1, 1, TimeUnit.SECONDS)
+
+ executor.schedule(new Runnable(){
+ public void run(){
+ f1.cancel()
+ executor.shutdownNow()
+ }
+ }, timeoutms, TimeUnit.MILLISECONDS)
+
+ executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS)
+ executor.shutdownNow()
+ }
+
+ }
+
+ doLast {
+ def found = false
+ stderr.toString().eachLine { line ->
+ if (line.contains(jalviewjs_desktop_init_string)) {
+ println("Found line '"+line+"'")
+ found = true
+ return
+ }
+ }
+ if (!found) {
+ throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
+ }
+ }
+}
+
+
task jalviewjs {
group "JalviewJS"
- description "Build the site"
+ description "Build the JalviewJS site and run the launch test"
dependsOn jalviewjsBuildSite
+ dependsOn jalviewjsLaunchTest
}
-