From 9b06f8dde42c7a199cd2985fc0c12ddecba93aab Mon Sep 17 00:00:00 2001 From: maxulysse Date: Tue, 28 Oct 2025 11:58:39 +0100 Subject: [PATCH 1/2] better tests for ansi color codes --- tests/filterNextflowOutput/main.nf | 91 ++++++++++---- tests/filterNextflowOutput/main.nf.test | 41 +++++++ tests/filterNextflowOutput/main.nf.test.snap | 121 ++++++++++++++++++- 3 files changed, 222 insertions(+), 31 deletions(-) diff --git a/tests/filterNextflowOutput/main.nf b/tests/filterNextflowOutput/main.nf index f694f7e..c0a257a 100644 --- a/tests/filterNextflowOutput/main.nf +++ b/tests/filterNextflowOutput/main.nf @@ -1,8 +1,13 @@ include { FASTQC } from './modules/local/fastqc' -params.failure = false -params.outdir = "results" +params.failure = false +params.outdir = "results" +params.monochrome_logs = false + workflow { + + colors = getColors(params.monochrome_logs) + input = channel.of( 'sample_1', 'sample_2', @@ -27,20 +32,20 @@ workflow { // Print a complex nf-core/pipeline logo (ie SAREK for example) log.info( """ -\033[2m----------------------------------------------------\033[0m- - \033[0;32m,--.\033[0;30m/\033[0;32m,-.\033[0m -\033[0;34m ___ __ __ __ ___ \033[0;32m/,-._.--~\'\033[0m -\033[0;34m |\\ | |__ __ / ` / \\ |__) |__ \033[0;33m} {\033[0m -\033[0;34m | \\| | \\__, \\__/ | \\ |___ \033[0;32m\\`-._,-`-,\033[0m - \033[0;32m`._,._,\'\033[0m - -\033[0;37m ____\033[0m -\033[0;37m .´ _ `.\033[0m -\033[0;37m / \033[0;32m|\\\033[0m`-_ \\\033[0m \033[0;34m __ __ ___ \033[0m -\033[0;37m | \033[0;32m| \\\033[0m `-|\033[0m \033[0;34m|__` /\\ |__) |__ |__/\033[0m -\033[0;37m \\ \033[0;32m| \\\033[0m /\033[0m \033[0;34m.__| /¯¯\\ | \\ |___ | \\\033[0m -\033[0;37m `\033[0;32m|\033[0m____\033[0;32m\\\033[0m´\033[0m --\033[2m----------------------------------------------------\033[0m- +${colors.dim}----------------------------------------------------${colors.reset}- + ${colors.green},--.${colors.black}/${colors.green},-.${colors.reset} +${colors.blue} ___ __ __ __ ___ ${colors.green}/,-._.--~\'${colors.reset} +${colors.blue} |\\ | |__ __ / ` / \\ |__) |__ ${colors.yellow}} {${colors.reset} +${colors.blue} | \\| | \\__, \\__/ | \\ |___ ${colors.green}\\`-._,-`-,${colors.reset} + ${colors.green}`._,._,\'${colors.reset} + +${colors.white} ____${colors.reset} +${colors.white} .´ _ `.${colors.reset} +${colors.white} / ${colors.green}|\\${colors.reset}`-_ \\${colors.reset} ${colors.blue} __ __ ___ ${colors.reset} +${colors.white} | ${colors.green}| \\${colors.reset} `-|${colors.reset} ${colors.blue}|__` /\\ |__) |__ |__/${colors.reset} +${colors.white} \\ ${colors.green}| \\${colors.reset} /${colors.reset} ${colors.blue}.__| /¯¯\\ | \\ |___ | \\${colors.reset} +${colors.white} `${colors.green}|${colors.reset}____${colors.green}\\${colors.reset}´${colors.reset} +-${colors.dim}----------------------------------------------------${colors.reset}- nf-core/pipeline1 1.0dev nf-core/pipeline2 1.0 nf-core/pipeline3 1.1.0dev @@ -69,17 +74,53 @@ runName[true]: ${workflow.runName} log.info("outdir : ${params.outdir}") println("println message") - log.info("\033[0;34mlog.info message\033[0m") - log.warn("\033[0;33mlog.warn message\033[0m") - log.error("\033[0;31mlog.error message\033[0m") + log.info("${colors.blue}log.info message${colors.reset}") + log.warn("${colors.yellow}log.warn message${colors.reset}") + log.error("${colors.red}log.error message${colors.reset}") println("println with path: ${test_file}") - log.info("\033[0;34mlog.info with path: ${test_file}\033[0m") - log.warn("\033[0;33mlog.warn with path: ${test_file}\033[0m") - log.error("\033[0;31mlog.error with path: ${test_file}\033[0m") + log.info("${colors.blue}log.info with path: ${test_file}${colors.reset}") + log.warn("${colors.yellow}log.warn with path: ${test_file}${colors.reset}") + log.error("${colors.red}log.error with path: ${test_file}${colors.reset}") if (params.failure) { - System.err.println("\033[1;31mSystem error with path: ${test_file}\033[0m") - System.err.println("\033[1;31mSystem error message\033[0m") - error("\033[1;31mError with path: ${test_file}\033[0m") + System.err.println("${colors.bred}System error with path: ${test_file}${colors.reset}") + System.err.println("${colors.bred}System error message${colors.reset}") + error("${colors.bred}Error with path: ${test_file}${colors.reset}") } } + +def getColors(monochrome_logs = true) { + + def colorsDict = [:] + + // Reset / Meta + colorsDict['reset'] = monochrome_logs ? '' : "\033[0m" + colorsDict['bold'] = monochrome_logs ? '' : "\033[1m" + colorsDict['dim'] = monochrome_logs ? '' : "\033[2m" + colorsDict['underlined'] = monochrome_logs ? '' : "\033[4m" + colorsDict['blink'] = monochrome_logs ? '' : "\033[5m" + colorsDict['reverse'] = monochrome_logs ? '' : "\033[7m" + colorsDict['hidden'] = monochrome_logs ? '' : "\033[8m" + + // Regular Colors + colorsDict['black'] = monochrome_logs ? '' : "\033[0;30m" + colorsDict['red'] = monochrome_logs ? '' : "\033[0;31m" + colorsDict['green'] = monochrome_logs ? '' : "\033[0;32m" + colorsDict['yellow'] = monochrome_logs ? '' : "\033[0;33m" + colorsDict['blue'] = monochrome_logs ? '' : "\033[0;34m" + colorsDict['purple'] = monochrome_logs ? '' : "\033[0;35m" + colorsDict['cyan'] = monochrome_logs ? '' : "\033[0;36m" + colorsDict['white'] = monochrome_logs ? '' : "\033[0;37m" + + // Bold + colorsDict['bblack'] = monochrome_logs ? '' : "\033[1;30m" + colorsDict['bred'] = monochrome_logs ? '' : "\033[1;31m" + colorsDict['bgreen'] = monochrome_logs ? '' : "\033[1;32m" + colorsDict['byellow'] = monochrome_logs ? '' : "\033[1;33m" + colorsDict['bblue'] = monochrome_logs ? '' : "\033[1;34m" + colorsDict['bpurple'] = monochrome_logs ? '' : "\033[1;35m" + colorsDict['bcyan'] = monochrome_logs ? '' : "\033[1;36m" + colorsDict['bwhite'] = monochrome_logs ? '' : "\033[1;37m" + + return colorsDict +} diff --git a/tests/filterNextflowOutput/main.nf.test b/tests/filterNextflowOutput/main.nf.test index 0cd6f71..0f2fe88 100644 --- a/tests/filterNextflowOutput/main.nf.test +++ b/tests/filterNextflowOutput/main.nf.test @@ -46,6 +46,47 @@ nextflow_pipeline { } } + test("Capture both stdout and stderr and keep ANSI codes - monochrome") { + + tag 'ansi' + tag 'both' + tag 'stderr' + tag 'stdout' + + when { + params { + monochrome_logs = true + } + } + + then { + def filtered_stdout = filterNextflowOutput(workflow.stdout, keepAnsi: true) + def filtered_stderr = filterNextflowOutput(workflow.stderr, keepAnsi: true) + + // Print the stdout + println "workflow.stdout [${workflow.stdout.size()}]:" + println workflow.stdout.join('\n') + + // Print the filtered stdout + println "filtered_stdout: [${filtered_stdout.size()}]" + println filtered_stdout.join('\n') + + // Print the stderr + println "workflow.stderr [${workflow.stderr.size()}]:" + println workflow.stderr.join('\n') + + // Print the filtered stderr + println "filtered_stderr: [${filtered_stderr.size()}]" + println filtered_stderr.join('\n') + + assert workflow.success + assert snapshot( + filterNextflowOutput(workflow.stdout, keepAnsi: true, ignore: ["Submitted process"]), + filterNextflowOutput(workflow.stderr, keepAnsi: true)?:"no stderr", + filterNextflowOutput(workflow.stdout + workflow.stderr, keepAnsi: true) + ).match() + } + } test("Capture both stdout and stderr and keep ANSI codes") { diff --git a/tests/filterNextflowOutput/main.nf.test.snap b/tests/filterNextflowOutput/main.nf.test.snap index cf82153..5f93469 100644 --- a/tests/filterNextflowOutput/main.nf.test.snap +++ b/tests/filterNextflowOutput/main.nf.test.snap @@ -106,10 +106,119 @@ ] ], "meta": { - "nf-test": "0.9.2", - "nextflow": "25.04.6" + "nf-test": "0.9.3", + "nextflow": "25.10.0" }, - "timestamp": "2025-09-05T09:42:24.347773785" + "timestamp": "2025-10-28T11:56:48.873208114" + }, + "Capture both stdout and stderr and keep ANSI codes - monochrome": { + "content": [ + [ + "N E X T F L O W ~ version [VERSION]", + "Launching `[PATH]/tests/filterNextflowOutput/./main.nf` [RUN_NAME] DSL2 - revision: [REVISION]", + "-----------------------------------------------------", + " ,--./,-.", + " ___ __ __ __ ___ /,-._.--~'", + " |\\ | |__ __ / ` / \\ |__) |__ } {", + " | \\| | \\__, \\__/ | \\ |___ \\`-._,-`-,", + " `._,._,'", + " ____", + " .\u00b4 _ `.", + " / |\\`-_ \\ __ __ ___ ", + " | | \\ `-| |__` /\\ |__) |__ |__/", + " \\ | \\ / .__| /\u00af\u00af\\ | \\ |___ | \\", + " `|____\\\u00b4", + "------------------------------------------------------", + "nf-core/pipeline1 [VERSION]", + "nf-core/pipeline2 [VERSION]", + "nf-core/pipeline3 [VERSION]", + "nf-core/pipeline4 [VERSION]", + "container: [CONTAINER]", + "image: [CONTAINER]", + "virtualenv: [CONTAINER]", + "profile: test,[CONTAINER],[CONTAINER],[CONTAINER]", + "runName[false]: test_run_name", + "runName[false]: amazing_mercury", + "runName[false]: [amazing_mercury]", + "runName[true]: [RUN_NAME]", + "HOME : [PATH]", + "NXF_CACHE_DIR : null", + "NXF_CONDA_CACHEDIR : null", + "NXF_HOME : [PATH]", + "NXF_SINGULARITY_CACHEDIR : [PATH]", + "NXF_SINGULARITY_LIBRARYDIR: [PATH]", + "NXF_TEMP : null", + "NXF_WORK : null", + "userName : [USER]", + "outdir : results", + "println message", + "log.info message", + "println with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt", + "log.info with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt", + " -- Check '[PATH]/.nf-test/tests/[NFT_HASH]/meta/nextflow.log' file for details", + "ERROR ~ log.error message", + "ERROR ~ log.error with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt", + "WARN: log.warn message", + "WARN: log.warn with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt" + ], + "no stderr", + [ + "N E X T F L O W ~ version [VERSION]", + "Launching `[PATH]/tests/filterNextflowOutput/./main.nf` [RUN_NAME] DSL2 - revision: [REVISION]", + "-----------------------------------------------------", + " ,--./,-.", + " ___ __ __ __ ___ /,-._.--~'", + " |\\ | |__ __ / ` / \\ |__) |__ } {", + " | \\| | \\__, \\__/ | \\ |___ \\`-._,-`-,", + " `._,._,'", + " ____", + " .\u00b4 _ `.", + " / |\\`-_ \\ __ __ ___ ", + " | | \\ `-| |__` /\\ |__) |__ |__/", + " \\ | \\ / .__| /\u00af\u00af\\ | \\ |___ | \\", + " `|____\\\u00b4", + "------------------------------------------------------", + "nf-core/pipeline1 [VERSION]", + "nf-core/pipeline2 [VERSION]", + "nf-core/pipeline3 [VERSION]", + "nf-core/pipeline4 [VERSION]", + "container: [CONTAINER]", + "image: [CONTAINER]", + "virtualenv: [CONTAINER]", + "profile: test,[CONTAINER],[CONTAINER],[CONTAINER]", + "runName[false]: test_run_name", + "runName[false]: amazing_mercury", + "runName[false]: [amazing_mercury]", + "runName[true]: [RUN_NAME]", + "HOME : [PATH]", + "NXF_CACHE_DIR : null", + "NXF_CONDA_CACHEDIR : null", + "NXF_HOME : [PATH]", + "NXF_SINGULARITY_CACHEDIR : [PATH]", + "NXF_SINGULARITY_LIBRARYDIR: [PATH]", + "NXF_TEMP : null", + "NXF_WORK : null", + "userName : [USER]", + "outdir : results", + "println message", + "log.info message", + "println with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt", + "log.info with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt", + " -- Check '[PATH]/.nf-test/tests/[NFT_HASH]/meta/nextflow.log' file for details", + "ERROR ~ log.error message", + "ERROR ~ log.error with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt", + "WARN: log.warn message", + "WARN: log.warn with path: [PATH]/.nf-test/tests/[NFT_HASH]/results/test.txt", + "[NXF_HASH] Submitted process > FASTQC (sample_1)", + "[NXF_HASH] Submitted process > FASTQC (sample_2)", + "[NXF_HASH] Submitted process > FASTQC (sample_3)" + ] + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-28T11:47:22.144274019" }, "Capture both stdout and stderr and keep ANSI codes": { "content": [ @@ -215,9 +324,9 @@ ] ], "meta": { - "nf-test": "0.9.2", - "nextflow": "25.04.6" + "nf-test": "0.9.3", + "nextflow": "25.10.0" }, - "timestamp": "2025-09-05T09:42:26.74594254" + "timestamp": "2025-10-28T11:56:58.291151246" } } \ No newline at end of file From 9364415fb4fa84ac9bc060ab986936f6af790ff4 Mon Sep 17 00:00:00 2001 From: maxulysse Date: Tue, 28 Oct 2025 12:03:00 +0100 Subject: [PATCH 2/2] pre-commit --- tests/getAllFilesFromDir/main.nf | 9 +++------ tests/getRelativePath/main.nf | 9 +++------ tests/listToMD5/main.nf | 1 + tests/removeFromYamlMap/main.nf | 3 +-- tests/removeNextflowVersion/main.nf | 3 +-- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tests/getAllFilesFromDir/main.nf b/tests/getAllFilesFromDir/main.nf index 01c4f11..42cec9b 100644 --- a/tests/getAllFilesFromDir/main.nf +++ b/tests/getAllFilesFromDir/main.nf @@ -3,8 +3,7 @@ workflow { def trace_timestamp = new java.util.Date().format('yyyy-MM-dd_HH-mm-ss') // stable_content - channel - .of( + channel.of( """ I HAVE STABLE CONTENT """.stripIndent().trim() @@ -12,8 +11,7 @@ workflow { .collectFile(storeDir: "${params.outdir}/stable", name: 'stable_content.txt', sort: true, newLine: true) // stable_name - channel - .of( + channel.of( """ I DO NOT HAVE STABLE CONTENT ${trace_timestamp} @@ -22,8 +20,7 @@ workflow { .collectFile(storeDir: "${params.outdir}/stable", name: 'stable_name.txt', sort: true, newLine: true) // unstable_name - channel - .of( + channel.of( """ I DO NOT HAVE STABLE NAME """.stripIndent().trim() diff --git a/tests/getRelativePath/main.nf b/tests/getRelativePath/main.nf index 01c4f11..42cec9b 100644 --- a/tests/getRelativePath/main.nf +++ b/tests/getRelativePath/main.nf @@ -3,8 +3,7 @@ workflow { def trace_timestamp = new java.util.Date().format('yyyy-MM-dd_HH-mm-ss') // stable_content - channel - .of( + channel.of( """ I HAVE STABLE CONTENT """.stripIndent().trim() @@ -12,8 +11,7 @@ workflow { .collectFile(storeDir: "${params.outdir}/stable", name: 'stable_content.txt', sort: true, newLine: true) // stable_name - channel - .of( + channel.of( """ I DO NOT HAVE STABLE CONTENT ${trace_timestamp} @@ -22,8 +20,7 @@ workflow { .collectFile(storeDir: "${params.outdir}/stable", name: 'stable_name.txt', sort: true, newLine: true) // unstable_name - channel - .of( + channel.of( """ I DO NOT HAVE STABLE NAME """.stripIndent().trim() diff --git a/tests/listToMD5/main.nf b/tests/listToMD5/main.nf index 838f894..75f6585 100644 --- a/tests/listToMD5/main.nf +++ b/tests/listToMD5/main.nf @@ -1,4 +1,5 @@ workflow TEST { + main: def fill = "This needs to be filled in" def lines = [ diff --git a/tests/removeFromYamlMap/main.nf b/tests/removeFromYamlMap/main.nf index 7a8aafc..ea3fa51 100644 --- a/tests/removeFromYamlMap/main.nf +++ b/tests/removeFromYamlMap/main.nf @@ -1,6 +1,5 @@ workflow { - channel - .of( + channel.of( """ Workflow: Pipeline: 1.0.0 diff --git a/tests/removeNextflowVersion/main.nf b/tests/removeNextflowVersion/main.nf index a6b60dd..d0b1320 100644 --- a/tests/removeNextflowVersion/main.nf +++ b/tests/removeNextflowVersion/main.nf @@ -1,7 +1,6 @@ workflow { - channel - .of( + channel.of( """ Workflow: Pipeline: 1.0.0