diff --git a/integration/feature/full-run-logs/src/FullRunLogsTests.scala b/integration/feature/full-run-logs/src/FullRunLogsTests.scala index 62d70ca9848..f9c677a012c 100644 --- a/integration/feature/full-run-logs/src/FullRunLogsTests.scala +++ b/integration/feature/full-run-logs/src/FullRunLogsTests.scala @@ -31,24 +31,25 @@ object FullRunLogsTests extends UtestIntegrationTestSuite { res.isSuccess ==> true assert("\\[\\d+\\]

hello

".r.matches(res.out)) - val expectedErrorRegex = - s"""========================================== run --text hello ====================================== - |================================================================================================== - |[build.mill-/] compile - |[build.mill-] [info] compiling 1 Scala source to ${tester.workspacePath}/out/mill-build/compile.dest/classes ... - |[build.mill-] [info] done compiling - |[/] compile - |[] [info] compiling 1 Java source to ${tester.workspacePath}/out/compile.dest/classes ... - |[] [info] done compiling - |[/] run - |[/] ================================== run --text hello ==================================s - |==================================================================================================""" - .stripMargin - .replaceAll("(\r\n)|\r", "\n") - .replace('\\', '/') - .split("") - .map(java.util.regex.Pattern.quote) - .mkString("=? ?[\\d]+") + val expectedErrorRegex = java.util.regex.Pattern + .quote( + s""" run --text hello + | + |[build.mill-/] compile + |[build.mill-] [info] compiling 1 Scala source to ${tester.workspacePath}/out/mill-build/compile.dest/classes ... + |[build.mill-] [info] done compiling + |[/] compile + |[] [info] compiling 1 Java source to ${tester.workspacePath}/out/compile.dest/classes ... + |[] [info] done compiling + |[/] run + |[/] run --text hello s + |""" + .stripMargin + .replaceAll("(\r\n)|\r", "\n") + .replace('\\', '/') + ) + .replace("", "\\E\\d+\\Q") + .replace("", "\\E=+\\Q") assert(expectedErrorRegex.r.matches(res.err.replace('\\', '/').replaceAll("(\r\n)|\r", "\n"))) } diff --git a/main/util/src/mill/util/PromptLoggerUtil.scala b/main/util/src/mill/util/PromptLoggerUtil.scala index 6589532454d..7d09586cd31 100644 --- a/main/util/src/mill/util/PromptLoggerUtil.scala +++ b/main/util/src/mill/util/PromptLoggerUtil.scala @@ -150,7 +150,7 @@ private object PromptLoggerUtil { // For non-interactive jobs, the prompt won't be at the bottom of the terminal but // will instead be in the middle of a big log file with logs above and below, so we // need some kind of footer to tell the reader when the prompt ends and logs begin - val footer = Option.when(!interactive)("=" * maxWidth).toList + val footer = Option.when(!interactive)("=" * header.length).toList header :: body ::: footer } @@ -187,28 +187,24 @@ private object PromptLoggerUtil { val headerPrefixStr = if (!interactive || ending) headerPrefix else s" $headerPrefix" val headerSuffixStr = headerSuffix0 val titleText = s" $titleText0 " - // -12 just to ensure we always have some ==== divider on each side of the title + + val dividerMaxLength = 30 + val dividerMinLength = 15 val maxTitleLength = - maxWidth - math.max(headerPrefixStr.length, headerSuffixStr.length) * 2 - 12 + maxWidth - headerPrefixStr.length - headerSuffixStr.length - dividerMinLength * 2 val shortenedTitle = splitShorten(titleText, maxTitleLength) - // +2 to offset the title a bit to the right so it looks centered, as the `headerPrefixStr` - // is usually longer than `headerSuffixStr`. We use a fixed offset rather than dynamically - // offsetting by `headerPrefixStr.length` to prevent the title from shifting left and right - // as the `headerPrefixStr` changes, even at the expense of it not being perfectly centered. - val leftDivider = "=" * ((maxWidth / 2) - (titleText.length / 2) - headerPrefixStr.length + 2) - val rightDivider = - "=" * ( - maxWidth - headerPrefixStr.length - leftDivider.length - - shortenedTitle.length - headerSuffixStr.length - ) - val headerString = - headerPrefixStr + leftDivider + shortenedTitle + rightDivider + headerSuffixStr - assert( - headerString.length == maxWidth, - s"${pprint.apply(headerString)} is length ${headerString.length}, requires $maxWidth" + val rightDiv = "=" * math.min( + dividerMaxLength, + (maxWidth - headerPrefixStr.length - headerSuffixStr.length - shortenedTitle.length) / 2 ) - headerString + val leftDiv = "=" * math.min( + dividerMaxLength, + maxWidth - headerPrefixStr.length - headerSuffixStr.length - shortenedTitle.length - rightDiv.length + ) + + val headerString = headerPrefixStr + leftDiv + shortenedTitle + rightDiv + headerSuffixStr + splitShorten(headerString, maxWidth) } def splitShorten(s: String, maxLength: Int): String = { diff --git a/main/util/test/src/mill/util/PromptLoggerTests.scala b/main/util/test/src/mill/util/PromptLoggerTests.scala index 28b146342ed..9f79125a9f2 100644 --- a/main/util/test/src/mill/util/PromptLoggerTests.scala +++ b/main/util/test/src/mill/util/PromptLoggerTests.scala @@ -78,8 +78,8 @@ object PromptLoggerTests extends TestSuite { promptLogger.close() check(promptLogger, baos, width = 999 /*log file has no line wrapping*/ )( - "================================================ TITLE ===========================================", - "==================================================================================================", + "============================== TITLE ==============================", + "===================================================================", // Make sure that the first time a prefix is reported, // we print the verbose prefix along with the ticker string "[1/456] my-task", @@ -89,17 +89,17 @@ object PromptLoggerTests extends TestSuite { // the double space prefix (since it's non-interactive and we don't need space for a cursor), // the time elapsed, the reported title and ticker, the list of active tickers, followed by the // footer - "[123/456] ====================================== TITLE ======================================= 10s", + "[123/456] ============================== TITLE ============================== 10s", "[1] my-task 10s", - "==================================================================================================", + "=================================================================================", "[1] WORLD", // Calling `refreshPrompt()` after closing the ticker shows the prompt without // the ticker in the list, with an updated time elapsed - "[123/456] ====================================== TITLE ======================================= 20s", - "==================================================================================================", + "[123/456] ============================== TITLE ============================== 20s", + "=================================================================================", // Closing the prompt prints the prompt one last time with an updated time elapsed - "[123/456] ====================================== TITLE ======================================= 30s", - "==================================================================================================", + "[123/456] ============================== TITLE ============================== 30s", + "=================================================================================", "" ) } @@ -113,7 +113,7 @@ object PromptLoggerTests extends TestSuite { promptLogger.setPromptHeaderPrefix("123/456") promptLogger.refreshPrompt() check(promptLogger, baos)( - " [123/456] ========================== TITLE ==================================" + " [123/456] ============================== TITLE ==============================" ) promptLogger.setPromptLine(Seq("1"), "/456", "my-task") @@ -130,7 +130,7 @@ object PromptLoggerTests extends TestSuite { "", "[1/456] my-task", "[1] HELLO", - " [123/456] ========================== TITLE ============================== 10s", + " [123/456] ============================ TITLE ============================ 10s", "[1] my-task 10s" ) @@ -143,7 +143,7 @@ object PromptLoggerTests extends TestSuite { "[1/456] my-task", "[1] HELLO", "[1] WORLD", - " [123/456] ========================== TITLE ============================== 10s", + " [123/456] ============================ TITLE ============================ 10s", "[1] my-task 10s" ) @@ -175,7 +175,7 @@ object PromptLoggerTests extends TestSuite { "[3/456] my-task-short-lived", "[3] hello short lived", "[3] goodbye short lived", - " [123/456] ========================== TITLE ============================== 10s", + " [123/456] ============================ TITLE ============================ 10s", "[1] my-task 10s" ) @@ -196,7 +196,7 @@ object PromptLoggerTests extends TestSuite { "[3/456] my-task-short-lived", "[3] hello short lived", "[3] goodbye short lived", - " [123/456] ========================== TITLE ============================== 11s", + " [123/456] ============================ TITLE ============================ 11s", "[1] my-task 11s", "[2] my-task-new 1s" ) @@ -218,7 +218,7 @@ object PromptLoggerTests extends TestSuite { "[3/456] my-task-short-lived", "[3] hello short lived", "[3] goodbye short lived", - " [123/456] ========================== TITLE ============================== 11s", + " [123/456] ============================ TITLE ============================ 11s", "[1] my-task 11s", "[2] my-task-new 1s" ) @@ -239,7 +239,7 @@ object PromptLoggerTests extends TestSuite { "[3/456] my-task-short-lived", "[3] hello short lived", "[3] goodbye short lived", - " [123/456] ========================== TITLE ============================== 12s", + " [123/456] ============================ TITLE ============================ 12s", "[2] my-task-new 2s", "" ) @@ -259,7 +259,7 @@ object PromptLoggerTests extends TestSuite { "[3/456] my-task-short-lived", "[3] hello short lived", "[3] goodbye short lived", - " [123/456] ========================== TITLE ============================== 22s", + " [123/456] ============================ TITLE ============================ 22s", "[2] my-task-new 12s" ) now += 10000 @@ -275,7 +275,7 @@ object PromptLoggerTests extends TestSuite { "[3/456] my-task-short-lived", "[3] hello short lived", "[3] goodbye short lived", - "[123/456] ============================ TITLE ============================== 32s", + "[123/456] ============================= TITLE ============================= 32s", "" ) } @@ -298,20 +298,20 @@ object PromptLoggerTests extends TestSuite { now += 1000 promptLogger.refreshPrompt() check(promptLogger, baos)( - " [123/456] ========================== TITLE =============================== 1s", + " [123/456] ============================= TITLE ============================ 1s", "[1] my-task 1s detail" ) prefixLogger.ticker("detail-too-long-gets-truncated-abcdefghijklmnopqrstuvwxyz1234567890") promptLogger.refreshPrompt() check(promptLogger, baos)( - " [123/456] ========================== TITLE =============================== 1s", + " [123/456] ============================= TITLE ============================ 1s", "[1] my-task 1s detail-too-long-gets-truncated...fghijklmnopqrstuvwxyz1234567890" ) promptLogger.removePromptLine(Seq("1")) now += 10000 promptLogger.refreshPrompt() check(promptLogger, baos)( - " [123/456] ========================== TITLE ============================== 11s" + " [123/456] ============================ TITLE ============================ 11s" ) } } diff --git a/main/util/test/src/mill/util/PromptLoggerUtilTests.scala b/main/util/test/src/mill/util/PromptLoggerUtilTests.scala index 35c88e3b758..fc4ae5ccf95 100644 --- a/main/util/test/src/mill/util/PromptLoggerUtilTests.scala +++ b/main/util/test/src/mill/util/PromptLoggerUtilTests.scala @@ -78,47 +78,56 @@ object PromptLoggerUtilTests extends TestSuite { val rendered = renderHeader(prefix, title, suffix, maxWidth) // leave two spaces open on the left so there's somewhere to park the cursor assert(expected == rendered) - assert(rendered.length == maxWidth) + assert(rendered.length <= maxWidth) rendered } - test("simple") - check( - "PREFIX", - "TITLE", - " SUFFIX", - 60, - expected = " PREFIX ==================== TITLE ================= SUFFIX" + def checkSimple(maxWidth: Int, expected: String) = + check("PREFIX", "TITLE", " SUFFIX", maxWidth, expected) + test("extra") - checkSimple( + 200, + " PREFIX ============================== TITLE ============================== SUFFIX" ) - - test("short") - check( - "PREFIX", - "TITLE", - " SUFFIX", - 40, - expected = " PREFIX ========== TITLE ======= SUFFIX" + test("exact") - checkSimple( + 83, + " PREFIX ============================== TITLE ============================== SUFFIX" ) - - test("shorter") - check( - "PREFIX", - "TITLE", - " SUFFIX", - 25, - expected = " PREFIX ========= SUFFIX" + test("short") - checkSimple( + 82, // One `=` is removed from the right + " PREFIX ============================== TITLE ============================= SUFFIX" + ) + test("shorter") - checkSimple( + 81, // Next `=` is removed from the right + " PREFIX ============================= TITLE ============================= SUFFIX" ) - test("truncateTitle") - check( - "PREFIX", - "TITLE_ABCDEFGHIJKLMNOPQRSTUVWXYZ", - " SUFFIX", - 60, - expected = " PREFIX ====== TITLE_ABCDEFG...OPQRSTUVWXYZ ======== SUFFIX" + test("shorter2") - checkSimple( + 70, // Lots of `=`s removed from both left and right + " PREFIX ======================== TITLE ======================= SUFFIX" ) - test("asymmetricTruncateTitle") - check( - "PREFIX_LONG", - "TITLE_ABCDEFGHIJKLMNOPQRSTUVWXYZ", - " SUFFIX", - 60, - expected = " PREFIX_LONG = TITLE_AB...TUVWXYZ ================== SUFFIX" + test("beforeShortenTitle") - checkSimple( + 53, // Minimum number of `=` on each side (15) before shortening title + " PREFIX =============== TITLE =============== SUFFIX" + ) + test("shortenTitle") - checkSimple( + 52, // Begin shortening Title + " PREFIX =============== T... =============== SUFFIX" + ) + test("shortenTitle2") - checkSimple( + 51, + " PREFIX =============== ... =============== SUFFIX" + ) + test("shortenTitle3") - checkSimple( + 50, // Title is already minimized, have to shorten other parts + " PREFIX =============== ...=============== SUFFIX" + ) + test("titleEntirelyGone") - checkSimple( + 30, // Title section entirely removed + " PREFIX ============== SUFFIX" + ) + test("shortenTitle3") - checkSimple( + 10, // `=`s are all removed + " PR...FIX" ) } @@ -129,7 +138,7 @@ object PromptLoggerUtilTests extends TestSuite { titleText: String = "__.compile" )(statuses: (Int, Status)*) = { renderPrompt( - consoleWidth = 60, + consoleWidth = 74, consoleHeight = 20, now = now, startTimeMillis = now - 1337000, @@ -147,7 +156,7 @@ object PromptLoggerUtilTests extends TestSuite { 1 -> Status(Some(StatusEntry("world", now - 2000)), 0, None) ) val expected = List( - " 123/456 =============== __.compile ================ 1337s", + " 123/456 ======================= __.compile ====================== 1337s", "hello 1s", "world 2s" ) @@ -172,7 +181,7 @@ object PromptLoggerUtilTests extends TestSuite { ) val expected = List( - " 123/456 ======== __.compile.abcdefghijklmn ======== 1337s", + " 123/456 =============== __.compile.abcdefghijklmn =============== 1337s", "#1 hello1234567890abcefghijklmnopqrstuvwxyz1234567890123 1s", "#2 world 2s", "#3 i am cow 3s", @@ -181,7 +190,7 @@ object PromptLoggerUtilTests extends TestSuite { ) assert(rendered == expected) } - test("minAfterTruncation") { + test("minAfterTruncateHeader") { val rendered = renderPromptTest(interactive = true, titleText = "__.compile.abcdefghijklmno")( 0 -> Status( @@ -204,8 +213,40 @@ object PromptLoggerUtilTests extends TestSuite { ) val expected = List( - " 123/456 ======= __.compile....efghijklmno ========= 1337s", - "#1 hello1234567890abcefghijk...pqrstuvwxyz12345678901234 1s", + " 123/456 =============== __.compile....efghijklmno =============== 1337s", + "#1 hello1234567890abcefghijklmnopqrstuvwxyz12345678901234 1s", + "#2 world 2s", + "#3 i am cow 3s", + "#4 hear me moo 4s", + "... and 2 more threads" + ) + assert(rendered == expected) + } + test("minAfterTruncateRow") { + val rendered = + renderPromptTest(interactive = true, titleText = "__.compile.abcdefghijklmno")( + 0 -> Status( + Some(StatusEntry( + "#1 hello1234567890abcefghijklmnopqrstuvwxyz1234567890123456789012345678", + now - 1000 + )), + 0, + None + ), + 1 -> Status(Some(StatusEntry("#2 world", now - 2000)), 0, None), + 2 -> Status(Some(StatusEntry("#3 i am cow", now - 3000)), 0, None), + 3 -> Status(Some(StatusEntry("#4 hear me moo", now - 4000)), 0, None), + 4 -> Status(Some(StatusEntry("#5 i weigh twice as much as you", now - 5000)), 0, None), + 5 -> Status( + Some(StatusEntry("#6 and I look good on the barbecue", now - 6000)), + 0, + None + ) + ) + + val expected = List( + " 123/456 =============== __.compile....efghijklmno =============== 1337s", + "#1 hello1234567890abcefghijklmnopqr...wxyz1234567890123456789012345678 1s", "#2 world 2s", "#3 i am cow 3s", "#4 hear me moo 4s", @@ -236,13 +277,14 @@ object PromptLoggerUtilTests extends TestSuite { ) ) val expected = List( - " 123/456 __.compile....z1234567890 ================ 1337s", - "#1 hello1234567890abcefghijk...abcefghijklmnopqrstuvwxyz 1s", + " 123/456 =============== __.compile....z1234567890 =============== 1337s", + "#1 hello1234567890abcefghijklmnopqr...4567890abcefghijklmnopqrstuvwxyz 1s", "#2 world 2s", "#3 i am cow 3s", "#4 hear me moo 4s", "... and 3 more threads" ) + assert(rendered == expected) } test("detail") { @@ -269,11 +311,11 @@ object PromptLoggerUtilTests extends TestSuite { ) ) val expected = List( - " 123/456 =============== __.compile ================ 1337s", - "1 hello 1s", // no detail - "2 world 2s HELLO", // simple detail - "3 truncated-detail 3s HELLO WORLD abcde...tuvwxyz1234567890", // truncated detail - "4 long-status-eliminated-det...lmnopqrstuvwxyz1234567890 4s" + " 123/456 ======================= __.compile ====================== 1337s", + "1 hello 1s", + "2 world 2s HELLO", + "3 truncated-detail 3s HELLO WORLD abcdefghijklmnopqrstuvwxyz1234567890", + "4 long-status-eliminated-detail-abcdefghijklmnopqrstuvwxyz1234567890 4s.." ) assert(rendered == expected) } @@ -322,8 +364,8 @@ object PromptLoggerUtilTests extends TestSuite { ) val expected = List( - " 123/456 __.compile....z1234567890 ================ 1337s", - "#1 hello1234567890abcefghijk...abcefghijklmnopqrstuvwxyz 1s", + " 123/456 =============== __.compile....z1234567890 =============== 1337s", + "#1 hello1234567890abcefghijklmnopqr...4567890abcefghijklmnopqrstuvwxyz 1s", "#3 i am cow 3s", "", "", @@ -358,8 +400,8 @@ object PromptLoggerUtilTests extends TestSuite { ) val expected = List( - " 123/456 __.compile....z1234567890 ================ 1337s", - "#1 hello1234567890abcefghijk...abcefghijklmnopqrstuvwxyz 1s", + " 123/456 =============== __.compile....z1234567890 =============== 1337s", + "#1 hello1234567890abcefghijklmnopqr...4567890abcefghijklmnopqrstuvwxyz 1s", "#3 i am cow 3s" ) @@ -410,11 +452,11 @@ object PromptLoggerUtilTests extends TestSuite { // Make sure the non-interactive prompt does not show the blank lines, // and it contains a footer line to mark the end of the prompt in logs val expected = List( - "123/456 __.compile.ab...xyz1234567890 ============== 1337s", - "#1 hello1234567890abcefghijk...abcefghijklmnopqrstuvwxyz 1s", + "123/456 =============== __.compile.a...yz1234567890 =============== 1337s", + "#1 hello1234567890abcefghijklmnopqr...4567890abcefghijklmnopqrstuvwxyz 1s", "#2 world 2s", "#3 i am cow 3s", - "===========================================================" + "=========================================================================" ) assert(rendered == expected) }