markdown_exec 3.3.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +8 -1
  5. data/README.md +262 -116
  6. data/Rakefile +2 -1
  7. data/bats/block-type-shell-require-ux.bats +15 -0
  8. data/bats/block-type-ux-auto.bats +1 -1
  9. data/bats/block-type-ux-default.bats +1 -1
  10. data/bats/block-type-ux-echo-hash.bats +2 -2
  11. data/bats/block-type-ux-exec-hash.bats +2 -2
  12. data/bats/block-type-ux-exec.bats +1 -1
  13. data/bats/block-type-ux-no-name.bats +8 -0
  14. data/bats/block-type-ux-require-context.bats +14 -0
  15. data/bats/block-type-ux-row-format.bats +1 -1
  16. data/bats/block-type-ux-transform.bats +1 -1
  17. data/bats/command-substitution-options.bats +2 -2
  18. data/bats/import-directive-line-continuation.bats +9 -0
  19. data/bats/import-directive-parameter-symbols.bats +1 -1
  20. data/bats/import-parameter-symbols.bats +1 -1
  21. data/bats/option-expansion.bats +1 -1
  22. data/bats/options.bats +2 -2
  23. data/bats/table-column-truncate.bats +1 -1
  24. data/bats/table.bats +1 -1
  25. data/bats/test_helper.bash +4 -3
  26. data/demo/trap.demo1.gif +0 -0
  27. data/demo/trap.demo1.mp4 +0 -0
  28. data/docs/dev/block-type-shell-require-ux.md +18 -0
  29. data/docs/dev/block-type-ux-format.md +10 -0
  30. data/docs/dev/block-type-ux-no-name.md +17 -0
  31. data/docs/dev/block-type-ux-require-context.md +32 -0
  32. data/docs/dev/block-type-ux-require.md +8 -4
  33. data/docs/dev/block-type-ux-row-format.md +1 -1
  34. data/docs/dev/import-directive-line-continuation.md +5 -0
  35. data/docs/dev/import-directive-parameter-symbols.md +0 -2
  36. data/docs/dev/import-parameter-symbols-template.md +7 -5
  37. data/docs/dev/import-parameter-symbols.md +10 -2
  38. data/docs/dev/table-column-truncate.md +1 -1
  39. data/examples/colors.md +31 -29
  40. data/lib/cached_nested_file_reader.rb +31 -51
  41. data/lib/command_result.rb +5 -5
  42. data/lib/constants.rb +3 -1
  43. data/lib/fcb.rb +22 -8
  44. data/lib/format_table.rb +22 -7
  45. data/lib/hash_delegator.rb +77 -33
  46. data/lib/link_history.rb +1 -1
  47. data/lib/markdown_exec/version.rb +1 -1
  48. data/lib/menu.src.yml +47 -38
  49. data/lib/menu.yml +43 -34
  50. data/lib/parameter_expansion.rb +918 -0
  51. data/lib/parse_animation_to_tts.rb +4417 -0
  52. data/lib/resize_terminal.rb +19 -16
  53. data/lib/ww.rb +493 -15
  54. metadata +15 -2
@@ -5,15 +5,16 @@
5
5
  require 'io/console'
6
6
  require 'timeout'
7
7
  require_relative 'env_interface'
8
+ require_relative 'ww'
8
9
 
9
10
  # This function attempts to resize the terminal to its maximum supported size.
10
11
  # It checks if the script is running in an interactive terminal with no arguments.
11
12
  # If so, it sends escape sequences to query the terminal size and reads the response.
12
13
  # It then compares the current terminal size with the calculated size and adjusts if necessary.
13
14
  # If the terminal emulator is unsupported, it prints an error message.
14
- # 2024-8-23 add require_stdout to allow for testing
15
+ # 2024-08-23 add require_stdout to allow for testing
15
16
  def resize_terminal(show_dims: false, show_rectangle: false,
16
- require_stdout: true)
17
+ require_stdout: true, debug: $debug)
17
18
  # Check if running in an interactive terminal and no arguments are provided
18
19
  unless $stdin.tty?
19
20
  warn 'Usage: resize_terminal'
