1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <gfx/gfx.h> 6#include <fbl/unique_ptr.h> 7#include <sys/param.h> 8#include <unittest/unittest.h> 9 10#include "textcon.h" 11#include "vc.h" 12 13// This is needed to satisfy a reference from vc_handle_device_control_keys 14// in vc-input.cpp but the code path to that reference is dead in this test. 15void vc_toggle_framebuffer() { 16 __builtin_trap(); 17} 18 19bool g_vc_owns_display = true; 20 21namespace { 22 23void invalidate_callback(void* cookie, int x, int y, int w, int h) { 24} 25 26void movecursor_callback(void* cookie, int x, int y) { 27} 28 29void push_scrollback_line_callback(void* cookie, int y) { 30} 31 32void copy_lines_callback(void* cookie, int y_dest, int y_src, int line_count) { 33 auto* tc = reinterpret_cast<textcon_t*>(cookie); 34 tc_copy_lines(tc, y_dest, y_src, line_count); 35} 36 37void setparam_callback(void* cookie, int param, uint8_t* arg, size_t arglen) { 38} 39 40// Helper for initializing and testing console instances. This actually 41// creates two console instances: 42// 43// * A textcon_t (non-graphical), for testing character-level output. 44// * A vc_t (graphical), for testing incremental updates to the 45// gfx_surface. 46// 47// In principle, we could test the character-level output via the textcon_t 48// that the vc_t creates internally. However, using our own 49// separate textcon_t instance helps check that textcon_t can be used on 50// its own, outside of vc_t. 51class TextconHelper { 52public: 53 TextconHelper(uint32_t size_x, uint32_t size_y) : size_x(size_x), 54 size_y(size_y) { 55 // Create a textcon_t. 56 textbuf = new vc_char_t[size_x * size_y]; 57 textcon.cookie = &textcon; 58 textcon.invalidate = invalidate_callback; 59 textcon.movecursor = movecursor_callback; 60 textcon.push_scrollback_line = push_scrollback_line_callback; 61 textcon.copy_lines = copy_lines_callback; 62 textcon.setparam = setparam_callback; 63 tc_init(&textcon, size_x, size_y, textbuf, 0, 0, 0, 0); 64 // Initialize buffer contents, since this is currently done 65 // outside of textcon.cpp in vc-device.cpp. 66 for (size_t i = 0; i < size_x * size_y; ++i) 67 textbuf[i] = ' '; 68 69 // Create a vc_t with the same size in characters. 70 const gfx_font* font = vc_get_font(); 71 int pixels_x = font->width * size_x; 72 int pixels_y = font->height * (size_y + 1); // Add 1 for status line. 73 // Add margins that aren't large enough to fit a whole column or 74 // row at the right and bottom. This tests incremental update of 75 // anything that might be displayed in the margins. 76 pixels_x += font->width - 1; 77 pixels_y += font->height - 1; 78 vc_surface = gfx_create_surface( 79 nullptr, pixels_x, pixels_y, /* stride= */ pixels_x, 80 ZX_PIXEL_FORMAT_RGB_565, 0); 81 EXPECT_TRUE(vc_surface, ""); 82 // This takes ownership of vc_surface. 83 EXPECT_EQ(vc_init_gfx(vc_surface), ZX_OK, ""); 84 EXPECT_EQ(vc_alloc(&vc_dev, false), ZX_OK, ""); 85 EXPECT_EQ(vc_dev->columns, size_x, ""); 86 EXPECT_EQ(vc_rows(vc_dev), static_cast<int>(size_y), ""); 87 // Mark the console as active so that display updates get 88 // propagated to vc_surface. 89 vc_dev->active = true; 90 // Propagate the initial display contents to vc_surface. 91 vc_full_repaint(vc_dev); 92 vc_gfx_invalidate_all(vc_dev); 93 } 94 95 ~TextconHelper() { 96 delete[] textbuf; 97 vc_free(vc_dev); 98 } 99 100 // Takes a snapshot of the vc_t's display. 101 class DisplaySnapshot { 102 public: 103 DisplaySnapshot(TextconHelper* helper) 104 : helper_(helper), 105 snapshot_(new uint8_t[helper->vc_surface->len]) { 106 memcpy(snapshot_.get(), helper->vc_surface->ptr, 107 helper->vc_surface->len); 108 } 109 110 // Returns whether the vc_t's display changed since the 111 // snapshot was taken. 112 bool ChangedSinceSnapshot() { 113 return memcmp(snapshot_.get(), helper_->vc_surface->ptr, 114 helper_->vc_surface->len) != 0; 115 } 116 117 fbl::unique_ptr<char[]> ComparisonString() { 118 vc_t* vc_dev = helper_->vc_dev; 119 gfx_surface* vc_surface = helper_->vc_surface; 120 // Add 1 to these sizes to account for the margins. 121 uint32_t cmp_size_x = vc_dev->columns + 1; 122 uint32_t cmp_size_y = vc_dev->rows + 1; 123 uint32_t size_in_chars = cmp_size_x * cmp_size_y; 124 125 fbl::unique_ptr<bool[]> diffs(new bool[size_in_chars]); 126 for (uint32_t i = 0; i < size_in_chars; ++i) 127 diffs[i] = false; 128 129 for (uint32_t i = 0; i < vc_surface->len; ++i) { 130 if (static_cast<uint8_t*>(vc_surface->ptr)[i] != snapshot_[i]) { 131 uint32_t pixel_index = i / vc_surface->pixelsize; 132 uint32_t x_pixels = pixel_index % vc_surface->stride; 133 uint32_t y_pixels = pixel_index / vc_surface->stride; 134 uint32_t x_chars = x_pixels / vc_dev->charw; 135 uint32_t y_chars = y_pixels / vc_dev->charh; 136 EXPECT_LT(x_chars, cmp_size_x, ""); 137 EXPECT_LT(y_chars, cmp_size_y, ""); 138 diffs[x_chars + y_chars * cmp_size_x] = true; 139 } 140 } 141 142 // Build a string showing the differences. If we had 143 // std::string or equivalent, we'd use that here. 144 size_t string_size = (cmp_size_x + 3) * cmp_size_y + 1; 145 fbl::unique_ptr<char[]> string(new char[string_size]); 146 char* ptr = string.get(); 147 for (uint32_t y = 0; y < cmp_size_y; ++y) { 148 *ptr++ = '|'; 149 for (uint32_t x = 0; x < cmp_size_x; ++x) { 150 bool diff = diffs[x + y * cmp_size_x]; 151 *ptr++ = diff ? 'D' : '-'; 152 } 153 *ptr++ = '|'; 154 *ptr++ = '\n'; 155 } 156 *ptr++ = 0; 157 EXPECT_EQ(ptr, string.get() + string_size, ""); 158 return string; 159 } 160 161 // Prints a representation of which characters in the vc_t's 162 // display changed since the snapshot was taken. 163 void PrintComparison() { 164 printf("%s", ComparisonString().get()); 165 } 166 167 private: 168 TextconHelper* helper_; 169 fbl::unique_ptr<uint8_t[]> snapshot_; 170 }; 171 172 void InvalidateAllGraphics() { 173 vc_full_repaint(vc_dev); 174 vc_gfx_invalidate_all(vc_dev); 175 } 176 177 void PutString(const char* str) { 178 for (const char* ptr = str; *ptr; ++ptr) 179 textcon.putc(&textcon, *ptr); 180 181 vc_write(vc_dev, str, strlen(str), 0); 182 // Test that the incremental update of the display was correct. We 183 // do that by refreshing the entire display, and checking that 184 // there was no change. 185 DisplaySnapshot copy(this); 186 InvalidateAllGraphics(); 187 if (copy.ChangedSinceSnapshot()) { 188 copy.PrintComparison(); 189 EXPECT_TRUE(false, "Display contents changed"); 190 } 191 } 192 193 void AssertTextbufLineContains(vc_char_t* buf, int line_num, 194 const char* str) { 195 size_t len = strlen(str); 196 EXPECT_LE(len, size_x, ""); 197 for (size_t i = 0; i < len; ++i) 198 EXPECT_EQ(str[i], vc_char_get_char(buf[size_x * line_num + i]), ""); 199 // The rest of the line should contain spaces. 200 for (size_t i = len; i < size_x; ++i) 201 EXPECT_EQ(' ', vc_char_get_char(buf[size_x * line_num + i]), ""); 202 } 203 204 void AssertLineContains(int line_num, const char* str) { 205 AssertTextbufLineContains(textbuf, line_num, str); 206 AssertTextbufLineContains(vc_dev->text_buf, line_num, str); 207 } 208 209 uint32_t size_x; 210 uint32_t size_y; 211 212 vc_char_t* textbuf; 213 textcon_t textcon = {}; 214 215 gfx_surface* vc_surface; 216 vc_t* vc_dev; 217}; 218 219bool test_simple() { 220 BEGIN_TEST; 221 222 TextconHelper tc(10, 5); 223 tc.PutString("Hello"); 224 tc.AssertLineContains(0, "Hello"); 225 tc.AssertLineContains(1, ""); 226 227 END_TEST; 228} 229 230// This tests the DisplaySnapshot test helper above. If we write directly 231// to vc_dev's text buffer without invalidating the display, the test 232// machinery should detect which characters in the display were not updated 233// properly. 234bool test_display_update_comparison() { 235 BEGIN_TEST; 236 237 TextconHelper tc(10, 3); 238 // Write some characters directly into the text buffer. 239 auto SetChar = [&](int x, int y, char ch) { 240 tc.vc_dev->text_buf[x + y * tc.size_x] = 241 vc_char_make(ch, tc.textcon.fg, tc.textcon.bg); 242 }; 243 SetChar(2, 1, 'x'); 244 SetChar(3, 1, 'y'); 245 SetChar(6, 1, 'z'); 246 247 // Check that these characters in the display are detected as not 248 // properly updated. 249 TextconHelper::DisplaySnapshot snapshot(&tc); 250 tc.InvalidateAllGraphics(); 251 EXPECT_TRUE(snapshot.ChangedSinceSnapshot(), ""); 252 const char *expected = 253 "|-----------|\n" // Console status line 254 "|-----------|\n" // Cursor at left was painted during tc init 255 "|--DD--D----|\n" // Chars set by SetChar() above 256 "|-----------|\n" 257 "|-----------|\n"; // Bottom margin 258 EXPECT_EQ(strcmp(snapshot.ComparisonString().get(), expected), 0, ""); 259 260 END_TEST; 261} 262 263bool test_wrapping() { 264 BEGIN_TEST; 265 266 TextconHelper tc(10, 5); 267 tc.PutString("Hello world! More text here."); 268 tc.AssertLineContains(0, "Hello worl"); 269 tc.AssertLineContains(1, "d! More te"); 270 tc.AssertLineContains(2, "xt here."); 271 272 END_TEST; 273} 274 275bool test_tabs() { 276 BEGIN_TEST; 277 278 TextconHelper tc(80, 40); 279 tc.PutString("\tA\n"); 280 tc.PutString(" \tB\n"); 281 tc.PutString(" \tC\n"); // 7 spaces 282 tc.PutString(" \tD\n"); // 8 spaces 283 tc.AssertLineContains(0, " A"); 284 tc.AssertLineContains(1, " B"); 285 tc.AssertLineContains(2, " C"); 286 tc.AssertLineContains(3, " D"); 287 288 END_TEST; 289} 290 291bool test_backspace_moves_cursor() { 292 BEGIN_TEST; 293 294 TextconHelper tc(10, 5); 295 tc.PutString("ABCDEF\b\b\b\bxy"); 296 // Backspace only moves the cursor and does not erase, so "EF" is left 297 // in place. 298 tc.AssertLineContains(0, "ABxyEF"); 299 300 END_TEST; 301} 302 303bool test_backspace_at_start_of_line() { 304 BEGIN_TEST; 305 306 TextconHelper tc(10, 5); 307 tc.PutString("Foo\n\bBar"); 308 // When the cursor is at the start of a line, backspace has no effect. 309 tc.AssertLineContains(0, "Foo"); 310 tc.AssertLineContains(1, "Bar"); 311 312 END_TEST; 313} 314 315bool test_scroll_up() { 316 BEGIN_TEST; 317 318 TextconHelper tc(10, 4); 319 tc.PutString("AAA\nBBB\nCCC\nDDD\n"); 320 tc.AssertLineContains(0, "BBB"); 321 tc.AssertLineContains(1, "CCC"); 322 tc.AssertLineContains(2, "DDD"); 323 tc.AssertLineContains(3, ""); 324 EXPECT_EQ(vc_get_scrollback_lines(tc.vc_dev), 1, ""); 325 326 END_TEST; 327} 328 329// Same as scroll_up(), but using ESC E (NEL) instead of "\n". 330bool test_scroll_up_nel() { 331 BEGIN_TEST; 332 333 TextconHelper tc(10, 4); 334 tc.PutString("AAA" "\x1b" "E" 335 "BBB" "\x1b" "E" 336 "CCC" "\x1b" "E" 337 "DDD" "\x1b" "E"); 338 tc.AssertLineContains(0, "BBB"); 339 tc.AssertLineContains(1, "CCC"); 340 tc.AssertLineContains(2, "DDD"); 341 tc.AssertLineContains(3, ""); 342 EXPECT_EQ(vc_get_scrollback_lines(tc.vc_dev), 1, ""); 343 344 END_TEST; 345} 346 347bool test_insert_lines() { 348 BEGIN_TEST; 349 350 TextconHelper tc(10, 5); 351 tc.PutString("AAA\nBBB\nCCC\nDDD\nEEE"); 352 tc.PutString("\x1b[2A"); // Move the cursor up 2 lines 353 tc.PutString("\x1b[2L"); // Insert 2 lines 354 tc.PutString("Z"); // Output char to show where the cursor ends up 355 tc.AssertLineContains(0, "AAA"); 356 tc.AssertLineContains(1, "BBB"); 357 tc.AssertLineContains(2, " Z"); 358 tc.AssertLineContains(3, ""); 359 tc.AssertLineContains(4, "CCC"); 360 EXPECT_EQ(vc_get_scrollback_lines(tc.vc_dev), 0, ""); 361 362 END_TEST; 363} 364 365bool test_delete_lines() { 366 BEGIN_TEST; 367 368 TextconHelper tc(10, 5); 369 tc.PutString("AAA\nBBB\nCCC\nDDD\nEEE"); 370 tc.PutString("\x1b[2A"); // Move the cursor up 2 lines 371 tc.PutString("\x1b[2M"); // Delete 2 lines 372 tc.PutString("Z"); // Output char to show where the cursor ends up 373 tc.AssertLineContains(0, "AAA"); 374 tc.AssertLineContains(1, "BBB"); 375 tc.AssertLineContains(2, "EEEZ"); 376 tc.AssertLineContains(3, ""); 377 tc.AssertLineContains(4, ""); 378 // TODO(mseaborn): We probably don't want to be adding the deleted 379 // lines to the scrollback in this case, because they are not from the 380 // top of the console. 381 EXPECT_EQ(vc_get_scrollback_lines(tc.vc_dev), 2, ""); 382 383 END_TEST; 384} 385 386// Test for a bug where this would cause an out-of-bounds array access. 387bool test_insert_lines_many() { 388 BEGIN_TEST; 389 390 TextconHelper tc(10, 5); 391 tc.PutString("AAA\nBBB"); 392 tc.PutString("\x1b[999L"); // Insert 999 lines 393 tc.PutString("Z"); // Output char to show where the cursor ends up 394 tc.AssertLineContains(0, "AAA"); 395 tc.AssertLineContains(1, " Z"); 396 397 END_TEST; 398} 399 400// Test for a bug where this would cause an out-of-bounds array access. 401bool test_delete_lines_many() { 402 BEGIN_TEST; 403 404 TextconHelper tc(10, 5); 405 tc.PutString("AAA\nBBB"); 406 tc.PutString("\x1b[999M"); // Delete 999 lines 407 tc.PutString("Z"); // Output char to show where the cursor ends up 408 tc.AssertLineContains(0, "AAA"); 409 tc.AssertLineContains(1, " Z"); 410 411 END_TEST; 412} 413 414 415// Check that passing a huge parameter via "insert lines" completes in a 416// reasonable amount of time. (We don't check the time here but we assume 417// that someone will notice if this takes a long time.) 418bool test_insert_lines_huge() { 419 BEGIN_TEST; 420 421 TextconHelper tc(10, 5); 422 tc.PutString("AAA\nBBB"); 423 tc.PutString("\x1b[2000000000L"); // Insert lines 424 tc.PutString("Z"); // Output char to show where the cursor ends up 425 tc.AssertLineContains(0, "AAA"); 426 tc.AssertLineContains(1, " Z"); 427 428 END_TEST; 429} 430 431// Check that passing a huge parameter via "delete lines" completes in a 432// reasonable amount of time. (We don't check the time here but we assume 433// that someone will notice if this takes a long time.) 434bool test_delete_lines_huge() { 435 BEGIN_TEST; 436 437 TextconHelper tc(10, 5); 438 tc.PutString("AAA\nBBB"); 439 tc.PutString("\x1b[200000000M"); // Delete lines 440 tc.PutString("Z"); // Output char to show where the cursor ends up 441 tc.AssertLineContains(0, "AAA"); 442 tc.AssertLineContains(1, " Z"); 443 444 END_TEST; 445} 446 447bool test_move_cursor_up_and_scroll() { 448 BEGIN_TEST; 449 450 TextconHelper tc(10, 4); 451 tc.PutString("AAA\nBBB\nCCC\nDDD"); 452 tc.PutString("\x1bM" "1"); // Move cursor up; print char 453 tc.PutString("\x1bM" "2"); // Move cursor up; print char 454 tc.PutString("\x1bM" "3"); // Move cursor up; print char 455 tc.PutString("\x1bM" "4"); // Move cursor up; print char 456 tc.AssertLineContains(0, " 4"); 457 tc.AssertLineContains(1, "AAA 3"); 458 tc.AssertLineContains(2, "BBB 2"); 459 tc.AssertLineContains(3, "CCC1"); 460 461 END_TEST; 462} 463 464bool test_move_cursor_down_and_scroll() { 465 BEGIN_TEST; 466 467 TextconHelper tc(10, 4); 468 tc.PutString("1" "\x1b" "D"); // Print char; move cursor down 469 tc.PutString("2" "\x1b" "D"); // Print char; move cursor down 470 tc.PutString("3" "\x1b" "D"); // Print char; move cursor down 471 tc.PutString("4" "\x1b" "D"); // Print char; move cursor down 472 tc.PutString("5"); 473 tc.AssertLineContains(0, " 2"); 474 tc.AssertLineContains(1, " 3"); 475 tc.AssertLineContains(2, " 4"); 476 tc.AssertLineContains(3, " 5"); 477 478 END_TEST; 479} 480 481bool test_cursor_hide_and_show() { 482 BEGIN_TEST; 483 484 TextconHelper tc(10, 4); 485 ASSERT_FALSE(tc.vc_dev->hide_cursor, ""); 486 tc.PutString("\x1b[?25l"); // Hide cursor 487 ASSERT_TRUE(tc.vc_dev->hide_cursor, ""); 488 tc.PutString("\x1b[?25h"); // Show cursor 489 ASSERT_FALSE(tc.vc_dev->hide_cursor, ""); 490 491 END_TEST; 492} 493 494// This tests for a bug: If the cursor was positioned over a character when 495// we scroll up, that character would get erased. 496bool test_cursor_scroll_bug() { 497 BEGIN_TEST; 498 499 TextconHelper tc(10, 3); 500 // Move the cursor to the bottom line. 501 tc.PutString("\n\n\n"); 502 // Scroll down when the cursor is over "C". 503 tc.PutString("ABCDE\b\b\b\n"); 504 505 END_TEST; 506} 507 508// Test for a bug where scrolling the console viewport by a large delta 509// (e.g. going from the top to the bottom) can crash due to out-of-bounds 510// memory accesses. 511bool test_scroll_viewport_by_large_delta() { 512 BEGIN_TEST; 513 514 TextconHelper tc(2, 2); 515 tc.PutString("\n"); 516 for (int lines = 1; lines < 100; ++lines) { 517 tc.PutString("\n"); 518 519 // Scroll up, to show older lines. 520 vc_scroll_viewport_top(tc.vc_dev); 521 EXPECT_EQ(tc.vc_dev->viewport_y, -lines, ""); 522 523 // Scroll down, to show newer lines. 524 vc_scroll_viewport_bottom(tc.vc_dev); 525 EXPECT_EQ(tc.vc_dev->viewport_y, 0, ""); 526 } 527 528 END_TEST; 529} 530 531// When the console is displaying only the main console region (and no 532// scrollback), the console should keep displaying that as new lines are 533// outputted. 534bool test_viewport_scrolling_follows_bottom() { 535 BEGIN_TEST; 536 537 TextconHelper tc(1, 1); 538 for (unsigned i = 0; i < tc.vc_dev->scrollback_rows_max * 2; ++i) { 539 EXPECT_EQ(tc.vc_dev->viewport_y, 0, ""); 540 tc.PutString("\n"); 541 } 542 543 END_TEST; 544} 545 546// When the console is displaying some of the scrollback buffer, then as 547// new lines are outputted, the console should scroll the viewpoint to keep 548// displaying the same point, unless we're at the top of the scrollback 549// buffer. 550bool test_viewport_scrolling_follows_scrollback() { 551 BEGIN_TEST; 552 553 TextconHelper tc(1, 1); 554 // Add 3 lines to the scrollback buffer. 555 tc.PutString("\n\n\n"); 556 vc_scroll_viewport(tc.vc_dev, -2); 557 558 EXPECT_EQ(tc.vc_dev->viewport_y, -2, ""); 559 int limit = tc.vc_dev->scrollback_rows_max; 560 for (int line = 3; line < limit * 2; ++line) { 561 // Output different strings on each line in order to test that the 562 // display is updated consistently when the console starts dropping 563 // lines from the scrollback region. 564 char str[3] = { static_cast<char>('0' + (line % 10)), '\n', '\0' }; 565 tc.PutString(str); 566 EXPECT_EQ(tc.vc_dev->viewport_y, -MIN(line, limit), ""); 567 } 568 569 END_TEST; 570} 571 572bool test_output_when_viewport_scrolled() { 573 BEGIN_TEST; 574 575 TextconHelper tc(10, 3); 576 // Line 1 will move into the scrollback region. 577 tc.PutString("1\n 2\n 3\n 4"); 578 EXPECT_EQ(tc.vc_dev->viewport_y, 0, ""); 579 vc_scroll_viewport_top(tc.vc_dev); 580 581 EXPECT_EQ(tc.vc_dev->viewport_y, -1, ""); 582 // Check redrawing consistency. 583 tc.PutString(""); 584 585 // Test that output updates the display correctly when the viewport is 586 // scrolled. Using two separate PutString() calls here was necessary 587 // for reproducing an incremental update bug. 588 tc.PutString("\x1b[1;1f"); // Move to top left 589 tc.PutString("Epilobium"); 590 tc.AssertLineContains(0, "Epilobium"); 591 tc.AssertLineContains(1, " 3"); 592 tc.AssertLineContains(2, " 4"); 593 594 // Test that erasing also updates the display correctly. This 595 // changes the console contents without moving the cursor. 596 tc.PutString("\b\b\b\b"); // Move cursor left 3 chars 597 tc.PutString("\x1b[1K"); // Erase to beginning of line 598 tc.AssertLineContains(0, " ium"); 599 tc.AssertLineContains(1, " 3"); 600 tc.AssertLineContains(2, " 4"); 601 602 END_TEST; 603} 604 605bool test_scrolling_when_viewport_scrolled() { 606 BEGIN_TEST; 607 608 TextconHelper tc(10, 3); 609 // Line 1 will move into the scrollback region. 610 tc.PutString("1\n 2\n 3\n 4"); 611 EXPECT_EQ(tc.vc_dev->viewport_y, 0, ""); 612 vc_scroll_viewport_top(tc.vc_dev); 613 EXPECT_EQ(tc.vc_dev->viewport_y, -1, ""); 614 // Check redrawing consistency. 615 tc.PutString(""); 616 617 // Test that the display is updated correctly when we scroll. 618 tc.PutString("\n5"); 619 tc.AssertLineContains(0, " 3"); 620 tc.AssertLineContains(1, " 4"); 621 tc.AssertLineContains(2, "5"); 622 623 END_TEST; 624} 625 626// Test that vc_get_scrollback_lines() gives the correct results. 627bool test_scrollback_lines_count() { 628 BEGIN_TEST; 629 630 TextconHelper tc(10, 3); 631 tc.PutString("\n\n"); 632 633 // Reduce the scrollback limit to make the test faster. 634 const int kLimit = 20; 635 EXPECT_LE(kLimit, tc.vc_dev->scrollback_rows_max, ""); 636 tc.vc_dev->scrollback_rows_max = kLimit; 637 638 for (int lines = 1; lines < kLimit * 4; ++lines) { 639 tc.PutString("\n"); 640 EXPECT_EQ(MIN(lines, kLimit), 641 vc_get_scrollback_lines(tc.vc_dev), ""); 642 } 643 644 END_TEST; 645} 646 647// Test that the scrollback lines have the correct contents. 648bool test_scrollback_lines_contents() { 649 BEGIN_TEST; 650 651 // Use a 1-row-high console, which simplifies this test. 652 TextconHelper tc(3, 1); 653 654 // Reduce the scrollback limit to make the test faster. 655 const int kLimit = 20; 656 EXPECT_LE(kLimit, tc.vc_dev->scrollback_rows_max, ""); 657 tc.vc_dev->scrollback_rows_max = kLimit; 658 659 vc_char_t test_val = 0; 660 for (int lines = 1; lines <= kLimit; ++lines) { 661 tc.vc_dev->text_buf[0] = test_val++; 662 tc.PutString("\n"); 663 664 EXPECT_EQ(lines, vc_get_scrollback_lines(tc.vc_dev), ""); 665 for (int i = 0; i < lines; ++i) 666 EXPECT_EQ(i, vc_get_scrollback_line_ptr(tc.vc_dev, i)[0], ""); 667 } 668 for (int lines = 0; lines < kLimit * 3; ++lines) { 669 tc.vc_dev->text_buf[0] = test_val++; 670 tc.PutString("\n"); 671 672 EXPECT_EQ(kLimit, vc_get_scrollback_lines(tc.vc_dev), ""); 673 for (int i = 0; i < kLimit; ++i) { 674 EXPECT_EQ(test_val + i - kLimit, 675 vc_get_scrollback_line_ptr(tc.vc_dev, i)[0], ""); 676 } 677 } 678 679 END_TEST; 680} 681 682BEGIN_TEST_CASE(gfxconsole_textbuf_tests) 683RUN_TEST(test_simple) 684RUN_TEST(test_display_update_comparison) 685RUN_TEST(test_wrapping) 686RUN_TEST(test_tabs) 687RUN_TEST(test_backspace_moves_cursor) 688RUN_TEST(test_backspace_at_start_of_line) 689RUN_TEST(test_scroll_up) 690RUN_TEST(test_scroll_up_nel) 691RUN_TEST(test_insert_lines) 692RUN_TEST(test_delete_lines) 693RUN_TEST(test_insert_lines_many) 694RUN_TEST(test_delete_lines_many) 695RUN_TEST(test_insert_lines_huge) 696RUN_TEST(test_delete_lines_huge) 697RUN_TEST(test_move_cursor_up_and_scroll) 698RUN_TEST(test_move_cursor_down_and_scroll) 699RUN_TEST(test_cursor_hide_and_show) 700RUN_TEST(test_cursor_scroll_bug) 701RUN_TEST(test_scroll_viewport_by_large_delta) 702RUN_TEST(test_viewport_scrolling_follows_bottom) 703RUN_TEST(test_viewport_scrolling_follows_scrollback) 704RUN_TEST(test_output_when_viewport_scrolled) 705RUN_TEST(test_scrolling_when_viewport_scrolled) 706RUN_TEST(test_scrollback_lines_count) 707RUN_TEST(test_scrollback_lines_contents) 708END_TEST_CASE(gfxconsole_textbuf_tests) 709 710} 711 712#ifndef BUILD_COMBINED_TESTS 713int main(int argc, char** argv) { 714 return unittest_run_all_tests(argc, argv) ? 0 : -1; 715} 716#endif 717