|
365 | 365 | end |
366 | 366 | end |
367 | 367 |
|
| 368 | + describe '.build_output_text' do |
| 369 | + it 'builds output text with metadata, table, and legend' do |
| 370 | + ruby_descriptions = { |
| 371 | + 'ruby-base' => 'ruby 3.3.0', |
| 372 | + 'ruby-yjit' => 'ruby 3.3.0 +YJIT' |
| 373 | + } |
| 374 | + table = [ |
| 375 | + ['bench', 'ruby-base (ms)', 'stddev (%)', 'ruby-yjit (ms)', 'stddev (%)'], |
| 376 | + ['fib', '100.0', '5.0', '50.0', '3.0'] |
| 377 | + ] |
| 378 | + format = ['%s', '%.1f', '%.1f', '%.1f', '%.1f'] |
| 379 | + bench_failures = {} |
| 380 | + base_name = 'ruby-base' |
| 381 | + other_names = ['ruby-yjit'] |
| 382 | + |
| 383 | + result = BenchmarkRunner.build_output_text( |
| 384 | + ruby_descriptions, table, format, bench_failures, base_name, other_names |
| 385 | + ) |
| 386 | + |
| 387 | + assert_includes result, 'ruby-base: ruby 3.3.0' |
| 388 | + assert_includes result, 'ruby-yjit: ruby 3.3.0 +YJIT' |
| 389 | + assert_includes result, 'Legend:' |
| 390 | + assert_includes result, '- ruby-yjit 1st itr: ratio of ruby-base/ruby-yjit time for the first benchmarking iteration.' |
| 391 | + assert_includes result, '- ruby-base/ruby-yjit: ratio of ruby-base/ruby-yjit time. Higher is better for ruby-yjit. Above 1 represents a speedup.' |
| 392 | + end |
| 393 | + |
| 394 | + it 'includes formatted table in output' do |
| 395 | + ruby_descriptions = { 'ruby' => 'ruby 3.3.0' } |
| 396 | + table = [ |
| 397 | + ['bench', 'ruby (ms)', 'stddev (%)'], |
| 398 | + ['fib', '100.0', '5.0'] |
| 399 | + ] |
| 400 | + format = ['%s', '%.1f', '%.1f'] |
| 401 | + bench_failures = {} |
| 402 | + base_name = 'ruby' |
| 403 | + other_names = [] |
| 404 | + |
| 405 | + result = BenchmarkRunner.build_output_text( |
| 406 | + ruby_descriptions, table, format, bench_failures, base_name, other_names |
| 407 | + ) |
| 408 | + |
| 409 | + # Should contain table headers |
| 410 | + assert_includes result, 'bench' |
| 411 | + assert_includes result, 'ruby (ms)' |
| 412 | + # Should contain table data |
| 413 | + assert_includes result, 'fib' |
| 414 | + assert_includes result, '100.0' |
| 415 | + end |
| 416 | + |
| 417 | + it 'omits legend when no other executables' do |
| 418 | + ruby_descriptions = { 'ruby' => 'ruby 3.3.0' } |
| 419 | + table = [['bench', 'ruby (ms)'], ['fib', '100.0']] |
| 420 | + format = ['%s', '%.1f'] |
| 421 | + bench_failures = {} |
| 422 | + base_name = 'ruby' |
| 423 | + other_names = [] |
| 424 | + |
| 425 | + result = BenchmarkRunner.build_output_text( |
| 426 | + ruby_descriptions, table, format, bench_failures, base_name, other_names |
| 427 | + ) |
| 428 | + |
| 429 | + refute_includes result, 'Legend:' |
| 430 | + end |
| 431 | + |
| 432 | + it 'handles multiple other executables in legend' do |
| 433 | + ruby_descriptions = { |
| 434 | + 'ruby' => 'ruby 3.3.0', |
| 435 | + 'ruby-yjit' => 'ruby 3.3.0 +YJIT', |
| 436 | + 'ruby-rjit' => 'ruby 3.3.0 +RJIT' |
| 437 | + } |
| 438 | + table = [['bench', 'ruby (ms)', 'ruby-yjit (ms)', 'ruby-rjit (ms)']] |
| 439 | + format = ['%s', '%.1f', '%.1f', '%.1f'] |
| 440 | + bench_failures = {} |
| 441 | + base_name = 'ruby' |
| 442 | + other_names = ['ruby-yjit', 'ruby-rjit'] |
| 443 | + |
| 444 | + result = BenchmarkRunner.build_output_text( |
| 445 | + ruby_descriptions, table, format, bench_failures, base_name, other_names |
| 446 | + ) |
| 447 | + |
| 448 | + assert_includes result, 'ruby-yjit 1st itr' |
| 449 | + assert_includes result, 'ruby-rjit 1st itr' |
| 450 | + assert_includes result, 'ruby/ruby-yjit' |
| 451 | + assert_includes result, 'ruby/ruby-rjit' |
| 452 | + end |
| 453 | + |
| 454 | + it 'includes benchmark failures in formatted output' do |
| 455 | + ruby_descriptions = { 'ruby' => 'ruby 3.3.0' } |
| 456 | + table = [['bench', 'ruby (ms)'], ['fib', '100.0']] |
| 457 | + format = ['%s', '%.1f'] |
| 458 | + bench_failures = { |
| 459 | + 'ruby' => { 'failed_bench' => 'error message' } |
| 460 | + } |
| 461 | + base_name = 'ruby' |
| 462 | + other_names = [] |
| 463 | + |
| 464 | + result = BenchmarkRunner.build_output_text( |
| 465 | + ruby_descriptions, table, format, bench_failures, base_name, other_names |
| 466 | + ) |
| 467 | + |
| 468 | + # TableFormatter handles displaying failures, just verify it's called |
| 469 | + assert_kind_of String, result |
| 470 | + assert result.length > 0 |
| 471 | + end |
| 472 | + |
| 473 | + it 'handles empty ruby_descriptions' do |
| 474 | + ruby_descriptions = {} |
| 475 | + table = [['bench']] |
| 476 | + format = ['%s'] |
| 477 | + bench_failures = {} |
| 478 | + base_name = 'ruby' |
| 479 | + other_names = [] |
| 480 | + |
| 481 | + result = BenchmarkRunner.build_output_text( |
| 482 | + ruby_descriptions, table, format, bench_failures, base_name, other_names |
| 483 | + ) |
| 484 | + |
| 485 | + assert_kind_of String, result |
| 486 | + assert result.start_with?("\n") # Should start with newline after empty descriptions |
| 487 | + end |
| 488 | + |
| 489 | + it 'preserves order of ruby_descriptions' do |
| 490 | + ruby_descriptions = { |
| 491 | + 'ruby-a' => 'version A', |
| 492 | + 'ruby-b' => 'version B', |
| 493 | + 'ruby-c' => 'version C' |
| 494 | + } |
| 495 | + table = [['bench']] |
| 496 | + format = ['%s'] |
| 497 | + bench_failures = {} |
| 498 | + base_name = 'ruby-a' |
| 499 | + other_names = [] |
| 500 | + |
| 501 | + result = BenchmarkRunner.build_output_text( |
| 502 | + ruby_descriptions, table, format, bench_failures, base_name, other_names |
| 503 | + ) |
| 504 | + |
| 505 | + lines = result.lines |
| 506 | + assert_includes lines[0], 'ruby-a: version A' |
| 507 | + assert_includes lines[1], 'ruby-b: version B' |
| 508 | + assert_includes lines[2], 'ruby-c: version C' |
| 509 | + end |
| 510 | + end |
| 511 | + |
368 | 512 | describe '.render_graph' do |
369 | 513 | it 'delegates to GraphRenderer and returns calculated png_path' do |
370 | 514 | Dir.mktmpdir do |dir| |
|
0 commit comments