@@ -37,14 +38,14 @@ def resize_terminal(show_dims: false, show_rectangle: false,
37
38
  end
38
39
 
39
40
  if response.empty?
40
- warn "Error: No response received from terminal. Response: #{response.inspect}"
41
+ wwe "Error: No response received from terminal. Response: #{response.inspect}" if debug
41
42
  return 1
42
43
  end
43
44
 
44
45
  # Match the response to extract the terminal dimensions
45
46
  match_data = response.match(/\[(\d+);(\d+)R/)
46
47
  unless match_data
47
- warn "Error: Failed to match terminal response pattern. Response: #{response.inspect}"
48
+ wwe "Error: Failed to match terminal response pattern. Response: #{response.inspect}" if debug
48
49
  return 1
49
50
  end
50
51
 
@@ -60,7 +61,7 @@ def resize_terminal(show_dims: false, show_rectangle: false,
60
61
  nil)} -> #{calculated_columns}x#{calculated_rows}" if show_dims
61
62
  system("stty cols #{calculated_columns} rows #{calculated_rows}")
62
63
  else
63
- warn "Error: Calculated terminal size is invalid. Columns: #{calculated_columns}, Rows: #{calculated_rows}"
64
+ wwe "Error: Calculated terminal size is invalid. Columns: #{calculated_columns}, Rows: #{calculated_rows}" if debug
64
65
  return 1
65
66
  end
66
67
 
@@ -68,10 +69,10 @@ def resize_terminal(show_dims: false, show_rectangle: false,
68
69
  display_terminal_rectangle(calculated_columns,
69
70
  calculated_rows) if show_rectangle
70
71
  rescue Timeout::Error
71
- warn 'Error: Timeout while reading terminal response. Unsupported terminal emulator.'
72
+ wwe 'Error: Timeout while reading terminal response. Unsupported terminal emulator.' if debug
72
73
  1
73
74
  rescue StandardError => err
74
- warn "Error: #{err.message}. Unsupported terminal emulator."
75
+ wwe "Error: #{err.message}. Unsupported terminal emulator." if debug
75
76
  1
76
77
  ensure
77
78
  EnvInterface.set('COLUMNS', @original_columns)
@@ -141,11 +142,10 @@ class ResizeTerminalTest < Minitest::Test
141
142
  $stdin.stub(:tty?, true) do
142
143
  ARGV.replace([])
143
144
  $stdin.stub(:getch, -> { '' }) do
144
- # assert_output(nil, /Error: No response received from terminal/) do
145
- assert_output(nil,
146
- "Error: Timeout while reading terminal response. Unsupported terminal emulator.\n") do
147
- assert_equal 1, resize_terminal(require_stdout: false)
145
+ error = assert_raises(StandardError) do
146
+ resize_terminal(require_stdout: false, debug: true)
148
147
  end
148
+ assert_equal 'Error: Timeout while reading terminal response. Unsupported terminal emulator.', error.message
149
149
  end
150
150
  end
151
151
  end
@@ -156,10 +156,11 @@ class ResizeTerminalTest < Minitest::Test
156
156
  ARGV.replace([])
157
157
  response = "\e[999;999H\e[6n\e[InvalidResponse".dup
158
158
  $stdin.stub(:getch, -> { response.slice!(0) || '' }) do
159
- assert_output(nil,
160
- /Error: Failed to match terminal response pattern/) do
161
- assert_equal 1, resize_terminal(require_stdout: false)
159
+ error = assert_raises(StandardError) do
160
+ resize_terminal(require_stdout: false, debug: true)
162
161
  end
162
+ assert_match /Error: Failed to match terminal response pattern/, error.message
163
+
163
164
  end
164
165
  end
165
166
  end
@@ -169,9 +170,11 @@ class ResizeTerminalTest < Minitest::Test
169
170
  $stdin.stub(:tty?, true) do
170
171
  ARGV.replace([])
171
172
  Timeout.stub(:timeout, ->(_) { raise Timeout::Error }) do
172
- assert_output(nil, /Error: Timeout while reading terminal response/) do
173
- assert_equal 1, resize_terminal(require_stdout: false)
173
+ error = assert_raises(StandardError) do
174
+ resize_terminal(require_stdout: false, debug: true)
174
175
  end
176
+ assert_match /Error: Timeout while reading terminal response/, error.message
177
+
175
178
  end
176
179
  end
177
180
  end
data/lib/ww.rb CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env -S bundle exec ruby
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # encoding=utf-8
@@ -12,16 +13,16 @@ DEPTH_ICON = '›'
12
13
  LOG_LEVELS = %i[debug info warn error fatal].freeze
13
14
 
14
15
  # is enabled
15
- def enable_debugging
16
+ private def enable_debugging
16
17
  ENV.fetch('WW', '0').to_i.positive?
17
18
  end
18
19
 
19
20
  # is enabled, not silent
20
- def env_show_attribution
21
+ private def env_show_attribution
21
22
  ENV['WW'] != '0'
22
23
  end
23
24
 
24
- def is_new_alg?
25
+ private def is_new_alg?
25
26
  # use the new algo only if env var is ALG is not empty
26
27
  !ENV.fetch('ALG', '').empty?
27
28
 
@@ -84,6 +85,11 @@ def wwa(*objs, **kwargs)
84
85
  exit 1
85
86
  end
86
87
 
88
+ # break into the debugger if enabled
89
+ def wwb
90
+ binding.irb if $debug
91
+ end
92
+
87
93
  # output the object and backtrace for the error
88
94
  # raise the error for the caller to handle
89
95
  def wwe(*objs, **kwargs)
@@ -136,9 +142,33 @@ def wwt(*objs, **kwargs)
136
142
  ))
