From f336b30eaf04b89c0e03c916ba15a0ed5ce40544 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 2 Mar 2023 22:37:34 -0500 Subject: [PATCH 01/32] Enable windows CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64eb32890..75e1e9f9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: os: - ubuntu-latest - macos-latest - # - windows-latest + - windows-latest crystal: - latest - nightly From c61c87a754e8412f7bd4f00b3e2d2888cf672d0e Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 2 Mar 2023 22:43:08 -0500 Subject: [PATCH 02/32] Trim down to only windows for now Expicitly use `bash` shell --- .github/workflows/ci.yml | 60 +++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75e1e9f9b..7d1475a3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,39 +12,39 @@ concurrency: cancel-in-progress: true jobs: - check_spelling: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Check Spelling - uses: crate-ci/typos@v1.13.8 - check_format: - runs-on: ubuntu-latest - container: - image: crystallang/crystal:latest-alpine - steps: - - uses: actions/checkout@v3 - - name: Check Format - run: crystal tool format --check - coding_standards: - runs-on: ubuntu-latest - container: - image: crystallang/crystal:latest-alpine - steps: - - uses: actions/checkout@v3 - - name: Install Dependencies - run: shards install - env: - SHARDS_OVERRIDE: shard.dev.yml - - name: Ameba - run: ./bin/ameba + # check_spelling: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Check Spelling + # uses: crate-ci/typos@v1.13.8 + # check_format: + # runs-on: ubuntu-latest + # container: + # image: crystallang/crystal:latest-alpine + # steps: + # - uses: actions/checkout@v3 + # - name: Check Format + # run: crystal tool format --check + # coding_standards: + # runs-on: ubuntu-latest + # container: + # image: crystallang/crystal:latest-alpine + # steps: + # - uses: actions/checkout@v3 + # - name: Install Dependencies + # run: shards install + # env: + # SHARDS_OVERRIDE: shard.dev.yml + # - name: Ameba + # run: ./bin/ameba test: strategy: fail-fast: false matrix: os: - - ubuntu-latest - - macos-latest + # - ubuntu-latest + # - macos-latest - windows-latest crystal: - latest @@ -61,12 +61,10 @@ jobs: uses: crystal-lang/install-crystal@v1 with: crystal: ${{ matrix.crystal }} - - name: Install pkg-config via brew - if: ${{ matrix.os == 'macos-latest' }} - run: brew install pkg-config - name: Install Dependencies run: shards install --skip-postinstall --skip-executables env: SHARDS_OVERRIDE: shard.dev.yml - name: Specs run: ./scripts/test.sh + shell: bash From 2d19ba8a674b6ada04db93b510744365e10bfa07 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 2 Mar 2023 22:44:14 -0500 Subject: [PATCH 03/32] Remove nightlies too --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d1475a3d..e9b384d3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - windows-latest crystal: - latest - - nightly + # - nightly runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 From 79ab790fcdd17373fdc61a00477e3511e1c7d4a4 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 2 Mar 2023 23:39:26 -0500 Subject: [PATCH 04/32] Support reading terminal size on Windows --- src/components/console/spec/cursor_spec.cr | 3 +- src/components/console/spec/terminal_spec.cr | 2 + src/components/console/src/cursor.cr | 27 +++--- src/components/console/src/terminal.cr | 93 ++++++++++++++++---- 4 files changed, 93 insertions(+), 32 deletions(-) diff --git a/src/components/console/spec/cursor_spec.cr b/src/components/console/spec/cursor_spec.cr index 443e32885..cdfb46957 100644 --- a/src/components/console/spec/cursor_spec.cr +++ b/src/components/console/spec/cursor_spec.cr @@ -100,8 +100,7 @@ struct CursorTest < ASPEC::TestCase position.should eq({1, 1}) end - # TODO: Figure out a less brittle way of testing this. - def ptest_current_position_tty : Nil + def test_current_position_tty : Nil @cursor = ACON::Cursor.new @output @cursor.move_to_position 10, 10 diff --git a/src/components/console/spec/terminal_spec.cr b/src/components/console/spec/terminal_spec.cr index 8af6442ea..7b4580cd3 100644 --- a/src/components/console/spec/terminal_spec.cr +++ b/src/components/console/spec/terminal_spec.cr @@ -28,6 +28,7 @@ struct TerminalTest < ASPEC::TestCase terminal = ACON::Terminal.new terminal.width.should eq 120 terminal.height.should eq 60 + terminal.size.should eq({120, 60}) end def test_zero_values : Nil @@ -37,5 +38,6 @@ struct TerminalTest < ASPEC::TestCase terminal = ACON::Terminal.new terminal.width.should eq 0 terminal.height.should eq 0 + terminal.size.should eq({0, 0}) end end diff --git a/src/components/console/src/cursor.cr b/src/components/console/src/cursor.cr index d1773da5f..a51eb6f2f 100644 --- a/src/components/console/src/cursor.cr +++ b/src/components/console/src/cursor.cr @@ -24,9 +24,7 @@ struct Athena::Console::Cursor @output : ACON::Output::Interface @input : IO - def initialize(@output : ACON::Output::Interface, input : IO? = nil) - @input = input || STDIN - end + def initialize(@output : ACON::Output::Interface, @input : IO = STDIN); end # Moves the cursor up *lines* lines. def move_up(lines : Int32 = 1) : self @@ -128,19 +126,26 @@ struct Athena::Console::Cursor # Returns the current column, row position of the cursor. def current_position : {Int32, Int32} - return {1, 1} unless @input.tty? + {% if flag? :win32 %} + return {1, 1} unless STDIN.tty? + + LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STD_INPUT_HANDLE), out csbi) + {csbi.dwCursorPosition.x.to_i32, dwCursorPosition.y.to_i32} + {% else %} + return {1, 1} unless @input.tty? - stty_mode = `stty -g` - system "stty -icanon -echo" + stty_mode = `stty -g` + system "stty -icanon -echo" - @input.print "\033[6n" + @input.print "\033[6n" - bytes = @input.peek + bytes = @input.peek - system "stty #{stty_mode}" + system "stty #{stty_mode}" - String.new(bytes.not_nil!).match /\e\[(\d+);(\d+)R/ + String.new(bytes.not_nil!).match /\e\[(\d+);(\d+)R/ - {$2.to_i, $1.to_i} + {$2.to_i, $1.to_i} + {% end %} end end diff --git a/src/components/console/src/terminal.cr b/src/components/console/src/terminal.cr index c9441e0f2..06d53bffe 100644 --- a/src/components/console/src/terminal.cr +++ b/src/components/console/src/terminal.cr @@ -1,8 +1,9 @@ +require "./ext/terminal" + # :nodoc: struct Athena::Console::Terminal @@width : Int32? = nil @@height : Int32? = nil - @@stty : Bool = false def self.has_stty_available? : Bool if stty = @@stty @@ -36,30 +37,84 @@ struct Athena::Console::Terminal @@height || 50 end - protected def self.init_dimensions : Nil - # TODO: Support Windows - {% raise "Athena::Console component does not support Windows yet." if flag?(:win32) %} - - self.init_dimensions_via_stty + def size : {Int32, Int32} + return self.width, self.height end - private def self.init_dimensions_via_stty : Nil - return unless stty_info = self.stty_columns + private def self.check_size(size) : Bool + if size && (cols = size[0]) && (rows = size[1]) && cols != 0 && rows != 0 + @@width = cols + @@height = rows - if match = stty_info.match /rows.(\d+);.columns.(\d+);/i - @@height = match[1].to_i - @@width = match[2].to_i - elsif match = stty_info.match /;.(\d+).rows;.(\d+).columns/i - @@height = match[1].to_i - @@width = match[2].to_i + return true end + + false end - private def self.stty_columns : String? - stty_info = `stty -a | grep columns` + {% if flag?(:win32) %} + protected def self.init_dimensions : Nil + return if check_size(size_from_screen_buffer) + return if check_size(size_from_ansicon) + end - return nil unless $?.success? + # Detect terminal size Windows `GetConsoleScreenBufferInfo`. + private def self.size_from_screen_buffer + LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STD_OUTPUT_HANDLE), out csbi) + cols = csbi.srWindow.right - csbi.srWindow.left + 1 + rows = csbi.srWindow.bottom - csbi.srWindow.top + 1 - stty_info - end + {cols.to_i32, rows.to_i32} + end + + # Detect terminal size from Windows ANSICON + private def self.size_from_ansicon + return unless ENV["ANSICON"]?.to_s =~ /\((.*)x(.*)\)/ + + rows, cols = [$2, $1].map(&.to_i) + {cols, rows} + end + {% else %} + protected def self.init_dimensions : Nil + return if self.check_size(self.size_from_ioctl(0)) # STDIN + return if self.check_size(self.size_from_ioctl(1)) # STDOUT + return if self.check_size(self.size_from_ioctl(2)) # STDERR + return if self.check_size(self.size_from_tput) + return if self.check_size(self.size_from_stty) + end + + # Read terminal size from Unix ioctl + private def self.size_from_ioctl(fd) + winsize = uninitialized LibC::Winsize + ret = LibC.ioctl(fd, LibC::TIOCGWINSZ, pointerof(winsize)) + return if ret < 0 + + {winsize.ws_col.to_i32, winsize.ws_row.to_i32} + end + + # Detect terminal size from tput utility + private def self.size_from_tput + return unless STDOUT.tty? + + lines = `tput lines`.to_i? + cols = `tput cols`.to_i? + + {cols, lines} + rescue + nil + end + + # Detect terminal size from stty utility + private def self.size_from_stty + return unless STDOUT.tty? + + parts = `stty size`.split(/\s+/) + return unless parts.size > 1 + lines, cols = parts.map(&.to_i?) + + {cols, lines} + rescue + nil + end + {% end %} end From 93862c5cf644147de88ca730068d679da0d5779c Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 2 Mar 2023 23:40:22 -0500 Subject: [PATCH 05/32] Re-enable nightly CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9b384d3a..7d1475a3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - windows-latest crystal: - latest - # - nightly + - nightly runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 From 4200f4a0730f486d55fd5611a69eb2b25bba88a1 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 2 Mar 2023 23:48:59 -0500 Subject: [PATCH 06/32] Actually push the new file :pitchfork: --- src/components/console/src/ext/terminal.cr | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/components/console/src/ext/terminal.cr diff --git a/src/components/console/src/ext/terminal.cr b/src/components/console/src/ext/terminal.cr new file mode 100644 index 000000000..014ec9f3a --- /dev/null +++ b/src/components/console/src/ext/terminal.cr @@ -0,0 +1,51 @@ +{% if flag?(:win32) %} + lib LibC + struct COORD + x : Int16 + y : Int16 + end + + struct SMALL_RECT + left : Int16 + top : Int16 + right : Int16 + bottom : Int16 + end + + struct CONSOLE_SCREEN_BUFFER_INFO + dwSize : COORD + dwCursorPosition : COORD + wAttributes : UInt16 + srWindow : SMALL_RECT + dwMaximumWindowSize : COORD + end + + STD_OUTPUT_HANDLE = -11 + + fun GetConsoleScreenBufferInfo(hConsoleOutput : Void*, lpConsoleScreenBufferInfo : CONSOLE_SCREEN_BUFFER_INFO*) : Void + fun GetStdHandle(nStdHandle : UInt32) : Void* + end +{% else %} + lib LibC + struct Winsize + ws_row : UShort + ws_col : UShort + ws_xpixel : UShort + ws_ypixel : UShort + end + + # TIOCGWINSZ is a platform dependent magic number passed to ioctl that requests the current terminal window size. + # Values lifted from https://github.com/crystal-term/screen/blob/ea51ee8d1f6c286573c41a7e784d31c80af7b9bb/src/term-screen.cr#L86-L88. + {% begin %} + {% if flag?(:darwin) || flag?(:bsd) %} + TIOCGWINSZ = 0x40087468 + {% elsif flag?(:unix) %} + TIOCGWINSZ = 0x5413 + {% else %} # Solaris + TIOCGWINSZ = 0x5468 + {% end %} + {% end %} + + fun ioctl(fd : Int, request : ULong, ...) : Int + end +{% end %} From 80ed52af0bf77e8f1b17c7379a8d98f534dc682f Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 3 Mar 2023 00:12:41 -0500 Subject: [PATCH 07/32] Support hidden input on windows Use proper exception type if input could not be hidden --- src/components/console/src/helper/question.cr | 5 +---- src/components/console/src/terminal.cr | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/console/src/helper/question.cr b/src/components/console/src/helper/question.cr index c23f85352..67f35f104 100644 --- a/src/components/console/src/helper/question.cr +++ b/src/components/console/src/helper/question.cr @@ -149,9 +149,6 @@ class Athena::Console::Helper::Question < Athena::Console::Helper end private def hidden_response(output : ACON::Output::Interface, input_stream : IO) : String - # TODO: Support Windows - {% raise "Athena::Console component does not support Windows yet." if flag?(:win32) %} - response = if input_stream.tty? && input_stream.responds_to? :noecho input_stream.noecho &.gets 4096 elsif @@stty && ACON::Terminal.has_stty_available? @@ -160,7 +157,7 @@ class Athena::Console::Helper::Question < Athena::Console::Helper input_stream.gets(4096).tap { system "stty #{stty_mode}" } elsif input_stream.tty? - raise ACON::Exceptions::MissingInput.new "Unable to hide the response." + raise ACON::Exceptions::RuntimeError.new "Unable to hide the response." end raise ACON::Exceptions::MissingInput.new "Aborted." if response.nil? diff --git a/src/components/console/src/terminal.cr b/src/components/console/src/terminal.cr index 06d53bffe..6bb3dc7ec 100644 --- a/src/components/console/src/terminal.cr +++ b/src/components/console/src/terminal.cr @@ -4,6 +4,7 @@ require "./ext/terminal" struct Athena::Console::Terminal @@width : Int32? = nil @@height : Int32? = nil + @@stty : Bool = false def self.has_stty_available? : Bool if stty = @@stty From 0490c97e9bc33514cfa559d23452987587aef176 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 3 Mar 2023 00:22:42 -0500 Subject: [PATCH 08/32] Add `LibC::STD_INPUT_HANDLE` const --- src/components/console/src/ext/terminal.cr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/console/src/ext/terminal.cr b/src/components/console/src/ext/terminal.cr index 014ec9f3a..30b58e622 100644 --- a/src/components/console/src/ext/terminal.cr +++ b/src/components/console/src/ext/terminal.cr @@ -20,7 +20,9 @@ dwMaximumWindowSize : COORD end + STD_INPUT_HANDLE = -10 STD_OUTPUT_HANDLE = -11 + STD_ERROR_HANDLE = -12 fun GetConsoleScreenBufferInfo(hConsoleOutput : Void*, lpConsoleScreenBufferInfo : CONSOLE_SCREEN_BUFFER_INFO*) : Void fun GetStdHandle(nStdHandle : UInt32) : Void* From b6f83b95834250e02b4b027226d1d48f680b736b Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 3 Mar 2023 00:43:00 -0500 Subject: [PATCH 09/32] Fix `ACON::Cursor#current_position` --- src/components/console/src/cursor.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/console/src/cursor.cr b/src/components/console/src/cursor.cr index a51eb6f2f..7abb3bb4a 100644 --- a/src/components/console/src/cursor.cr +++ b/src/components/console/src/cursor.cr @@ -130,7 +130,7 @@ struct Athena::Console::Cursor return {1, 1} unless STDIN.tty? LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STD_INPUT_HANDLE), out csbi) - {csbi.dwCursorPosition.x.to_i32, dwCursorPosition.y.to_i32} + {csbi.dwCursorPosition.x.to_i32, csbi.dwCursorPosition.y.to_i32} {% else %} return {1, 1} unless @input.tty? From fa2f1ce50cebaad38ef96628c5db8400d1892764 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Wed, 8 Mar 2023 22:33:48 -0500 Subject: [PATCH 10/32] Leverage a `System::EOL` newline character Support normalizing command/application tester output --- .../console/spec/application_spec.cr | 42 +++++++++---------- .../abstract_descriptor_test_case.cr | 6 ++- src/components/console/src/athena-console.cr | 11 +++++ src/components/console/src/output/section.cr | 2 +- .../console/src/output/sized_buffer.cr | 2 +- src/components/console/src/spec.cr | 22 +++++++--- src/components/console/src/style/athena.cr | 8 ++-- src/components/console/src/style/output.cr | 2 +- 8 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/components/console/spec/application_spec.cr b/src/components/console/spec/application_spec.cr index bca80b1d1..ccc070bee 100644 --- a/src/components/console/spec/application_spec.cr +++ b/src/components/console/spec/application_spec.cr @@ -339,7 +339,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "foos:bar1", decorated: false - self.assert_file_equals_string "text/application_alternative_namespace.txt", tester.display + self.assert_file_equals_string "text/application_alternative_namespace.txt", tester.display true end def test_run_alternate_command_name : Nil @@ -524,10 +524,10 @@ struct ApplicationTest < ASPEC::TestCase app.catch_exceptions = true tester.run command: "foo", decorated: false - self.assert_file_equals_string "text/application_renderexception1.txt", tester.display + self.assert_file_equals_string "text/application_renderexception1.txt", tester.display true tester.run command: "foo", decorated: false, capture_stderr_separately: true - self.assert_file_equals_string "text/application_renderexception1.txt", tester.error_output + self.assert_file_equals_string "text/application_renderexception1.txt", tester.error_output true tester.display.should be_empty app.catch_exceptions = false @@ -544,30 +544,30 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "foo", decorated: false, capture_stderr_separately: true - self.assert_file_equals_string "text/application_renderexception1.txt", tester.error_output + self.assert_file_equals_string "text/application_renderexception1.txt", tester.error_output true tester.run command: "foo", decorated: false, capture_stderr_separately: true, verbosity: :verbose tester.error_output.should contain "Exception trace" tester.run command: "list", "--foo": true, decorated: false, capture_stderr_separately: true - self.assert_file_equals_string "text/application_renderexception2.txt", tester.error_output + self.assert_file_equals_string "text/application_renderexception2.txt", tester.error_output true app.add Foo3Command.new tester = ACON::Spec::ApplicationTester.new app tester.run command: "foo3:bar", decorated: false, capture_stderr_separately: true - self.assert_file_equals_string "text/application_renderexception3.txt", tester.error_output + self.assert_file_equals_string "text/application_renderexception3.txt", tester.error_output true tester.run({"command" => "foo3:bar"}, decorated: false, verbosity: :verbose) - tester.display.should match /\[Exception\]\s*First exception/ - tester.display.should match /\[Exception\]\s*Second exception/ - tester.display.should match /\[Exception\]\s*Third exception/ + tester.display(true).should match /\[Exception\]\s*First exception/ + tester.display(true).should match /\[Exception\]\s*Second exception/ + tester.display(true).should match /\[Exception\]\s*Third exception/ tester.run command: "foo3:bar", decorated: true - self.assert_file_equals_string "text/application_renderexception3_decorated.txt", tester.display + self.assert_file_equals_string "text/application_renderexception3_decorated.txt", tester.display true tester.run command: "foo3:bar", decorated: true, capture_stderr_separately: true - self.assert_file_equals_string "text/application_renderexception3_decorated.txt", tester.error_output + self.assert_file_equals_string "text/application_renderexception3_decorated.txt", tester.error_output true app = ACON::Application.new "foo" app.auto_exit = false @@ -575,7 +575,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "foo", decorated: false, capture_stderr_separately: true - self.assert_file_equals_string "text/application_renderexception4.txt", tester.error_output + self.assert_file_equals_string "text/application_renderexception4.txt", tester.error_output true ENV["COLUMNS"] = "120" end @@ -604,7 +604,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "foo", decorated: false - self.assert_file_equals_string "text/application_renderexception_escapeslines.txt", tester.display + self.assert_file_equals_string "text/application_renderexception_escapeslines.txt", tester.display true ENV["COLUMNS"] = "120" end @@ -619,7 +619,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "foo", decorated: false - self.assert_file_equals_string "text/application_renderexception_linebreaks.txt", tester.display + self.assert_file_equals_string "text/application_renderexception_linebreaks.txt", tester.display true end def test_render_exception_escapes_lines_of_synopsis : Nil @@ -659,7 +659,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run decorated: false - self.assert_file_equals_string "text/application_run1.txt", tester.display + self.assert_file_equals_string "text/application_run1.txt", tester.display true end def test_run_help_command : Nil @@ -686,10 +686,10 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "list", "--help": true, decorated: false - self.assert_file_equals_string "text/application_run3.txt", tester.display + self.assert_file_equals_string "text/application_run3.txt", tester.display true tester.run command: "list", "-h": true, decorated: false - self.assert_file_equals_string "text/application_run3.txt", tester.display + self.assert_file_equals_string "text/application_run3.txt", tester.display true end def test_run_ansi : Nil @@ -712,10 +712,10 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run "--version": true, decorated: false - self.assert_file_equals_string "text/application_run4.txt", tester.display + self.assert_file_equals_string "text/application_run4.txt", tester.display true tester.run "-V": true, decorated: false - self.assert_file_equals_string "text/application_run4.txt", tester.display + self.assert_file_equals_string "text/application_run4.txt", tester.display true end def test_run_quest : Nil @@ -775,10 +775,10 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "help", "--help": true, decorated: false - self.assert_file_equals_string "text/application_run5.txt", tester.display + self.assert_file_equals_string "text/application_run5.txt", tester.display true tester.run command: "help", "-h": true, decorated: false - self.assert_file_equals_string "text/application_run5.txt", tester.display + self.assert_file_equals_string "text/application_run5.txt", tester.display true end def test_run_no_interaction : Nil diff --git a/src/components/console/spec/descriptor/abstract_descriptor_test_case.cr b/src/components/console/spec/descriptor/abstract_descriptor_test_case.cr index 4c99329c7..2a22b945d 100644 --- a/src/components/console/spec/descriptor/abstract_descriptor_test_case.cr +++ b/src/components/console/spec/descriptor/abstract_descriptor_test_case.cr @@ -62,6 +62,10 @@ abstract struct AbstractDescriptorTestCase < ASPEC::TestCase context = context.clone context.raw_output = true self.descriptor.describe output, object, context - output.to_s.strip.should eq expected.strip + self.normalize_output(output.to_s).should eq self.normalize_output(expected) + end + + private def normalize_output(output : String) : String + output.gsub(ACON::System::EOL, "\n").strip end end diff --git a/src/components/console/src/athena-console.cr b/src/components/console/src/athena-console.cr index bf3007faa..8fd9417ee 100644 --- a/src/components/console/src/athena-console.cr +++ b/src/components/console/src/athena-console.cr @@ -107,4 +107,15 @@ module Athena::Console # Contains types related to lazily loading commands. module Loader; end + + # :nodoc: + # + # TODO: Remove this in favor of `::System::EOL` when/if https://github.com/crystal-lang/crystal/pull/11303 is released. + module System + EOL = {% if flag? :windows %} + "\r\n" + {% else %} + "\n" + {% end %} + end end diff --git a/src/components/console/src/output/section.cr b/src/components/console/src/output/section.cr index f0e957b8e..7c1f102bf 100644 --- a/src/components/console/src/output/section.cr +++ b/src/components/console/src/output/section.cr @@ -92,7 +92,7 @@ class Athena::Console::Output::Section < Athena::Console::Output::IO input.each_line do |line| lines = (self.get_display_width(line) // @terminal.width).ceil @lines += lines.zero? ? 1 : lines - @content.push line, "\n" + @content.push line, System::EOL end end diff --git a/src/components/console/src/output/sized_buffer.cr b/src/components/console/src/output/sized_buffer.cr index 619b44cbe..6fba30933 100644 --- a/src/components/console/src/output/sized_buffer.cr +++ b/src/components/console/src/output/sized_buffer.cr @@ -23,7 +23,7 @@ class Athena::Console::Output::SizedBuffer < Athena::Console::Output protected def do_write(message : String, new_line : Bool) : Nil @buffer += message - @buffer += "\n" if new_line + @buffer += System::EOL if new_line @buffer = @buffer.chars.last(@max_length).join end diff --git a/src/components/console/src/spec.cr b/src/components/console/src/spec.cr index a911010dc..1698f3ddd 100644 --- a/src/components/console/src/spec.cr +++ b/src/components/console/src/spec.cr @@ -13,17 +13,29 @@ module Athena::Console::Spec # Returns the output resulting from running the command. # Raises if called before executing the command. - def display : String + def display(normalize : Bool = false) : String raise ACON::Exceptions::Logic.new "Output not initialized. Did you execute the command before requesting the display?" unless (output = @output) - output.to_s + output = output.to_s + + if normalize + output = output.gsub System::EOL, "\n" + end + + output end # Returns the error output resulting from running the command. # Raises if `capture_stderr_separately` was not set to `true`. - def error_output : String + def error_output(normalize : Bool = false) : String raise ACON::Exceptions::Logic.new "The error output is not available when the test is ran without 'capture_stderr_separately' set." unless @capture_stderr_separately - self.output.as(ACON::Output::ConsoleOutput).error_output.to_s + output = self.output.as(ACON::Output::ConsoleOutput).error_output.to_s + + if normalize + output = output.gsub System::EOL, "\n" + end + + output end # Helper method to setting the `#inputs=` property. @@ -67,7 +79,7 @@ module Athena::Console::Spec input_stream = IO::Memory.new inputs.each do |input| - input_stream << "#{input}\n" + input_stream << "#{input}#{System::EOL}" end input_stream.rewind diff --git a/src/components/console/src/style/athena.cr b/src/components/console/src/style/athena.cr index da714afd2..d5b68594c 100644 --- a/src/components/console/src/style/athena.cr +++ b/src/components/console/src/style/athena.cr @@ -49,7 +49,7 @@ class Athena::Console::Style::Athena < Athena::Console::Style::Output if @input.interactive? self.new_line - @buffered_output.print "\n" + @buffered_output.print System::EOL end answer @@ -247,7 +247,7 @@ class Athena::Console::Style::Athena < Athena::Console::Style::Output # :inherit: def new_line(count : Int32 = 1) : Nil super - @buffered_output.print "\n" * count + @buffered_output.print System::EOL * count end # :inherit: @@ -359,7 +359,7 @@ class Athena::Console::Style::Athena < Athena::Console::Style::Output # end private def auto_prepend_block : Nil - chars = @buffered_output.fetch + chars = @buffered_output.fetch.gsub System::EOL, "\n" if chars.empty? return self.new_line @@ -390,7 +390,7 @@ class Athena::Console::Style::Athena < Athena::Console::Style::Output decoration_length = ACON::Helper.width(message) - ACON::Helper.width(ACON::Helper.remove_decoration(self.formatter, message)) message_line_length = Math.min(@line_length - prefix_length - indent_length + decoration_length, @line_length) - message.gsub(/(.{1,#{message_line_length}})( +|$\n?)|(.{1,#{message_line_length}})/, "\\0\n").split "\n", remove_empty: true do |match| + message.gsub(/(.{1,#{message_line_length}})( +|$#{System::EOL}?)|(.{1,#{message_line_length}})/, "\\0#{System::EOL}").split System::EOL, remove_empty: true do |match| lines << match.strip end diff --git a/src/components/console/src/style/output.cr b/src/components/console/src/style/output.cr index cb0d4a4ce..208553aac 100644 --- a/src/components/console/src/style/output.cr +++ b/src/components/console/src/style/output.cr @@ -31,7 +31,7 @@ abstract class Athena::Console::Style::Output # :inherit: def new_line(count : Int32 = 1) : Nil - @output.print "\n" * count + @output.print System::EOL * count end # See `ACON::Output::Interface#puts`. From 726b0e051d57608894b2e11dbf1da71c25665093 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Wed, 8 Mar 2023 22:51:12 -0500 Subject: [PATCH 11/32] Normalize more newlines in specs --- src/components/console/spec/application_spec.cr | 2 +- src/components/console/spec/helper/table_spec.cr | 2 +- .../console/spec/style/athena_style_spec.cr | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/console/spec/application_spec.cr b/src/components/console/spec/application_spec.cr index ccc070bee..9b5f4b36f 100644 --- a/src/components/console/spec/application_spec.cr +++ b/src/components/console/spec/application_spec.cr @@ -19,7 +19,7 @@ struct ApplicationTest < ASPEC::TestCase protected def assert_file_equals_string(filepath : String, string : String, *, file : String = __FILE__, line : Int32 = __LINE__) : Nil normalized_path = File.join __DIR__, "fixtures", filepath - string.should match(Regex.new(File.read(normalized_path))), file: file, line: line + string.should match(Regex.new(File.read(normalized_path).gsub System::EOL, "\n")), file: file, line: line end protected def ensure_static_command_help(application : ACON::Application) : Nil diff --git a/src/components/console/spec/helper/table_spec.cr b/src/components/console/spec/helper/table_spec.cr index 2e07b2c46..a2602acd9 100644 --- a/src/components/console/spec/helper/table_spec.cr +++ b/src/components/console/spec/helper/table_spec.cr @@ -4,7 +4,7 @@ struct TableSpec < ASPEC::TestCase @output : IO protected def get_table_contents(table_name : String) : String - File.read File.join __DIR__, "..", "fixtures", "helper", "table", "#{table_name}.txt" + File.read(File.join __DIR__, "..", "fixtures", "helper", "table", "#{table_name}.txt").gsub(ACON::System::EOL, "\n") end def initialize diff --git a/src/components/console/spec/style/athena_style_spec.cr b/src/components/console/spec/style/athena_style_spec.cr index 07729f35d..8a14c4187 100644 --- a/src/components/console/spec/style/athena_style_spec.cr +++ b/src/components/console/spec/style/athena_style_spec.cr @@ -18,7 +18,7 @@ struct AthenaStyleTest < ASPEC::TestCase private def assert_file_equals_string(filepath : String, string : String, *, file : String = __FILE__, line : Int32 = __LINE__) : Nil normalized_path = File.join __DIR__, "..", "fixtures", filepath - string.should match(Regex.new(File.read(normalized_path))), file: file, line: line + string.should match(Regex.new(File.read(normalized_path).gsub System::EOL, "\n")), file: file, line: line end def test_error_style : Nil @@ -48,7 +48,7 @@ struct AthenaStyleTest < ASPEC::TestCase tester = ACON::Spec::CommandTester.new command tester.execute interactive: false, decorated: false - self.assert_file_equals_string file_path, tester.display + self.assert_file_equals_string file_path, tester.display true end def output_provider : Hash @@ -358,7 +358,7 @@ struct AthenaStyleTest < ASPEC::TestCase tester = ACON::Spec::CommandTester.new command tester.execute interactive: false, decorated: false - self.assert_file_equals_string "style/table.txt", tester.display + self.assert_file_equals_string "style/table.txt", tester.display true end def test_table : Nil @@ -374,7 +374,7 @@ struct AthenaStyleTest < ASPEC::TestCase tester = ACON::Spec::CommandTester.new command tester.execute interactive: false, decorated: false - self.assert_file_equals_string "style/table.txt", tester.display + self.assert_file_equals_string "style/table.txt", tester.display true end def test_horizontal_table : Nil @@ -390,7 +390,7 @@ struct AthenaStyleTest < ASPEC::TestCase tester = ACON::Spec::CommandTester.new command tester.execute interactive: false, decorated: false - self.assert_file_equals_string "style/table_horizontal.txt", tester.display + self.assert_file_equals_string "style/table_horizontal.txt", tester.display true end def test_vertical_table : Nil @@ -406,6 +406,6 @@ struct AthenaStyleTest < ASPEC::TestCase tester = ACON::Spec::CommandTester.new command tester.execute interactive: false, decorated: false - self.assert_file_equals_string "style/table_vertical.txt", tester.display + self.assert_file_equals_string "style/table_vertical.txt", tester.display true end end From 761baddb0c234f60518a3a8a2e268eb1b9ea39d9 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 9 Mar 2023 06:00:40 -0500 Subject: [PATCH 12/32] Get framework component nearly working Signed-off-by: George Dietrich --- .../controller/value_resolvers/time_spec.cr | 4 +- .../framework/spec/controller_spec.cr | 4 +- .../framework/spec/error_renderer_spec.cr | 53 ++++++++++++++----- src/components/framework/src/athena.cr | 16 ++++-- .../framework/src/error_renderer.cr | 19 +++++-- 5 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/components/framework/spec/controller/value_resolvers/time_spec.cr b/src/components/framework/spec/controller/value_resolvers/time_spec.cr index ebec99696..f912a2118 100644 --- a/src/components/framework/spec/controller/value_resolvers/time_spec.cr +++ b/src/components/framework/spec/controller/value_resolvers/time_spec.cr @@ -91,7 +91,7 @@ describe ATHR::Time do "foo", annotation_configurations: ACF::AnnotationConfigurations.new({ ATHR::Time::Format => [ - ATHR::Time::FormatConfiguration.new(format: "%Y--%m//%d %T", location: Time::Location.load("Europe/Berlin")), + ATHR::Time::FormatConfiguration.new(format: "%Y--%m//%d %T", location: Time::Location.fixed(9001)), ] of ACF::AnnotationConfigurations::ConfigurationBase, } of ACF::AnnotationConfigurations::Classes => Array(ACF::AnnotationConfigurations::ConfigurationBase)) ) @@ -99,7 +99,7 @@ describe ATHR::Time do request = new_request request.attributes.set "foo", "2020--04//07 12:34:56" - ATHR::Time.new.resolve(request, parameter).should eq Time.local 2020, 4, 7, 12, 34, 56, location: Time::Location.load("Europe/Berlin") + ATHR::Time.new.resolve(request, parameter).should eq Time.local 2020, 4, 7, 12, 34, 56, location: Time::Location.fixed(9001) end it "raises an ATH::Exceptions::BadRequest if a time could not be parsed from the string" do diff --git a/src/components/framework/spec/controller_spec.cr b/src/components/framework/spec/controller_spec.cr index a143250e7..4b725cda6 100644 --- a/src/components/framework/spec/controller_spec.cr +++ b/src/components/framework/spec/controller_spec.cr @@ -9,7 +9,7 @@ describe ATH::Controller do response.status.should eq HTTP::Status::OK response.headers["content-type"].should eq "text/html" - response.content.should eq "Greetings, TEST!\n" + response.content.should eq "Greetings, TEST!#{ATH::System::EOL}" end it "creates a proper response for the template with a layout" do @@ -19,7 +19,7 @@ describe ATH::Controller do response.status.should eq HTTP::Status::OK response.headers["content-type"].should eq "text/html" - response.content.should eq "