137
143
  end
138
144
 
145
+ # enhanced expression wrapper with better context
146
+ # usage: wwx { some_expression } or wwx(expression)
147
+ def wwx(expression = nil, **kwargs, &block)
148
+ if block_given?
149
+ # Block form: wwx { some_expression }
150
+ result = block.call
151
+ return result unless $debug
152
+
153
+ # Capture the source location of the block
154
+ locations = kwargs[:locations] || caller_locations
155
+ ww0(result, **kwargs.merge(locations: locations, context: 'block'))
156
+ elsif expression
157
+ # Direct form: wwx(some_expression)
158
+ return expression unless $debug
159
+
160
+ locations = kwargs[:locations] || caller_locations
161
+ ww0(expression, **kwargs.merge(locations: locations, context: 'direct'))
162
+ else
163
+ # No arguments - just return nil
164
+ nil
165
+ end
166
+ end
167
+
139
168
  # output the formatted data and location
140
169
  def ww0(*objs,
141
170
  category: $ww_category,
171
+ context: nil,
142
172
  full_backtrace: false,
143
173
  level: :debug,
144
174
  locations: caller_locations,
@@ -172,12 +202,13 @@ def ww0(*objs,
172
202
  # Add optional timestamp
173
203
  time_prefix = timestamp ? "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " : ''
174
204
 
175
- # Add log level and category prefix
205
+ # Add log level, category, and context prefix
176
206
  level_prefix = "[#{level.to_s.upcase}]"
177
207
  category_prefix = category ? "[#{category}] " : ''
208
+ context_prefix = context ? "[#{context}] " : ''
178
209
 
179
210
  # Combine all parts into the final message
180
- header = "#{time_prefix}#{level_prefix} #{category_prefix}"
211
+ header = "#{time_prefix}#{level_prefix} #{category_prefix}#{context_prefix}"
181
212
  trace = backtrace + objs
182
213
  io = StringIO.new
183
214
  formatted_message = if single_line
@@ -197,21 +228,16 @@ def ww0(*objs,
197
228
  output.flush
198
229
 
199
230
  # Optionally log to a file
200
- return objs.size == 1 ? objs.first : objs unless log_file
201
-
202
- File.open(log_file, 'a') do |file|
203
- file.puts(formatted_message)
231
+ if log_file
232
+ File.open(log_file, 'a') do |file|
233
+ file.puts(formatted_message)
234
+ end
204
235
  end
205
236
 
206
- # return the last item in the list, as the label is usually first
237
+ # Always return the last item in the list, as the label is usually first
207
238
  objs.last
208
239
  end
209
240
 
210
- # break into the debugger if enabled
211
- def wwb
212
- binding.irb if $debug
213
- end
214
-
215
241
  class Array
216
242
  unless defined?(deref)
217
243
 
@@ -430,3 +456,455 @@ class Array
430
456
  []
431
457
  end
432
458
  end
459
+
460
+ return if $PROGRAM_NAME != __FILE__
461
+
462
+ require 'minitest/autorun'
463
+
464
+ class TestWwFunction < Minitest::Test
465
+ def setup
466
+ # Save original global state
467
+ @original_debug = $debug
468
+ @original_ww_log_file = $ww_log_file
469
+ @original_ww_output = $ww_output
470
+ @original_ww_category = $ww_category
471
+
472
+ # Redirect output to capture it - ensure it's writable
473
+ @output_buffer = StringIO.new
474
+ @output_buffer.sync = true
475
+ $ww_output = @output_buffer
476
+ $ww_category = nil
477
+ end
478
+
479
+ def teardown
480
+ # Restore original global state
481
+ $debug = @original_debug
482
+ $ww_log_file = @original_ww_log_file
483
+ $ww_output = @original_ww_output
484
+ $ww_category = @original_ww_category
485
+
486
+ # Clean up any test log files
487
+ Dir.glob('test*.log').each { |f| FileUtils.rm_f(f) }
488
+ end
489
+
490
+ # Core functionality tests
491
+ def test_ww_returns_last_item_with_debug_disabled
492
+ $debug = false
493
+ $ww_log_file = nil
494
+
495
+ # Test with single item
496
+ result = ww('single_item')
497
+ assert_equal 'single_item', result,
498
+ 'ww should return the single item when debug is disabled'
499
+
500
+ # Test with multiple items
501
+ result = ww('first', 'second', 'third')
502
+ assert_equal 'third', result,
503
+ 'ww should return the last item when debug is disabled'
504
+
505
+ # Test with various data types
506
+ result = ww(1, 'string', :symbol, [1, 2, 3])
507
+ assert_equal [1, 2, 3], result,
508
+ 'ww should return the last item regardless of type'
509
+
510
+ # Verify no output when debug is disabled
511
+ assert_empty @output_buffer.string,
512
+ 'No output should be generated when debug is disabled'
513
+ end
514
+
515
+ def test_ww_returns_last_item_with_debug_enabled_no_log_file
516
+ $debug = true
517
+ $ww_log_file = nil
518
+
519
+ # Test with single item
520
+ result = ww('single_item')
521
+ assert_equal 'single_item', result,
522
+ 'ww should return the single item when debug is enabled and no log file'
523
+
524
+ # Test with multiple items
525
+ result = ww('first', 'second', 'third')
526
+ assert_equal 'third', result,
527
+ 'ww should return the last item when debug is enabled and no log file'
528
+
529
+ # Test with mixed types
530
+ result = ww(42, 'hello', :world, { key: 'value' })
531
+ assert_equal({ key: 'value' }, result,
532
+ 'ww should return the last item even with hash')
533
+
534
+ # Verify output is generated when debug is enabled
535
+ refute_empty @output_buffer.string,
536
+ 'Output should be generated when debug is enabled'
537
+ end
538
+
539
+ def test_ww_returns_last_item_with_debug_enabled_with_log_file
540
+ $debug = true
541
+ $ww_log_file = 'test_ww.log'
542
+
543
+ begin
544
+ # Test with single item
545
+ result = ww('single_item')
546
+ assert_equal 'single_item', result,
547
+ 'ww should return the single item when debug is enabled with log file'
548
+
549
+ # Test with multiple items
550
+ result = ww('first', 'second', 'third')
551
+ assert_equal 'third', result,
552
+ 'ww should return the last item when debug is enabled with log file'
553
+
554
+ # Test with various data types
555
+ result = ww(1, 'string', :symbol, [1, 2, 3])
556
+ assert_equal [1, 2, 3], result,
557
+ 'ww should return the last item regardless of type with log file'
558
+
559
+ # Verify log file was created and contains content
560
+ assert File.exist?('test_ww.log'), 'Log file should be created'
561
+ refute_empty File.read('test_ww.log'), 'Log file should contain content'
562
+ ensure
563
+ # Clean up test log file
564
+ FileUtils.rm_f('test_ww.log')
565
+ end
566
+ end
567
+
568
+ def test_ww_with_named_options
569
+ $debug = false
570
+
571
+ # Test that named options don't affect the return value
572
+ result = ww('first', 'second', category: 'test', level: :info)
573
+ assert_equal 'second', result,
574
+ 'ww should return the last item even with named options'
575
+
576
+ # Test with single item and options
577
+ result = ww('only_item', timestamp: true, single_line: true)
578
+ assert_equal 'only_item', result,
579
+ 'ww should return the single item even with named options'
580
+
581
+ # Test with various option combinations
582
+ result = ww('a', 'b', 'c', category: 'testing', level: :warn,
583
+ timestamp: true, single_line: false)
584
+ assert_equal 'c', result,
585
+ 'ww should return the last item with complex options'
586
+ end
587
+
588
+ # Test all ww function variants
589
+ def test_wwr_function_returns_last_item
590
+ $debug = true
591
+ $ww_log_file = nil
592
+
593
+ # Test wwr with multiple items
594
+ result = wwr('first', 'second', 'third')
595
+ assert_equal 'third', result, 'wwr should return the last item'
596
+
597
+ # Test wwr with single item
598
+ result = wwr('only_item')
599
+ assert_equal 'only_item', result, 'wwr should return the single item'
600
+
601
+ # Test wwr with debug disabled
602
+ $debug = false
603
+ result = wwr('a', 'b', 'c')
604
+ assert_equal 'c', result,
605
+ 'wwr should return the last item even when debug is disabled'
606
+ end
607
+
608
+ def test_wwp_function_returns_last_item
609
+ $debug = true
610
+ $ww_log_file = nil
611
+
612
+ # Test wwp with multiple items
613
+ result = wwp('first', 'second', 'third')
614
+ assert_equal 'third', result, 'wwp should return the last item'
615
+
616
+ # Test wwp with single item
617
+ result = wwp('only_item')
618
+ assert_equal 'only_item', result, 'wwp should return the single item'
619
+
620
+ # Test wwp with debug disabled
621
+ $debug = false
622
+ result = wwp('a', 'b', 'c')
623
+ assert_equal 'c', result,
624
+ 'wwp should return the last item even when debug is disabled'
625
+ end
626
+
627
+ def test_wwt_function_returns_last_item
628
+ $debug = true
629
+ $ww_log_file = nil
630
+
631
+ # Test wwt with multiple items (first item is tag)
632
+ result = wwt(:test_tag, 'first', 'second', 'third')
633
+ assert_equal 'third', result, 'wwt should return the last item'
634
+
635
+ # Test wwt with single data item after tag
636
+ result = wwt(:tag, 'only_data_item')
637
+ assert_equal 'only_data_item', result,
638
+ 'wwt should return the single data item'
639
+
640
+ # Test wwt with skipped tags
641
+ result = wwt(:blocks, 'data')
642
+ assert_equal 'data', result, 'wwt should return data even for skipped tags'
643
+
644
+ # Test wwt with debug disabled
645
+ $debug = false
646
+ result = wwt(:any_tag, 'a', 'b', 'c')
647
+ assert_equal 'c', result,
648
+ 'wwt should return the last item even when debug is disabled'
649
+ end
650
+
651
+ def test_wwt_multiline_continuation
652
+ $debug = true
653
+ $ww_log_file = nil
654
+
655
+ # Test multiline continuation pattern
656
+ var1 = 'test_value'
657
+ result = wwt :tag1, \
658
+ var1
659
+ assert_equal 'test_value', result,
660
+ 'wwt should work with multiline continuation'
661
+
662
+ # Test with complex expression
663
+ complex_var = [1, 2, 3]
664
+ result = wwt(:processing, \
665
+ complex_var.map { |x| x * 2 })
666
+ assert_equal [2, 4, 6], result,
667
+ 'wwt should work with complex expressions in multiline'
668
+
669
+ # Test with debug disabled
670
+ $debug = false
671
+ simple_var = 'no_debug'
672
+ result = wwt :disabled, \
673
+ simple_var
674
+ assert_equal 'no_debug', result,
675
+ 'wwt should return correct value even with debug disabled'
676
+ end
677
+
678
+ def test_ww_multiline_continuation
679
+ $debug = true
680
+ $ww_log_file = nil
681
+
682
+ # Test ww with multiline continuation
683
+ var1 = 'test_value'
684
+ result = ww \
685
+ var1
686
+ assert_equal 'test_value', result,
687
+ 'ww should work with multiline continuation'
688
+
689
+ # Test with multiple items in multiline
690
+ result = ww 'prefix', \
691
+ 'value'
692
+ assert_equal 'value', result,
693
+ 'ww should work with multiple items in multiline'
694
+
695
+ # Test with debug disabled
696
+ $debug = false
697
+ result = ww \
698
+ 'no_debug_value'
699
+ assert_equal 'no_debug_value', result,
700
+ 'ww should return correct value with debug disabled'
701
+ end
702
+
703
+ def test_wwa_function_behavior
704
+ $debug = true
705
+
706
+ # Test that wwa exits (we can't easily test exit behavior in minitest)
707
+ # So we'll just verify it would call ww0 properly by testing the structure
708
+ # Note: wwa calls exit, so we can't test it directly without special handling
709
+ skip 'wwa exits the program, cannot test directly in minitest'
710
+ end
711
+
712
+ def test_wwe_function_behavior
713
+ $debug = true
714
+
715
+ # Test that wwe raises an error
716
+ assert_raises(StandardError) do
717
+ wwe('error message')
718
+ end
719
+
720
+ # Test that wwe raises with the first object as the error message
721
+ error = assert_raises(StandardError) do
722
+ wwe('custom error', 'additional', 'data')
723
+ end
724
+ assert_equal 'custom error', error.message
725
+ end
726
+
727
+ # Edge case tests
728
+ def test_edge_cases
729
+ $debug = false
730
+
731
+ # Test with nil values
732
+ result = ww(nil, 'not_nil')
733
+ assert_equal 'not_nil', result, 'ww should handle nil values correctly'
734
+
735
+ # Test with empty array as last item
736
+ result = ww('first', [])
737
+ assert_equal [], result,
738
+ 'ww should return empty array if it is the last item'
739
+
740
+ # Test with false as last item
741
+ result = ww('first', false)
742
+ assert_equal false, result, 'ww should return false if it is the last item'
743
+
744
+ # Test with zero as last item
745
+ result = ww('first', 0)
746
+ assert_equal 0, result, 'ww should return zero if it is the last item'
747
+
748
+ # Test with empty string as last item
749
+ result = ww('first', '')
750
+ assert_equal '', result,
751
+ 'ww should return empty string if it is the last item'
752
+ end
753
+
754
+ def test_complex_data_structures
755
+ $debug = false
756
+
757
+ # Test with nested arrays
758
+ nested = [1, [2, [3, 4]], 5]
759
+ result = ww('start', nested)
760
+ assert_equal nested, result, 'ww should handle nested arrays'
761
+
762
+ # Test with complex hash
763
+ complex_hash = {
764
+ users: [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }],
765
+ metadata: { created: Time.now, version: '1.0' }
766
+ }
767
+ result = ww('prefix', complex_hash)
768
+ assert_equal complex_hash, result, 'ww should handle complex hashes'
769
+
770
+ # Test with objects
771
+ string_obj = String.new('test')
772
+ result = ww('object', string_obj)
773
+ assert_equal string_obj, result, 'ww should handle object instances'
774
+ end
775
+
776
+ def test_options_validation
777
+ $debug = true
778
+ $ww_log_file = nil
779
+
780
+ # Test valid log levels
781
+ %i[debug info warn error fatal].each do |level|
782
+ result = ww('test', level: level)
783
+ assert_equal 'test', result, "ww should work with log level #{level}"
784
+ end
785
+
786
+ # Test invalid log level raises error
787
+ assert_raises(ArgumentError) do
788
+ ww('test', level: :invalid)
789
+ end
790
+ end
791
+
792
+ def test_output_formatting_options
793
+ $debug = true
794
+ $ww_log_file = nil
795
+
796
+ # Create fresh buffer for this test
797
+ fresh_buffer = StringIO.new
798
+ $ww_output = fresh_buffer
799
+
800
+ # Test single_line option
801
+ ww('test', 'data', single_line: true)
802
+ output = fresh_buffer.string
803
+ refute_includes output, "\n [",
804
+ 'single_line should format output on one line'
805
+
806
+ # Reset buffer
807
+ fresh_buffer = StringIO.new
808
+ $ww_output = fresh_buffer
809
+
810
+ # Test timestamp option
811
+ ww('test', timestamp: true)
812
+ output = fresh_buffer.string
813
+ assert_match(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]/, output,
814
+ 'timestamp should be included in output')
815
+
816
+ # Reset buffer
817
+ fresh_buffer = StringIO.new
818
+ $ww_output = fresh_buffer
819
+
820
+ # Test category option
821
+ ww('test', category: 'MYCATEGORY')
822
+ output = fresh_buffer.string
823
+ assert_includes output, '[MYCATEGORY]',
824
+ 'category should be included in output'
825
+ end
826
+
827
+ def test_location_information
828
+ $debug = true
829
+ $ww_log_file = nil
830
+
831
+ # Create fresh buffer for this test
832
+ fresh_buffer = StringIO.new
833
+ $ww_output = fresh_buffer
834
+
835
+ # Test that location information is included
836
+ ww('location test')
837
+ output = fresh_buffer.string
838
+
839
+ # Should include file path and line number information
840
+ assert_includes output, 'lib/ww.rb', 'output should include filename'
841
+ assert_match(/\s:\s\d+\s:/, output,
842
+ 'output should include line number with format')
843
+ end
844
+
845
+ def test_full_backtrace_option
846
+ $debug = true
847
+ $ww_log_file = nil
848
+
849
+ # Create fresh buffer for this test
850
+ fresh_buffer = StringIO.new
851
+ $ww_output = fresh_buffer
852
+
853
+ # Test full_backtrace option
854
+ ww('test', full_backtrace: true)
855
+ output = fresh_buffer.string
856
+
857
+ # With full backtrace, we should see multiple stack levels
858
+ # (This is hard to test precisely since it depends on call stack depth)
859
+ refute_empty output, 'full_backtrace should produce output'
860
+ end
861
+
862
+ def test_multiple_consecutive_calls
863
+ $debug = false
864
+
865
+ # Test that multiple calls work correctly
866
+ results = []
867
+ results << ww('call1', 'result1')
868
+ results << ww('call2a', 'call2b', 'result2')
869
+ results << ww('call3a', 'call3b', 'call3c', 'result3')
870
+
871
+ assert_equal %w[result1 result2 result3], results,
872
+ 'Multiple consecutive calls should work correctly'
873
+ end
874
+
875
+ def test_return_value_consistency_across_debug_states
876
+ # Test the same call with debug enabled and disabled
877
+ test_args = %w[first second third]
878
+
879
+ # With debug disabled
880
+ $debug = false
881
+ $ww_log_file = nil
882
+ result_no_debug = ww(*test_args)
883
+
884
+ # With debug enabled, no log file
885
+ $debug = true
886
+ $ww_log_file = nil
887
+ result_debug_no_log = ww(*test_args)
888
+
889
+ # With debug enabled, with log file
890
+ $debug = true
891
+ $ww_log_file = 'consistency_test.log'
892
+ begin
893
+ result_debug_with_log = ww(*test_args)
894
+ ensure
895
+ FileUtils.rm_f('consistency_test.log')
896
+ end
897
+
898
+ # All should return the same value
899
+ assert_equal 'third', result_no_debug,
900
+ 'Should return last item with debug disabled'
901
+ assert_equal 'third', result_debug_no_log,
902
+ 'Should return last item with debug enabled, no log'
903
+ assert_equal 'third', result_debug_with_log,
904
+ 'Should return last item with debug enabled, with log'
905
+ assert_equal result_no_debug, result_debug_no_log,
906
+ 'Results should be consistent across debug states'
907
+ assert_equal result_no_debug, result_debug_with_log,
908
+ 'Results should be consistent across log file states'
909
+ end
910
+ end