Content:

Greetings, TEST!\n" + response.content.should eq "

Content:

Greetings, TEST!#{ATH::System::EOL}" end end diff --git a/src/components/framework/spec/error_renderer_spec.cr b/src/components/framework/spec/error_renderer_spec.cr index 8535ed0c7..d66e72ddf 100644 --- a/src/components/framework/spec/error_renderer_spec.cr +++ b/src/components/framework/spec/error_renderer_spec.cr @@ -1,5 +1,15 @@ require "./spec_helper" +private class MockException < ::Exception + def initialize(@first_line : String) + super "ERR" + end + + def backtrace? : Array(String) + [@first_line] + end +end + describe ATH::ErrorRenderer do it ATH::Exceptions::HTTPException do exception = ATH::Exceptions::TooManyRequests.new "cool your jets", 42 @@ -30,24 +40,39 @@ describe ATH::ErrorRenderer do response.content.should eq %({"code":500,"message":"Internal Server Error"}) end - it "when in debug mode" do - exception = uninitialized Exception + describe "debug mode" do + it "line + column" do + path = Path["src", "components", "framework", "spec", "error_renderer_spec.cr"] + exception = MockException.new "#{path}:10:20" + + renderer = ATH::ErrorRenderer.new true + + response = renderer.render exception - begin - raise Exception.new "ERR" - rescue exception + response.headers["content-type"].should eq "application/json; charset=UTF-8" + response.headers["x-debug-exception-message"].should eq "ERR" + response.headers["x-debug-exception-class"].should eq "MockException" + response.headers["x-debug-exception-file"].should match /#{URI.encode_path path.to_s}:\d+:\d+$/ + response.headers["x-debug-exception-code"].should eq "500" + response.status.should eq HTTP::Status::INTERNAL_SERVER_ERROR + response.content.should eq %({"code":500,"message":"Internal Server Error"}) end - renderer = ATH::ErrorRenderer.new true + it "only line" do + path = Path["src", "components", "framework", "spec", "error_renderer_spec.cr"] + exception = MockException.new "#{path}:10" - response = renderer.render exception + renderer = ATH::ErrorRenderer.new true - response.headers["content-type"].should eq "application/json; charset=UTF-8" - response.headers["x-debug-exception-message"].should eq "ERR" - response.headers["x-debug-exception-class"].should eq "Exception" - response.headers["x-debug-exception-file"].should match /src\/components\/framework\/spec\/error_renderer_spec\.cr:\d+:\d+/ - response.headers["x-debug-exception-code"].should eq "500" - response.status.should eq HTTP::Status::INTERNAL_SERVER_ERROR - response.content.should eq %({"code":500,"message":"Internal Server Error"}) + response = renderer.render exception + + response.headers["content-type"].should eq "application/json; charset=UTF-8" + response.headers["x-debug-exception-message"].should eq "ERR" + response.headers["x-debug-exception-class"].should eq "MockException" + response.headers["x-debug-exception-file"].should match /#{URI.encode_path path.to_s}:\d+$/ + response.headers["x-debug-exception-code"].should eq "500" + response.status.should eq HTTP::Status::INTERNAL_SERVER_ERROR + response.content.should eq %({"code":500,"message":"Internal Server Error"}) + end end end diff --git a/src/components/framework/src/athena.cr b/src/components/framework/src/athena.cr index 8f26a6cc8..1af1b6733 100755 --- a/src/components/framework/src/athena.cr +++ b/src/components/framework/src/athena.cr @@ -127,6 +127,17 @@ module Athena::Framework # See each command class for more information. module Commands; end + # :nodoc: + # + # TODO: Remove this in favor of `::System::EOL` when/if https://github.com/crystal-lang/crystal/pull/11303 is released. + module System + EOL = {% if flag? :windows %} + "\r\n" + {% else %} + "\n" + {% end %} + end + # Runs an `HTTP::Server` listening on the given *port* and *host*. # # ``` @@ -210,9 +221,8 @@ module Athena::Framework end {% end %} - # Handle exiting correctly on stop/kill signals - Signal::INT.trap { self.stop } - Signal::TERM.trap { self.stop } + # Handle exiting correctly on interrupt signals + Process.on_interrupt { self.stop } Log.info { %(Server has started and is listening at #{@ssl_context ? "https" : "http"}://#{@server.addresses.first}) } diff --git a/src/components/framework/src/error_renderer.cr b/src/components/framework/src/error_renderer.cr index 761c09b2d..e9c53e338 100644 --- a/src/components/framework/src/error_renderer.cr +++ b/src/components/framework/src/error_renderer.cr @@ -18,11 +18,20 @@ struct Athena::Framework::ErrorRenderer headers["content-type"] = "application/json; charset=UTF-8" # TODO: Use a better API to get the file/line/column info. - if @debug && (match = exception.backtrace?.try(&.first).to_s.match(/(.*):(\d+):(\d+)/)) - headers["x-debug-exception-message"] = URI.encode_path exception.message.to_s - headers["x-debug-exception-class"] = exception.class.to_s - headers["x-debug-exception-code"] = status.value.to_s - headers["x-debug-exception-file"] = "#{URI.encode_path(match[1])}:#{match[2]}:#{match[3]}" + if @debug && (backtrace = exception.backtrace?.try(&.first).to_s.presence) + if (match = backtrace.match(/(.*):(\d+):(\d+)/)) || (match = backtrace.match(/(.*):(\d+)/)) + headers["x-debug-exception-message"] = URI.encode_path exception.message.to_s + headers["x-debug-exception-class"] = exception.class.to_s + headers["x-debug-exception-code"] = status.value.to_s + + file = "#{URI.encode_path(match[1])}:#{match[2]}" + + if m3 = match[3]? + file = "#{file}:#{match[3]}" + end + + headers["x-debug-exception-file"] = file + end end ATH::Response.new exception.to_json, status, headers From 223038ea1494a3ea9d3f602d4a6d5166717495df Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 10 Mar 2023 00:50:14 -0500 Subject: [PATCH 13/32] Get `framework` and `console` specs passing on windows --- .../console/spec/application_spec.cr | 6 +- .../console/spec/commands/list_spec.cr | 6 +- .../abstract_question_helper_test_case.cr | 10 +++- .../spec/helper/athena_question_spec.cr | 2 +- .../console/spec/helper/formatter_spec.cr | 10 +++- .../console/spec/helper/table_spec.cr | 56 ++++++++++--------- .../console/spec/input/argv_spec.cr | 7 ++- .../console/spec/input/hash_spec.cr | 7 ++- .../console/spec/input/string_line_spec.cr | 16 ++++-- .../console/spec/style/athena_style_spec.cr | 2 +- src/components/console/src/cursor.cr | 2 +- src/components/console/src/output/section.cr | 2 +- src/components/console/src/style/athena.cr | 2 +- .../commands/debug_event_dispatcher_spec.cr | 2 +- .../spec/commands/debug_router_match_spec.cr | 2 +- 15 files changed, 84 insertions(+), 48 deletions(-) diff --git a/src/components/console/spec/application_spec.cr b/src/components/console/spec/application_spec.cr index 9b5f4b36f..4a0a4914b 100644 --- a/src/components/console/spec/application_spec.cr +++ b/src/components/console/spec/application_spec.cr @@ -19,7 +19,7 @@ struct ApplicationTest < ASPEC::TestCase protected def assert_file_equals_string(filepath : String, string : String, *, file : String = __FILE__, line : Int32 = __LINE__) : Nil normalized_path = File.join __DIR__, "fixtures", filepath - string.should match(Regex.new(File.read(normalized_path).gsub System::EOL, "\n")), file: file, line: line + string.should match(Regex.new(File.read(normalized_path).gsub ACON::System::EOL, "\n")), file: file, line: line end protected def ensure_static_command_help(application : ACON::Application) : Nil @@ -671,10 +671,10 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run "--help": true, decorated: false - self.assert_file_equals_string "text/application_run2.txt", tester.display + self.assert_file_equals_string "text/application_run2.txt", tester.display true tester.run "-h": true, decorated: false - self.assert_file_equals_string "text/application_run2.txt", tester.display + self.assert_file_equals_string "text/application_run2.txt", tester.display true end def test_run_help_list_command : Nil diff --git a/src/components/console/spec/commands/list_spec.cr b/src/components/console/spec/commands/list_spec.cr index 4c6e587a5..243fd637d 100644 --- a/src/components/console/spec/commands/list_spec.cr +++ b/src/components/console/spec/commands/list_spec.cr @@ -1,5 +1,9 @@ require "../spec_helper" +private def normalize(input : String) : String + input.gsub ACON::System::EOL, "\n" +end + describe ACON::Commands::List do describe "#execute" do it "executes" do @@ -35,7 +39,7 @@ describe ACON::Commands::List do tester = ACON::Spec::CommandTester.new app.get("list") tester.execute command: "list", decorated: false - tester.display.should eq <<-OUTPUT + tester.display(true).should eq normalize <<-OUTPUT foo 0.1.0 Usage: diff --git a/src/components/console/spec/helper/abstract_question_helper_test_case.cr b/src/components/console/spec/helper/abstract_question_helper_test_case.cr index d23d0a3a2..e055de73d 100644 --- a/src/components/console/spec/helper/abstract_question_helper_test_case.cr +++ b/src/components/console/spec/helper/abstract_question_helper_test_case.cr @@ -16,10 +16,16 @@ abstract struct AbstractQuestionHelperTest < ASPEC::TestCase yield input end - protected def assert_output_contains(string : String) : Nil + protected def assert_output_contains(string : String, normalize : Bool = false) : Nil stream = @output.io stream.rewind - stream.to_s.should contain string + output = stream.to_s + + if normalize + output = output.gsub ACON::System::EOL, "\n|" + end + + output.should contain string.gsub ACON::System::EOL, "\n" end end diff --git a/src/components/console/spec/helper/athena_question_spec.cr b/src/components/console/spec/helper/athena_question_spec.cr index 98addcda3..283532bd0 100644 --- a/src/components/console/spec/helper/athena_question_spec.cr +++ b/src/components/console/spec/helper/athena_question_spec.cr @@ -135,7 +135,7 @@ struct AthenaQuestionTest < AbstractQuestionHelperTest @helper.ask input, @output, question end - self.assert_output_contains <<-OUT + self.assert_output_contains <<-OUT, true qqq: [foo ] foo [żółw ] bar diff --git a/src/components/console/spec/helper/formatter_spec.cr b/src/components/console/spec/helper/formatter_spec.cr index 52298c72c..bbae72063 100644 --- a/src/components/console/spec/helper/formatter_spec.cr +++ b/src/components/console/spec/helper/formatter_spec.cr @@ -1,5 +1,9 @@ require "../spec_helper" +private def normalize(input : String) : String + input.gsub ACON::System::EOL, "\n" +end + describe ACON::Helper::Formatter do it "#format_section" do ACON::Helper::Formatter.new.format_section("cli", "some text to display").should eq "[cli] some text to display" @@ -11,7 +15,7 @@ describe ACON::Helper::Formatter do formatter.format_block("Some text to display", "error").should eq " Some text to display " formatter.format_block({"Some text to display", "foo bar"}, "error").should eq " Some text to display \n foo bar " - formatter.format_block("Some text to display", "error", true).should eq <<-BLOCK + formatter.format_block("Some text to display", "error", true).should eq normalize <<-BLOCK Some text to display @@ -21,7 +25,7 @@ describe ACON::Helper::Formatter do it "formats with diacritic letters" do formatter = ACON::Helper::Formatter.new - formatter.format_block("Du texte à afficher", "error", true).should eq <<-BLOCK + formatter.format_block("Du texte à afficher", "error", true).should eq normalize <<-BLOCK Du texte à afficher @@ -32,7 +36,7 @@ describe ACON::Helper::Formatter do end it "escapes < within the block" do - ACON::Helper::Formatter.new.format_block("some info", "error", true).should eq <<-BLOCK + ACON::Helper::Formatter.new.format_block("some info", "error", true).should eq normalize <<-BLOCK \\some info\\ diff --git a/src/components/console/spec/helper/table_spec.cr b/src/components/console/spec/helper/table_spec.cr index a2602acd9..cadffacb5 100644 --- a/src/components/console/spec/helper/table_spec.cr +++ b/src/components/console/spec/helper/table_spec.cr @@ -4,7 +4,7 @@ struct TableSpec < ASPEC::TestCase @output : IO protected def get_table_contents(table_name : String) : String - File.read(File.join __DIR__, "..", "fixtures", "helper", "table", "#{table_name}.txt").gsub(ACON::System::EOL, "\n") + File.read(File.join __DIR__, "..", "fixtures", "helper", "table", "#{table_name}.txt")#.gsub(ACON::System::EOL, "\n") end def initialize @@ -29,7 +29,7 @@ struct TableSpec < ASPEC::TestCase .row(0, %w(a b c)) .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +-------+------+-------+ | false | true | false | +-------+------+-------+ @@ -51,7 +51,7 @@ struct TableSpec < ASPEC::TestCase .style(style) .render - self.output_content(output).should eq expected + self.output_content(output).should eq expected.gsub ACON::System::EOL, "\n" end def render_provider : Hash @@ -470,7 +470,7 @@ struct TableSpec < ASPEC::TestCase .style("default") .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +------+ | 🍝 | +------+ @@ -486,7 +486,7 @@ struct TableSpec < ASPEC::TestCase .rows([[ACON::Helper::Table::Cell.new(1234)]]) .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +------+ | 1234 | +------+ @@ -500,7 +500,7 @@ struct TableSpec < ASPEC::TestCase .rows([[ACON::Helper::Table::Cell.new(3.14)]]) .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +------+ | 3.14 | +------+ @@ -523,7 +523,7 @@ struct TableSpec < ASPEC::TestCase .style("dotfull") .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE ....... . Foo . ....... @@ -542,7 +542,7 @@ struct TableSpec < ASPEC::TestCase table.render table.render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +----+---+ | foo | +----+---+ @@ -573,7 +573,7 @@ struct TableSpec < ASPEC::TestCase table.render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +---------------+----------------------+-----------------+--------+ | ISBN | Title | Author | Price | +---------------+----------------------+-----------------+--------+ @@ -602,7 +602,7 @@ struct TableSpec < ASPEC::TestCase table.render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +-----------------+----------------------+-----------------+------------+ | ISBN | Title | Author | Price | +-----------------+----------------------+-----------------+------------+ @@ -630,7 +630,7 @@ struct TableSpec < ASPEC::TestCase table.render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +-----------------+----------------------+-----------------+------------+ | ISBN | Title | Author | Price | +-----------------+----------------------+-----------------+------------+ @@ -658,7 +658,7 @@ struct TableSpec < ASPEC::TestCase table.render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +-----------------+----------------------+-----------------+------------+ | ISBN | Title | Author | Price | +-----------------+----------------------+-----------------+------------+ @@ -680,7 +680,7 @@ struct TableSpec < ASPEC::TestCase .column_max_width(3, 15) .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +---------------+-------+------------+-----------------+ | Divine Comedy | A Tal | The Lord o | And Then There | | | e of | f the Ring | Were None | @@ -710,7 +710,7 @@ struct TableSpec < ASPEC::TestCase .column_max_width(1, 30) .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +-------------+--------------------------------+ | Publication | Very long header with a lot of | | | information | @@ -732,7 +732,7 @@ struct TableSpec < ASPEC::TestCase .column_max_width(0, 5) .render - self.output_content(output).should eq <<-'TABLE' + self.output_content(output).should eq self.normalize <<-'TABLE' +-------+ | 1234\ | | 6 | @@ -761,7 +761,7 @@ struct TableSpec < ASPEC::TestCase .column_max_width(2, 15) .render - self.output_content(output).should eq <<-'TABLE' + self.output_content(output).should eq self.normalize <<-'TABLE' +-----------------+-----------------+-----------------+ | Lorem ipsum dolor sit amet, consectetur adipi | | scing elit, sed do eiusmod tempor | @@ -804,7 +804,7 @@ struct TableSpec < ASPEC::TestCase table.append_row ["9971-5-0210-0", "A Tale of Two Cities", "Charles Dickens", "139.25"] table.append_row "9971-5-0210-0", "A Tale of Two Cities", "Charles Dickens", "139.25" - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +---------------+---------------+-----------------+-------+ | ISBN | Title | Author | Price | +---------------+---------------+-----------------+-------+ @@ -842,7 +842,7 @@ struct TableSpec < ASPEC::TestCase table.append_row "9971-5-0210-0", "A Tale of Two Cities", "Charles Dickens", "139.25" - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +---------------+----------------------+-----------------+--------+ | ISBN | Title | Author | Price | +---------------+----------------------+-----------------+--------+ @@ -869,7 +869,7 @@ struct TableSpec < ASPEC::TestCase table.append_row "9971-5-0210-0", "A Tale of Two Cities", "Charles Dickens", "139.25" - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +---------------+---------------+-----------------+-------+ | ISBN | Title | Author | Price | +---------------+---------------+-----------------+-------+ @@ -898,7 +898,7 @@ struct TableSpec < ASPEC::TestCase table.append_row "9971-5-0210-0", "A Tale of Two Cities", "Charles Dickens", "139.25" - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +------+-------+--------+-------+ | ISBN | Title | Author | Price | +------+-------+--------+-------+ @@ -948,7 +948,7 @@ struct TableSpec < ASPEC::TestCase .style(style) .render - self.output_content(output).should eq expected + self.output_content(output).should eq expected.gsub ACON::System::EOL, "\n" end def title_provider : Tuple @@ -1031,7 +1031,7 @@ struct TableSpec < ASPEC::TestCase ]) .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE +-------- Reproducer --------+ | Value | 123-456 | | Some other value | 789-0 | @@ -1056,7 +1056,7 @@ struct TableSpec < ASPEC::TestCase ]) .render - self.output_content(output).should eq <<-TABLE + self.output_content(output).should eq self.normalize <<-TABLE ┌───────────────┬───────────────┬─────────────────┐ │ ISBN │ Title │ Author │ ├───────────────┼───────────────┼─────────────────┤ @@ -1076,7 +1076,7 @@ struct TableSpec < ASPEC::TestCase .horizontal .render - self.output_content(output).should eq expected + self.output_content(output).should eq expected.gsub ACON::System::EOL, "\n" end def horizontal_provider : Tuple @@ -1141,7 +1141,7 @@ struct TableSpec < ASPEC::TestCase .vertical .render - self.output_content(output).should eq expected + self.output_content(output).should eq expected.gsub ACON::System::EOL, "\n" end def vertical_provider : Hash @@ -1463,10 +1463,14 @@ struct TableSpec < ASPEC::TestCase end private def output_content(output : ACON::Output::IO) : String - output.to_s + self.normalize output.to_s end private def io_output(decorated : Bool = false) : ACON::Output::IO ACON::Output::IO.new @output, decorated: decorated end + + private def normalize(input : String) : String + input.gsub ACON::System::EOL, "\n" + end end diff --git a/src/components/console/spec/input/argv_spec.cr b/src/components/console/spec/input/argv_spec.cr index 269e80954..3abdc04ba 100644 --- a/src/components/console/spec/input/argv_spec.cr +++ b/src/components/console/spec/input/argv_spec.cr @@ -233,6 +233,11 @@ struct ARGVTest < ASPEC::TestCase def test_to_s_complex : Nil input = ACON::Input::ARGV.new "-f", "--bar=foo", "a b c d", "A\nB'C" - input.to_s.should eq "-f --bar=foo 'a b c d' 'A\nB'\"'\"'C'" + + {% if flag? :windows %} + input.to_s.should eq "-f --bar=foo \"a b c d\" A\nB'C" + {% else %} + input.to_s.should eq "-f --bar=foo 'a b c d' 'A\nB'\"'\"'C'" + {% end %} end end diff --git a/src/components/console/spec/input/hash_spec.cr b/src/components/console/spec/input/hash_spec.cr index 389f66bf1..cd85e8bd1 100644 --- a/src/components/console/spec/input/hash_spec.cr +++ b/src/components/console/spec/input/hash_spec.cr @@ -129,7 +129,12 @@ struct HashTest < ASPEC::TestCase def test_to_s_complex_mix : Nil input = ACON::Input::Hash.new "-f": nil, "-b": "bar", "--foo": "b a z", "--lala": nil, "test": "Foo", "test2": "A\nB'C" - input.to_s.should eq "-f -b bar --foo='b a z' --lala Foo 'A\nB'\"'\"'C'" + + {% if flag? :windows %} + input.to_s.should eq "-f -b bar --foo=\"b a z\" --lala Foo A\nB'C" + {% else %} + input.to_s.should eq "-f -b bar --foo='b a z' --lala Foo 'A\nB'\"'\"'C'" + {% end %} end def test_to_s_array_options : Nil diff --git a/src/components/console/spec/input/string_line_spec.cr b/src/components/console/spec/input/string_line_spec.cr index 6bf33fe7d..dfe997eb6 100644 --- a/src/components/console/spec/input/string_line_spec.cr +++ b/src/components/console/spec/input/string_line_spec.cr @@ -41,10 +41,18 @@ struct StringLineTest < ASPEC::TestCase input = ACON::Input::StringLine.new "-f foo" input.to_s.should eq "-f foo" - input = ACON::Input::StringLine.new %(-f --bar=foo "a b c d") - input.to_s.should eq "-f --bar=foo 'a b c d'" + {% if flag? :windows %} + input = ACON::Input::StringLine.new %(-f --bar=foo "a b c d") + input.to_s.should eq "-f --bar=foo \"a b c d\"" - input = ACON::Input::StringLine.new %(-f --bar=foo 'a b c d' 'A\nB\\'C') - input.to_s.should eq "-f --bar=foo 'a b c d' 'A\nB'\"'\"'C'" + input = ACON::Input::StringLine.new %(-f --bar=foo 'a b c d' 'A\nB\\'C') + input.to_s.should eq "-f --bar=foo \"a b c d\" A\nB'C" + {% else %} + input = ACON::Input::StringLine.new %(-f --bar=foo "a b c d") + input.to_s.should eq "-f --bar=foo 'a b c d'" + + input = ACON::Input::StringLine.new %(-f --bar=foo 'a b c d' 'A\nB\\'C') + input.to_s.should eq "-f --bar=foo 'a b c d' 'A\nB'\"'\"'C'" + {% end %} end end diff --git a/src/components/console/spec/style/athena_style_spec.cr b/src/components/console/spec/style/athena_style_spec.cr index 8a14c4187..b30574e57 100644 --- a/src/components/console/spec/style/athena_style_spec.cr +++ b/src/components/console/spec/style/athena_style_spec.cr @@ -18,7 +18,7 @@ struct AthenaStyleTest < ASPEC::TestCase private def assert_file_equals_string(filepath : String, string : String, *, file : String = __FILE__, line : Int32 = __LINE__) : Nil normalized_path = File.join __DIR__, "..", "fixtures", filepath - string.should match(Regex.new(File.read(normalized_path).gsub System::EOL, "\n")), file: file, line: line + string.should match(Regex.new(File.read(normalized_path).gsub ACON::System::EOL, "\n")), file: file, line: line end def test_error_style : Nil diff --git a/src/components/console/src/cursor.cr b/src/components/console/src/cursor.cr index 7abb3bb4a..f592d4fa9 100644 --- a/src/components/console/src/cursor.cr +++ b/src/components/console/src/cursor.cr @@ -127,7 +127,7 @@ struct Athena::Console::Cursor # Returns the current column, row position of the cursor. def current_position : {Int32, Int32} {% if flag? :win32 %} - return {1, 1} unless STDIN.tty? + return {1, 1} unless @input.tty? LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STD_INPUT_HANDLE), out csbi) {csbi.dwCursorPosition.x.to_i32, csbi.dwCursorPosition.y.to_i32} diff --git a/src/components/console/src/output/section.cr b/src/components/console/src/output/section.cr index 7c1f102bf..f0e957b8e 100644 --- a/src/components/console/src/output/section.cr +++ b/src/components/console/src/output/section.cr @@ -92,7 +92,7 @@ class Athena::Console::Output::Section < Athena::Console::Output::IO input.each_line do |line| lines = (self.get_display_width(line) // @terminal.width).ceil @lines += lines.zero? ? 1 : lines - @content.push line, System::EOL + @content.push line, "\n" end end diff --git a/src/components/console/src/style/athena.cr b/src/components/console/src/style/athena.cr index d5b68594c..4cbbbe91d 100644 --- a/src/components/console/src/style/athena.cr +++ b/src/components/console/src/style/athena.cr @@ -390,7 +390,7 @@ class Athena::Console::Style::Athena < Athena::Console::Style::Output decoration_length = ACON::Helper.width(message) - ACON::Helper.width(ACON::Helper.remove_decoration(self.formatter, message)) message_line_length = Math.min(@line_length - prefix_length - indent_length + decoration_length, @line_length) - message.gsub(/(.{1,#{message_line_length}})( +|$#{System::EOL}?)|(.{1,#{message_line_length}})/, "\\0#{System::EOL}").split System::EOL, remove_empty: true do |match| + message.gsub(/(.{1,#{message_line_length}})( +|$\n?)|(.{1,#{message_line_length}})/, "\\0\n").split "\n", remove_empty: true do |match| lines << match.strip end diff --git a/src/components/framework/spec/commands/debug_event_dispatcher_spec.cr b/src/components/framework/spec/commands/debug_event_dispatcher_spec.cr index b1e60107c..f9b355a13 100644 --- a/src/components/framework/spec/commands/debug_event_dispatcher_spec.cr +++ b/src/components/framework/spec/commands/debug_event_dispatcher_spec.cr @@ -23,7 +23,7 @@ struct DebugEventDispatcherCommandTest < ASPEC::TestCase ret.should eq ACON::Command::Status::SUCCESS tester.display.should be_empty - tester.error_output.should contain "[WARNING] The event 'blah' does not have any registered listeners." + tester.error_output(true).should contain "[WARNING] The event 'blah' does not have any registered listeners." end def test_specific_event_partial_match_single : Nil diff --git a/src/components/framework/spec/commands/debug_router_match_spec.cr b/src/components/framework/spec/commands/debug_router_match_spec.cr index afd896bdc..8f4d7cde9 100644 --- a/src/components/framework/spec/commands/debug_router_match_spec.cr +++ b/src/components/framework/spec/commands/debug_router_match_spec.cr @@ -14,7 +14,7 @@ struct DebugRouterMatchCommandTest < ASPEC::TestCase ret = tester.execute path_info: "/test", decorated: false ret.should eq ACON::Command::Status::FAILURE - tester.display.should contain "None of the routes match the path '/test'" + tester.display(true).should contain "None of the routes match the path '/test'" end def test_partial : Nil From 4885cdf2758cab31f0c8cdc0882c9c07f6e04cb5 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 11 Mar 2023 12:57:04 -0500 Subject: [PATCH 14/32] Fix some minor issues --- .../console/spec/helper/abstract_question_helper_test_case.cr | 2 +- src/components/framework/src/error_renderer.cr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/console/spec/helper/abstract_question_helper_test_case.cr b/src/components/console/spec/helper/abstract_question_helper_test_case.cr index e055de73d..39cdfa567 100644 --- a/src/components/console/spec/helper/abstract_question_helper_test_case.cr +++ b/src/components/console/spec/helper/abstract_question_helper_test_case.cr @@ -23,7 +23,7 @@ abstract struct AbstractQuestionHelperTest < ASPEC::TestCase output = stream.to_s if normalize - output = output.gsub ACON::System::EOL, "\n|" + output = output.gsub ACON::System::EOL, "\n" end output.should contain string.gsub ACON::System::EOL, "\n" diff --git a/src/components/framework/src/error_renderer.cr b/src/components/framework/src/error_renderer.cr index e9c53e338..8d8d0d138 100644 --- a/src/components/framework/src/error_renderer.cr +++ b/src/components/framework/src/error_renderer.cr @@ -27,7 +27,7 @@ struct Athena::Framework::ErrorRenderer file = "#{URI.encode_path(match[1])}:#{match[2]}" if m3 = match[3]? - file = "#{file}:#{match[3]}" + file = "#{file}:#{m3}" end headers["x-debug-exception-file"] = file From 095a9920aaab4b7b535be5d5b79cd83528f03979 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 16 Mar 2023 21:04:55 -0400 Subject: [PATCH 15/32] Handle non-tty cursor inputs within `cursor_spec.cr` --- src/components/console/spec/cursor_spec.cr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/console/spec/cursor_spec.cr b/src/components/console/spec/cursor_spec.cr index cdfb46957..e1a8c79b1 100644 --- a/src/components/console/spec/cursor_spec.cr +++ b/src/components/console/spec/cursor_spec.cr @@ -101,6 +101,8 @@ struct CursorTest < ASPEC::TestCase end def test_current_position_tty : Nil + pending! "Cursor input must be a TTY" unless STDIN.tty? + @cursor = ACON::Cursor.new @output @cursor.move_to_position 10, 10 From 0980be571d97369ee77f8d2b3d3cfdd4bf3d446c Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 16 Mar 2023 23:36:40 -0400 Subject: [PATCH 16/32] Re-enable other CI jobs --- .github/workflows/ci.yml | 56 ++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7824df53e..1bec34888 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,39 +12,39 @@ concurrency: cancel-in-progress: true jobs: - # check_spelling: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v3 - # - name: Check Spelling - # uses: crate-ci/typos@v1.13.18 - # check_format: - # runs-on: ubuntu-latest - # container: - # image: crystallang/crystal:latest-alpine - # steps: - # - uses: actions/checkout@v3 - # - name: Check Format - # run: crystal tool format --check - # coding_standards: - # runs-on: ubuntu-latest - # container: - # image: crystallang/crystal:latest - # steps: - # - uses: actions/checkout@v3 - # - name: Install Dependencies - # run: shards install - # env: - # SHARDS_OVERRIDE: shard.dev.yml - # - name: Ameba - # run: ./bin/ameba + check_spelling: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check Spelling + uses: crate-ci/typos@v1.13.18 + check_format: + runs-on: ubuntu-latest + container: + image: crystallang/crystal:latest-alpine + steps: + - uses: actions/checkout@v3 + - name: Check Format + run: crystal tool format --check + coding_standards: + runs-on: ubuntu-latest + container: + image: crystallang/crystal:latest + steps: + - uses: actions/checkout@v3 + - name: Install Dependencies + run: shards install + env: + SHARDS_OVERRIDE: shard.dev.yml + - name: Ameba + run: ./bin/ameba test: strategy: fail-fast: false matrix: os: - # - ubuntu-latest - # - macos-latest + - ubuntu-latest + - macos-latest - windows-latest crystal: - latest From 0a417c282e459de90e49190d40e11d734b3a9b4e Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 16 Mar 2023 23:38:32 -0400 Subject: [PATCH 17/32] Fix formatting --- src/components/console/spec/helper/table_spec.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/console/spec/helper/table_spec.cr b/src/components/console/spec/helper/table_spec.cr index cadffacb5..793a14a95 100644 --- a/src/components/console/spec/helper/table_spec.cr +++ b/src/components/console/spec/helper/table_spec.cr @@ -4,7 +4,7 @@ struct TableSpec < ASPEC::TestCase @output : IO protected def get_table_contents(table_name : String) : String - File.read(File.join __DIR__, "..", "fixtures", "helper", "table", "#{table_name}.txt")#.gsub(ACON::System::EOL, "\n") + File.read(File.join __DIR__, "..", "fixtures", "helper", "table", "#{table_name}.txt") # .gsub(ACON::System::EOL, "\n") end def initialize @@ -1470,7 +1470,7 @@ struct TableSpec < ASPEC::TestCase ACON::Output::IO.new @output, decorated: decorated end - private def normalize(input : String) : String + private def normalize(input : String) : String input.gsub ACON::System::EOL, "\n" end end From c6ac1897b545ac1001ada37563fc00ea14bdf16e Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 14 Apr 2023 22:51:29 -0400 Subject: [PATCH 18/32] Use proper EOL on windows --- src/components/spec/spec/compiler_spec.cr | 2 +- src/components/spec/src/athena-spec.cr | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/spec/spec/compiler_spec.cr b/src/components/spec/spec/compiler_spec.cr index 410974322..7ee07ffe9 100644 --- a/src/components/spec/spec/compiler_spec.cr +++ b/src/components/spec/spec/compiler_spec.cr @@ -84,7 +84,7 @@ describe Athena::Spec do end it "reports actual failing tests" do - assert_error " Expected: 2\n got: 1", <<-CODE, codegen: true + assert_error " Expected: 2#{Athena::Spec::System::EOL} got: 1", <<-CODE, codegen: true struct TestTestCase < ASPEC::TestCase def test_one 1.should eq 2 diff --git a/src/components/spec/src/athena-spec.cr b/src/components/spec/src/athena-spec.cr index 18e34aa04..b172ed5fc 100644 --- a/src/components/spec/src/athena-spec.cr +++ b/src/components/spec/src/athena-spec.cr @@ -35,4 +35,15 @@ module Athena::Spec {{unit_test.id}}.run {% end %} end + + # :nodoc: + # + # TODO: Remove this in favor of `::System::EOL` when/if https://github.com/crystal-lang/crystal/pull/11303 is released. + module System + EOL = {% if flag? :windows %} + "\r\n" + {% else %} + "\n" + {% end %} + end end From 79eba678e6f5baea02d38ab5719598e88cc66a1f Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 19 May 2023 20:06:21 -0400 Subject: [PATCH 19/32] Fix `dotenv` spec failure on windows --- src/components/dotenv/spec/athena-dotenv_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dotenv/spec/athena-dotenv_spec.cr b/src/components/dotenv/spec/athena-dotenv_spec.cr index 5ff7f0b7d..10489f470 100644 --- a/src/components/dotenv/spec/athena-dotenv_spec.cr +++ b/src/components/dotenv/spec/athena-dotenv_spec.cr @@ -156,7 +156,7 @@ struct DotEnvTest < ASPEC::TestCase {"FOO=\nBAR=${FOO:-a\"a}", "Unclosed braces on variable expansion in '.env' at line 2.\n...FOO=\\nBAR=${FOO:-a\"a}...\n ^ line 2 offset 17"}, ] of {String, String} - {% if flag? :win32 %} + {% if flag? :unix %} tests << {"FOO=$((1dd2))", "Issue expanding a command (%s\n) in '.env' at line 1.\n...FOO=$((1dd2))...\n ^ line 1 offset 13"} {% end %} From 6539f34edd223d6171023a1cfaf14d4a47b33429 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 16 Sep 2023 21:35:57 -0400 Subject: [PATCH 20/32] Remove now duplicate LibC const --- src/components/console/src/ext/terminal.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/console/src/ext/terminal.cr b/src/components/console/src/ext/terminal.cr index 30b58e622..46865b177 100644 --- a/src/components/console/src/ext/terminal.cr +++ b/src/components/console/src/ext/terminal.cr @@ -22,7 +22,6 @@ STD_INPUT_HANDLE = -10 STD_OUTPUT_HANDLE = -11 - STD_ERROR_HANDLE = -12 fun GetConsoleScreenBufferInfo(hConsoleOutput : Void*, lpConsoleScreenBufferInfo : CONSOLE_SCREEN_BUFFER_INFO*) : Void fun GetStdHandle(nStdHandle : UInt32) : Void* From 90c2d898d1020390a64f29809537243f42a443ae Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 13:58:31 -0500 Subject: [PATCH 21/32] Fix format issue --- src/components/console/spec/commands/list_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/console/spec/commands/list_spec.cr b/src/components/console/spec/commands/list_spec.cr index 5d2836c7c..72baeab9e 100644 --- a/src/components/console/spec/commands/list_spec.cr +++ b/src/components/console/spec/commands/list_spec.cr @@ -37,7 +37,7 @@ struct ListCommandTest < ASPEC::TestCase tester = ACON::Spec::CommandTester.new app.get("list") tester.execute command: "list", decorated: false - + tester.display(true).should eq normalize <<-OUTPUT foo 0.1.0 From a9f0860aecf7dced646097f056df68ee9c168416 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 16:41:59 -0500 Subject: [PATCH 22/32] Force line endings to `lf` --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..6a728af96 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.cr text eol=lf From 46f37f7e84910b6fb1238e940d9bb0d0c6f72ef1 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 17:26:42 -0500 Subject: [PATCH 23/32] Use `ACON::System::EOL` in more places --- .../console/spec/application_spec.cr | 10 ++++---- .../console/spec/application_tester_spec.cr | 4 ++-- src/components/console/spec/command_spec.cr | 4 ++-- .../console/spec/command_tester_spec.cr | 4 ++-- .../abstract_question_helper_test_case.cr | 2 +- .../spec/helper/athena_question_spec.cr | 2 +- .../output/console_section_output_spec.cr | 24 +++++++++---------- src/components/console/spec/output/io_spec.cr | 2 +- .../console/spec/style/athena_style_spec.cr | 4 ++-- .../console/src/completion/output/zsh.cr | 2 +- src/components/console/src/output/io.cr | 8 +++++-- 11 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/components/console/spec/application_spec.cr b/src/components/console/spec/application_spec.cr index 4eea403fa..7a85460e3 100644 --- a/src/components/console/spec/application_spec.cr +++ b/src/components/console/spec/application_spec.cr @@ -633,7 +633,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "foo", decorated: false - self.assert_file_equals_string "text/application_renderexception_synopsis_escapeslines.txt", tester.display + self.assert_file_equals_string "text/application_renderexception_synopsis_escapeslines.txt", tester.display true end def test_run_passes_io_thru : Nil @@ -792,10 +792,10 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run command: "foo:bar", "--no-interaction": true, decorated: false - tester.display.should eq "execute called\n" + tester.display.should eq "execute called#{ACON::System::EOL}" tester.run command: "foo:bar", "-n": true, decorated: false - tester.display.should eq "execute called\n" + tester.display.should eq "execute called#{ACON::System::EOL}" end def test_run_global_option_and_no_command : Nil @@ -974,7 +974,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run interactive: false - tester.display.should eq "execute called\n" + tester.display.should eq "execute called#{ACON::System::EOL}" # TODO: Test custom application default. end @@ -987,7 +987,7 @@ struct ApplicationTest < ASPEC::TestCase tester = ACON::Spec::ApplicationTester.new app tester.run "--fooopt": "opt", interactive: false - tester.display.should eq "execute called\nopt\n" + tester.display.should eq "execute called#{ACON::System::EOL}opt#{ACON::System::EOL}" end def test_run_custom_single_default_command : Nil diff --git a/src/components/console/spec/application_tester_spec.cr b/src/components/console/spec/application_tester_spec.cr index 5776d6a99..49ac54b62 100644 --- a/src/components/console/spec/application_tester_spec.cr +++ b/src/components/console/spec/application_tester_spec.cr @@ -29,11 +29,11 @@ struct ApplicationTesterTest < ASPEC::TestCase end def test_output : Nil - @tester.output.to_s.should eq "foo\n" + @tester.output.to_s.should eq "foo#{ACON::System::EOL}" end def test_display : Nil - @tester.display.to_s.should eq "foo\n" + @tester.display.to_s.should eq "foo#{ACON::System::EOL}" end def test_status : Nil diff --git a/src/components/console/spec/command_spec.cr b/src/components/console/spec/command_spec.cr index e393b8ec3..5234b501b 100644 --- a/src/components/console/spec/command_spec.cr +++ b/src/components/console/spec/command_spec.cr @@ -179,13 +179,13 @@ describe ACON::Command do it "interactive" do tester = ACON::Spec::CommandTester.new TestCommand.new tester.execute interactive: true - tester.display.should eq "interact called\nexecute called\n" + tester.display.should eq "interact called#{ACON::System::EOL}execute called#{ACON::System::EOL}" end it "non-interactive" do tester = ACON::Spec::CommandTester.new TestCommand.new tester.execute interactive: false - tester.display.should eq "execute called\n" + tester.display.should eq "execute called#{ACON::System::EOL}" end it "invalid option" do diff --git a/src/components/console/spec/command_tester_spec.cr b/src/components/console/spec/command_tester_spec.cr index ac0ba68d0..b0a4b27f8 100644 --- a/src/components/console/spec/command_tester_spec.cr +++ b/src/components/console/spec/command_tester_spec.cr @@ -28,11 +28,11 @@ struct CommandTesterTest < ASPEC::TestCase end def test_output : Nil - @tester.output.to_s.should eq "foo\n" + @tester.output.to_s.should eq "foo#{ACON::System::EOL}" end def test_display : Nil - @tester.display.to_s.should eq "foo\n" + @tester.display.to_s.should eq "foo#{ACON::System::EOL}" end def test_display_before_calling_execute : Nil diff --git a/src/components/console/spec/helper/abstract_question_helper_test_case.cr b/src/components/console/spec/helper/abstract_question_helper_test_case.cr index 39cdfa567..96fa45ed6 100644 --- a/src/components/console/spec/helper/abstract_question_helper_test_case.cr +++ b/src/components/console/spec/helper/abstract_question_helper_test_case.cr @@ -26,6 +26,6 @@ abstract struct AbstractQuestionHelperTest < ASPEC::TestCase output = output.gsub ACON::System::EOL, "\n" end - output.should contain string.gsub ACON::System::EOL, "\n" + output.should contain string end end diff --git a/src/components/console/spec/helper/athena_question_spec.cr b/src/components/console/spec/helper/athena_question_spec.cr index 283532bd0..c11cff8a5 100644 --- a/src/components/console/spec/helper/athena_question_spec.cr +++ b/src/components/console/spec/helper/athena_question_spec.cr @@ -152,7 +152,7 @@ struct AthenaQuestionTest < AbstractQuestionHelperTest @helper.ask input, @output, question end - self.assert_output_contains <<-OUT + self.assert_output_contains <<-OUT, true qqq: [0] foo >ccc> diff --git a/src/components/console/spec/output/console_section_output_spec.cr b/src/components/console/spec/output/console_section_output_spec.cr index 21e1ad870..de4c671ac 100644 --- a/src/components/console/spec/output/console_section_output_spec.cr +++ b/src/components/console/spec/output/console_section_output_spec.cr @@ -19,10 +19,10 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase sections = Array(ACON::Output::Section).new output = ACON::Output::Section.new @io, sections, :normal, true, ACON::Formatter::Output.new - output.puts "Foo\nBar" + output.puts "Foo#{ACON::System::EOL}Bar" output.clear - @io.to_s.should eq "Foo\nBar\n\e[2A\e[0J" + @io.to_s.should eq "Foo#{ACON::System::EOL}Bar#{ACON::System::EOL}\e[2A\e[0J" end def test_clear_number_of_lines : Nil @@ -32,7 +32,7 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase output.puts "Foo\nBar\nBaz\nFooBar" output.clear 2 - @io.to_s.should eq "Foo\nBar\nBaz\nFooBar\n\e[2A\e[0J" + @io.to_s.should eq "Foo\nBar\nBaz\nFooBar#{ACON::System::EOL}\e[2A\e[0J" end def test_clear_number_more_than_current_size : Nil @@ -42,7 +42,7 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase output.puts "Foo" output.clear 2 - @io.to_s.should eq "Foo\n\e[2A\e[0J" + @io.to_s.should eq "Foo#{ACON::System::EOL}\e[2A\e[0J" end def test_clear_number_of_lines_multiple_sections : Nil @@ -55,7 +55,7 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase output2.clear 1 output1.puts "Baz" - @io.to_s.should eq "Foo\nBar\n\e[1A\e[0J\e[1A\e[0JBaz\nFoo\n" + @io.to_s.should eq "Foo#{ACON::System::EOL}Bar#{ACON::System::EOL}\e[1A\e[0J\e[1A\e[0JBaz#{ACON::System::EOL}Foo#{ACON::System::EOL}" end def test_clear_number_of_lines_multiple_sections_preserves_empty_lines : Nil @@ -63,11 +63,11 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase output1 = ACON::Output::Section.new @io, sections, :normal, true, ACON::Formatter::Output.new output2 = ACON::Output::Section.new @io, sections, :normal, true, ACON::Formatter::Output.new - output2.puts "\nfoo" + output2.puts "#{ACON::System::EOL}foo" output2.clear 1 output1.puts "bar" - @io.to_s.should eq "\nfoo\n\e[1A\e[0J\e[1A\e[0Jbar\n\n" + @io.to_s.should eq "#{ACON::System::EOL}foo#{ACON::System::EOL}\e[1A\e[0J\e[1A\e[0Jbar#{ACON::System::EOL}#{ACON::System::EOL}" end def test_clear_with_question : Nil @@ -81,7 +81,7 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase ACON::Helper::Question.new.ask input, output, ACON::Question(String?).new("What's your favorite superhero?", nil) output.clear - @io.to_s.should eq "What's your favorite superhero?\n\e[2A\e[0J" + @io.to_s.should eq "What's your favorite superhero?#{ACON::System::EOL}\e[2A\e[0J" end def test_clear_after_overwrite_clear_correct_number_of_lines : Nil @@ -106,17 +106,17 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase output.puts "Foo" output.overwrite "Bar" - @io.to_s.should eq "Foo\n\e[1A\e[0JBar\n" + @io.to_s.should eq "Foo#{ACON::System::EOL}\e[1A\e[0JBar#{ACON::System::EOL}" end def test_overwrite_multiple_lines : Nil sections = Array(ACON::Output::Section).new output = ACON::Output::Section.new @io, sections, :normal, true, ACON::Formatter::Output.new - output.puts "Foo\nBar\nBaz" + output.puts "Foo#{ACON::System::EOL}Bar#{ACON::System::EOL}Baz" output.overwrite "Bar" - @io.to_s.should eq "Foo\nBar\nBaz\n\e[3A\e[0JBar\n" + @io.to_s.should eq "Foo#{ACON::System::EOL}Bar#{ACON::System::EOL}Baz#{ACON::System::EOL}\e[3A\e[0JBar#{ACON::System::EOL}" end def test_overwrite_multiple_section_output : Nil @@ -130,7 +130,7 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase output1.overwrite "Baz" output2.overwrite "Foobar" - @io.to_s.should eq "Foo\nBar\n\e[2A\e[0JBar\n\e[1A\e[0JBaz\nBar\n\e[1A\e[0JFoobar\n" + @io.to_s.should eq "Foo#{ACON::System::EOL}Bar#{ACON::System::EOL}\e[2A\e[0JBar#{ACON::System::EOL}\e[1A\e[0JBaz#{ACON::System::EOL}Bar#{ACON::System::EOL}\e[1A\e[0JFoobar#{ACON::System::EOL}" end def test_max_height : Nil diff --git a/src/components/console/spec/output/io_spec.cr b/src/components/console/spec/output/io_spec.cr index f998606a2..86fe059d5 100644 --- a/src/components/console/spec/output/io_spec.cr +++ b/src/components/console/spec/output/io_spec.cr @@ -14,6 +14,6 @@ struct IOTest < ASPEC::TestCase def test_do_write : Nil output = ACON::Output::IO.new @io output.puts "foo" - output.to_s.should eq "foo\n" + output.to_s.should eq "foo#{ACON::System::EOL}" end end diff --git a/src/components/console/spec/style/athena_style_spec.cr b/src/components/console/spec/style/athena_style_spec.cr index 7e4829767..bcacd2ac5 100644 --- a/src/components/console/spec/style/athena_style_spec.cr +++ b/src/components/console/spec/style/athena_style_spec.cr @@ -29,7 +29,7 @@ struct AthenaStyleTest < ASPEC::TestCase style = ACON::Style::Athena.new ACON::Input::Hash.new({} of String => String), output style.error_style.puts "foo" - io.to_s.should eq "foo\n" + io.to_s.should eq "foo#{ACON::System::EOL}" end def test_error_style_non_console_output : Nil @@ -38,7 +38,7 @@ struct AthenaStyleTest < ASPEC::TestCase style = ACON::Style::Athena.new ACON::Input::Hash.new({} of String => String), output style.error_style.puts "foo" - io.to_s.should eq "foo\n" + io.to_s.should eq "foo#{ACON::System::EOL}" end @[DataProvider("output_provider")] diff --git a/src/components/console/src/completion/output/zsh.cr b/src/components/console/src/completion/output/zsh.cr index 24ffa5b73..6bb6e6b10 100644 --- a/src/components/console/src/completion/output/zsh.cr +++ b/src/components/console/src/completion/output/zsh.cr @@ -18,6 +18,6 @@ struct Athena::Console::Completion::Output::Zsh < Athena::Console::Completion::O end end - output.puts values.join "\n" + output.print "#{values.join "\n"}\n" end end diff --git a/src/components/console/src/output/io.cr b/src/components/console/src/output/io.cr index a6dc7ea90..7b9513197 100644 --- a/src/components/console/src/output/io.cr +++ b/src/components/console/src/output/io.cr @@ -16,11 +16,15 @@ class Athena::Console::Output::IO < Athena::Console::Output end protected def do_write(message : String, new_line : Bool) : Nil - new_line ? @io.puts(message) : @io.print(message) + message += ACON::System::EOL if new_line + + @io.print message end private def io_do_write(message : String, new_line : Bool) : Nil - new_line ? @io.puts(message) : @io.print(message) + message += ACON::System::EOL if new_line + + @io.print message end private def has_color_support? : Bool From 62ef814ec7ea772f69155841e5c27b574f8f9c34 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 17:56:14 -0500 Subject: [PATCH 24/32] Try to debug failing tests on CI --- .../console/spec/output/console_section_output_spec.cr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/console/spec/output/console_section_output_spec.cr b/src/components/console/spec/output/console_section_output_spec.cr index de4c671ac..0beca2b35 100644 --- a/src/components/console/spec/output/console_section_output_spec.cr +++ b/src/components/console/spec/output/console_section_output_spec.cr @@ -99,7 +99,10 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase @io.to_s.should eq expected.to_s end + @[Focus] def test_overwrite : Nil + pp ENV + sections = Array(ACON::Output::Section).new output = ACON::Output::Section.new @io, sections, :normal, true, ACON::Formatter::Output.new From 9e9cc55be02903bc517ee991063495832dbe0ad3 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 18:11:59 -0500 Subject: [PATCH 25/32] Try not using the test script --- .github/workflows/ci.yml | 3 +-- .../console/spec/output/console_section_output_spec.cr | 3 --- src/components/console/src/output/section.cr | 6 +++++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf159479c..f8129fe2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,5 +66,4 @@ jobs: env: SHARDS_OVERRIDE: shard.dev.yml - name: Specs - run: ./scripts/test.sh - shell: bash + run: crystal spec --order=random src/components/console diff --git a/src/components/console/spec/output/console_section_output_spec.cr b/src/components/console/spec/output/console_section_output_spec.cr index 0beca2b35..de4c671ac 100644 --- a/src/components/console/spec/output/console_section_output_spec.cr +++ b/src/components/console/spec/output/console_section_output_spec.cr @@ -99,10 +99,7 @@ struct ConsoleSectionOutputTest < ASPEC::TestCase @io.to_s.should eq expected.to_s end - @[Focus] def test_overwrite : Nil - pp ENV - sections = Array(ACON::Output::Section).new output = ACON::Output::Section.new @io, sections, :normal, true, ACON::Formatter::Output.new diff --git a/src/components/console/src/output/section.cr b/src/components/console/src/output/section.cr index b5278ed0a..a240bbb5f 100644 --- a/src/components/console/src/output/section.cr +++ b/src/components/console/src/output/section.cr @@ -149,7 +149,11 @@ class Athena::Console::Output::Section < Athena::Console::Output::IO new_line = true end - return super unless self.decorated? + unless self.decorated? + super message, new_line + + return + end # Check if the previous line (last entry of @content) needs to be continued # i.e. does not end with a line break. In which case, it needs to be erased first From d29c65f66a5479da9c4def21176fbf6aa1bc6f55 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 19:56:44 -0500 Subject: [PATCH 26/32] See what terminal size is on GHA --- src/components/console/spec/application_spec.cr | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/console/spec/application_spec.cr b/src/components/console/spec/application_spec.cr index 7a85460e3..cc053d254 100644 --- a/src/components/console/spec/application_spec.cr +++ b/src/components/console/spec/application_spec.cr @@ -265,7 +265,10 @@ struct ApplicationTest < ASPEC::TestCase end @[DataProvider("ambiguous_abbreviations_provider")] + @[Focus] def test_find_ambiguous_abbreviations(abbreviation, expected_message) : Nil + pp ACON::Terminal.new.size + app = ACON::Application.new "foo" app.add FooCommand.new app.add Foo1Command.new @@ -278,9 +281,9 @@ struct ApplicationTest < ASPEC::TestCase def ambiguous_abbreviations_provider : Tuple { - {"f", "Command 'f' is not defined."}, + # {"f", "Command 'f' is not defined."}, {"a", "Command 'a' is ambiguous."}, - {"foo:b", "Command 'foo:b' is ambiguous."}, + # {"foo:b", "Command 'foo:b' is ambiguous."}, } end From 4c233859dd0742ef6a8fbcf87ffc08475bc9a11a Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 20:02:18 -0500 Subject: [PATCH 27/32] Maybe fix Windows API console size logic? --- src/components/console/src/ext/terminal.cr | 36 ++++++++++++---------- src/components/console/src/terminal.cr | 6 ++-- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/components/console/src/ext/terminal.cr b/src/components/console/src/ext/terminal.cr index 46865b177..46aec46da 100644 --- a/src/components/console/src/ext/terminal.cr +++ b/src/components/console/src/ext/terminal.cr @@ -1,30 +1,32 @@ {% if flag?(:win32) %} lib LibC - struct COORD - x : Int16 - y : Int16 + STDOUT_HANDLE = 0xFFFFFFF5 + + struct Point + x : UInt16 + y : UInt16 end - struct SMALL_RECT - left : Int16 - top : Int16 - right : Int16 - bottom : Int16 + struct SmallRect + left : UInt16 + top : UInt16 + right : UInt16 + bottom : UInt16 end - struct CONSOLE_SCREEN_BUFFER_INFO - dwSize : COORD - dwCursorPosition : COORD + struct ScreenBufferInfo + dwSize : Point + dwCursorPosition : Point wAttributes : UInt16 - srWindow : SMALL_RECT - dwMaximumWindowSize : COORD + srWindow : SmallRect + dwMaximumWindowSize : Point end - STD_INPUT_HANDLE = -10 - STD_OUTPUT_HANDLE = -11 + alias Handle = Void* + alias ScreenBufferInfoPtr = ScreenBufferInfo* - fun GetConsoleScreenBufferInfo(hConsoleOutput : Void*, lpConsoleScreenBufferInfo : CONSOLE_SCREEN_BUFFER_INFO*) : Void - fun GetStdHandle(nStdHandle : UInt32) : Void* + fun GetConsoleScreenBufferInfo(handle : Handle, info : ScreenBufferInfoPtr) : Bool + fun GetStdHandle(handle : UInt32) : Handle end {% else %} lib LibC diff --git a/src/components/console/src/terminal.cr b/src/components/console/src/terminal.cr index 6bb3dc7ec..c0dde8046 100644 --- a/src/components/console/src/terminal.cr +++ b/src/components/console/src/terminal.cr @@ -61,9 +61,9 @@ struct Athena::Console::Terminal # Detect terminal size Windows `GetConsoleScreenBufferInfo`. private def self.size_from_screen_buffer - LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STD_OUTPUT_HANDLE), out csbi) - cols = csbi.srWindow.right - csbi.srWindow.left + 1 - rows = csbi.srWindow.bottom - csbi.srWindow.top + 1 + LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STDOUT_HANDLE), out csbi) + rows = csbi.srWindow.right - csbi.srWindow.left + 1 + cols = csbi.srWindow.bottom - csbi.srWindow.top + 1 {cols.to_i32, rows.to_i32} end From c2ea365631d06f60c4140b0f9fe6ce215625ae4c Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 20:05:55 -0500 Subject: [PATCH 28/32] Update missing reference --- src/components/console/src/cursor.cr | 2 +- src/components/console/src/terminal.cr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/console/src/cursor.cr b/src/components/console/src/cursor.cr index f592d4fa9..5f77bc004 100644 --- a/src/components/console/src/cursor.cr +++ b/src/components/console/src/cursor.cr @@ -129,7 +129,7 @@ struct Athena::Console::Cursor {% if flag? :win32 %} return {1, 1} unless @input.tty? - LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STD_INPUT_HANDLE), out csbi) + LibC.GetConsoleScreenBufferInfo(LibC.GetStdHandle(LibC::STDOUT_HANDLE), out csbi) {csbi.dwCursorPosition.x.to_i32, csbi.dwCursorPosition.y.to_i32} {% else %} return {1, 1} unless @input.tty? diff --git a/src/components/console/src/terminal.cr b/src/components/console/src/terminal.cr index c0dde8046..cb52249a5 100644 --- a/src/components/console/src/terminal.cr +++ b/src/components/console/src/terminal.cr @@ -55,7 +55,7 @@ struct Athena::Console::Terminal {% if flag?(:win32) %} protected def self.init_dimensions : Nil - return if check_size(size_from_screen_buffer) + # return if check_size(size_from_screen_buffer) return if check_size(size_from_ansicon) end From c79f32c2f252b7a1723e97d91b884e82ad9dd1b3 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 20:08:33 -0500 Subject: [PATCH 29/32] Now check with libc code enabled again --- src/components/console/src/terminal.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/console/src/terminal.cr b/src/components/console/src/terminal.cr index cb52249a5..c0dde8046 100644 --- a/src/components/console/src/terminal.cr +++ b/src/components/console/src/terminal.cr @@ -55,7 +55,7 @@ struct Athena::Console::Terminal {% if flag?(:win32) %} protected def self.init_dimensions : Nil - # return if check_size(size_from_screen_buffer) + return if check_size(size_from_screen_buffer) return if check_size(size_from_ansicon) end From 42e861eb1ca95beb88e610c111856746d752cc59 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 20:12:57 -0500 Subject: [PATCH 30/32] Enable all specs again --- src/components/console/spec/application_spec.cr | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/console/spec/application_spec.cr b/src/components/console/spec/application_spec.cr index cc053d254..7a85460e3 100644 --- a/src/components/console/spec/application_spec.cr +++ b/src/components/console/spec/application_spec.cr @@ -265,10 +265,7 @@ struct ApplicationTest < ASPEC::TestCase end @[DataProvider("ambiguous_abbreviations_provider")] - @[Focus] def test_find_ambiguous_abbreviations(abbreviation, expected_message) : Nil - pp ACON::Terminal.new.size - app = ACON::Application.new "foo" app.add FooCommand.new app.add Foo1Command.new @@ -281,9 +278,9 @@ struct ApplicationTest < ASPEC::TestCase def ambiguous_abbreviations_provider : Tuple { - # {"f", "Command 'f' is not defined."}, + {"f", "Command 'f' is not defined."}, {"a", "Command 'a' is ambiguous."}, - # {"foo:b", "Command 'foo:b' is ambiguous."}, + {"foo:b", "Command 'foo:b' is ambiguous."}, } end From 6cb1c03ece9e1954ecc982843b14b5c92d881228 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 20:17:14 -0500 Subject: [PATCH 31/32] Enable all component specs again --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8129fe2a..cf159479c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,4 +66,5 @@ jobs: env: SHARDS_OVERRIDE: shard.dev.yml - name: Specs - run: crystal spec --order=random src/components/console + run: ./scripts/test.sh + shell: bash From bf63e51914879ad7ef50fc2bd7249cb06e4ee624 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Sat, 3 Feb 2024 20:31:21 -0500 Subject: [PATCH 32/32] Fix `spec` specs --- src/components/spec/spec/compiler_spec.cr | 2 +- src/components/spec/src/athena-spec.cr | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/components/spec/spec/compiler_spec.cr b/src/components/spec/spec/compiler_spec.cr index 7ee07ffe9..410974322 100644 --- a/src/components/spec/spec/compiler_spec.cr +++ b/src/components/spec/spec/compiler_spec.cr @@ -84,7 +84,7 @@ describe Athena::Spec do end it "reports actual failing tests" do - assert_error " Expected: 2#{Athena::Spec::System::EOL} got: 1", <<-CODE, codegen: true + assert_error " Expected: 2\n got: 1", <<-CODE, codegen: true struct TestTestCase < ASPEC::TestCase def test_one 1.should eq 2 diff --git a/src/components/spec/src/athena-spec.cr b/src/components/spec/src/athena-spec.cr index 453419065..c385c3d9e 100644 --- a/src/components/spec/src/athena-spec.cr +++ b/src/components/spec/src/athena-spec.cr @@ -35,15 +35,4 @@ module Athena::Spec {{unit_test.id}}.run {% end %} end - - # :nodoc: - # - # TODO: Remove this in favor of `::System::EOL` when/if https://github.com/crystal-lang/crystal/pull/11303 is released. - module System - EOL = {% if flag? :windows %} - "\r\n" - {% else %} - "\n" - {% end %} - end end