1 /* 2 * ConsoleOutputWriterTests.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 package org.rstudio.core.client; 16 17 import java.util.List; 18 19 import org.rstudio.core.client.dom.DomUtils; 20 import org.rstudio.studio.client.workbench.prefs.model.UserPrefs; 21 22 import com.google.gwt.dom.client.Element; 23 import com.google.gwt.dom.client.SpanElement; 24 import com.google.gwt.dom.client.Text; 25 import com.google.gwt.junit.client.GWTTestCase; 26 27 import junit.framework.Assert; 28 29 public class ConsoleOutputWriterTests extends GWTTestCase 30 { 31 private final String nullClazz = null; 32 private final boolean notError = false; 33 private final boolean isError = true; 34 private final boolean checkLineCount = false; 35 private final boolean ignoreLineCount = true; 36 private final String myClass = "myClass"; 37 private final String myErrorClass = "myErrorClass"; 38 39 private final String newlineErrorSpan = "<span class=\"myErrorClass\">\n</span>"; 40 numberedLine(int i)41 private String numberedLine(int i) 42 { 43 return i + "\n"; 44 } 45 getInnerHTML(ConsoleOutputWriter output)46 private String getInnerHTML(ConsoleOutputWriter output) 47 { 48 SpanElement outerSpan = SpanElement.as(output.getElement().getFirstChildElement()); 49 return outerSpan.getInnerHTML(); 50 } 51 52 private static class FakePrefs implements VirtualConsole.Preferences 53 { 54 @Override truncateLongLinesInConsoleHistory()55 public int truncateLongLinesInConsoleHistory() 56 { 57 return truncateLines_; 58 } 59 60 @Override consoleAnsiMode()61 public String consoleAnsiMode() 62 { 63 return ansiMode_; 64 } 65 66 @Override screenReaderEnabled()67 public boolean screenReaderEnabled() 68 { 69 return screenReaderEnabled_; 70 } 71 72 @Override limitConsoleVisible()73 public boolean limitConsoleVisible() 74 { 75 return limitConsoleVisible_; 76 } 77 78 public boolean limitConsoleVisible_ = false; 79 public final int truncateLines_ = 1000; 80 public final String ansiMode_ = UserPrefs.ANSI_CONSOLE_MODE_ON; 81 public final boolean screenReaderEnabled_ = false; 82 } 83 84 private static class VCFactory implements VirtualConsoleFactory 85 { 86 @Override create(Element elem)87 public VirtualConsole create(Element elem) 88 { 89 return new VirtualConsole(elem, new FakePrefs()); 90 } 91 } 92 getCOW()93 private ConsoleOutputWriter getCOW() 94 { 95 return new ConsoleOutputWriter(new VCFactory(), null); 96 } 97 98 @Override getModuleName()99 public String getModuleName() 100 { 101 return "org.rstudio.studio.RStudioTests"; 102 } 103 testCreation()104 public void testCreation() 105 { 106 ConsoleOutputWriter output = getCOW(); 107 Assert.assertNotNull(output); 108 Assert.assertNotNull(output.getWidget()); 109 } 110 testSetGetMaxLines()111 public void testSetGetMaxLines() 112 { 113 ConsoleOutputWriter output = getCOW(); 114 Assert.assertEquals(-1, output.getMaxOutputLines()); 115 output.setMaxOutputLines(1000); 116 Assert.assertEquals(1000, output.getMaxOutputLines()); 117 } 118 testSimpleLineCount()119 public void testSimpleLineCount() 120 { 121 ConsoleOutputWriter output = getCOW(); 122 Assert.assertEquals(0, output.getCurrentLines()); 123 Assert.assertTrue(output.outputToConsole("Hello World", 124 nullClazz, notError, checkLineCount, false)); 125 Assert.assertEquals(0, output.getCurrentLines()); 126 Assert.assertTrue(output.outputToConsole(" more on same line", 127 nullClazz, notError, checkLineCount, false)); 128 Assert.assertEquals(0, output.getCurrentLines()); 129 Assert.assertTrue(output.outputToConsole("next line starts now\n", 130 nullClazz, notError, checkLineCount, false)); 131 Assert.assertEquals(1, output.getCurrentLines()); 132 Assert.assertEquals(1, DomUtils.countLines(output.getElement(), true)); 133 } 134 testTrimming()135 public void testTrimming() 136 { 137 // this test is rather bulky; it tests that lines get trimmed, but also 138 // some basic cross-checking of the output structure, behavior of 139 // trimOutput, and lack of a class attribute when no clazz was specified 140 ConsoleOutputWriter output = getCOW(); 141 final int maxLines = 25; 142 output.setMaxOutputLines(maxLines); 143 144 // write just below the maximum 145 for (int i = 0; i < maxLines; i++) 146 { 147 Assert.assertTrue(output.outputToConsole(numberedLine(i), 148 nullClazz, notError, checkLineCount, false)); 149 Assert.assertEquals(i + 1, output.getCurrentLines()); 150 } 151 152 // trim should be a no-op when below the limit 153 Assert.assertEquals(maxLines, output.getCurrentLines()); 154 Assert.assertFalse(output.trimExcess()); 155 Assert.assertEquals(maxLines, output.getCurrentLines()); 156 157 // go over the limit 158 Assert.assertFalse(output.outputToConsole(numberedLine(maxLines), 159 nullClazz, notError, checkLineCount, false)); 160 Assert.assertEquals(maxLines, output.getCurrentLines()); 161 162 // go over the limit again 163 Assert.assertFalse(output.outputToConsole(numberedLine(maxLines + 1), 164 nullClazz, notError, checkLineCount, false)); 165 Assert.assertEquals(maxLines, output.getCurrentLines()); 166 167 // verify DOM matches expectations; first two output lines (0 and 1) 168 // should have been removed; since clazz has been the same, all lines 169 // should be in a single child <span> under the initial <span> 170 Element parent = output.getElement(); 171 Assert.assertEquals(1, parent.getChildCount()); 172 SpanElement outerSpan = SpanElement.as(parent.getFirstChildElement()); 173 Assert.assertEquals(1, outerSpan.getChildCount()); 174 SpanElement span = SpanElement.as(outerSpan.getFirstChildElement()); 175 Assert.assertEquals(1, span.getChildCount()); 176 Text text = Text.as(span.getChild(0)); 177 178 StringBuilder expected = new StringBuilder(); 179 for (int i = 2; i <= maxLines + 1; i++) 180 { 181 expected.append(numberedLine(i)); 182 } 183 Assert.assertEquals(expected.toString(), text.getData()); 184 Assert.assertEquals(span.getClassName(), ""); 185 186 String expectedInnerHtml = "<span>" + expected.toString() + "</span>"; 187 Assert.assertEquals(expectedInnerHtml, getInnerHTML(output)); 188 Assert.assertEquals(maxLines, DomUtils.countLines(output.getElement(), true)); 189 } 190 testBulkAddThenTrim()191 public void testBulkAddThenTrim() 192 { 193 // bulk adding lets you add more lines than the maximum, but defer 194 // trimming until the end 195 ConsoleOutputWriter output = getCOW(); 196 final int maxLines = 50; 197 output.setMaxOutputLines(maxLines); 198 199 for (int i = 0; i < maxLines + 10; i++) 200 { 201 Assert.assertTrue(output.outputToConsole(numberedLine(i), 202 myClass, notError, ignoreLineCount, false)); 203 Assert.assertEquals(i + 1, output.getCurrentLines()); 204 } 205 206 Assert.assertEquals(maxLines + 10, output.getCurrentLines()); 207 Assert.assertTrue(output.trimExcess()); 208 Assert.assertEquals(maxLines, output.getCurrentLines()); 209 Assert.assertEquals(maxLines, DomUtils.countLines(output.getElement(), true)); 210 211 StringBuilder expected = new StringBuilder(); 212 expected.append("<span class=\"" + myClass + "\">"); 213 for (int i = 10; i < maxLines + 10; i++) 214 { 215 expected.append(numberedLine(i)); 216 } 217 expected.append("</span>"); 218 219 Assert.assertEquals(expected.toString(), getInnerHTML(output)); 220 } 221 testWriteSimpleError()222 public void testWriteSimpleError() 223 { 224 ConsoleOutputWriter output = getCOW(); 225 String errorMsg = "Oh no, an error!!"; 226 227 Assert.assertTrue(output.outputToConsole(errorMsg, 228 myErrorClass, isError, ignoreLineCount, false)); 229 230 Assert.assertEquals(0, output.getCurrentLines()); 231 232 String expected = "<span class=\"myErrorClass\">" + errorMsg + "</span>"; 233 Assert.assertEquals(expected, getInnerHTML(output)); 234 } 235 testWriteSimpleErrorWithNewline()236 public void testWriteSimpleErrorWithNewline() 237 { 238 ConsoleOutputWriter output = getCOW(); 239 String errorMsg = "Oh no, an error!!\n"; 240 241 Assert.assertTrue(output.outputToConsole(errorMsg, 242 myErrorClass, isError, ignoreLineCount, false)); 243 244 Assert.assertEquals(1, output.getCurrentLines()); 245 String expected = "<span class=\"myErrorClass\">" + errorMsg + "</span>"; 246 Assert.assertEquals(expected, getInnerHTML(output)); 247 } 248 249 //////////////////////////////////////////////////////////////////////////// 250 // Below here are a bunch of tests I had written in R and was checking by 251 // eyeball directly in RStudio. https://github.com/gtritchie/console_tests 252 // These test a variety of issues that came up in the product during 253 // development, with a mixture of Ansi colors, regular output, error output, 254 // and so on. 255 //////////////////////////////////////////////////////////////////////////// 256 test1()257 public void test1() 258 { 259 // output multiple errors with same style, ensure each goes into its own 260 // span and the final one is captured and available via getNewElements 261 ConsoleOutputWriter output = getCOW(); 262 263 output.outputToConsole("1\n", myErrorClass, isError, ignoreLineCount, false); 264 output.outputToConsole("2\n", myErrorClass, isError, ignoreLineCount, false); 265 output.outputToConsole("3\n", myErrorClass, isError, ignoreLineCount, false); 266 output.outputToConsole("4\n", myErrorClass, isError, ignoreLineCount, false); 267 268 String lastError = "Error in h() : An error! Oh No!"; 269 output.outputToConsole(lastError, myErrorClass, isError, ignoreLineCount, false); 270 271 String lastErrorSpan = "<span class=\"myErrorClass\">" + lastError + "</span>"; 272 String expected = 273 "<span class=\"myErrorClass\">1\n</span>" + 274 "<span class=\"myErrorClass\">2\n</span>" + 275 "<span class=\"myErrorClass\">3\n</span>" + 276 "<span class=\"myErrorClass\">4\n</span>" + 277 lastErrorSpan; 278 279 Assert.assertEquals(4, output.getCurrentLines()); 280 Assert.assertEquals(expected, getInnerHTML(output)); 281 282 List<Element> newElements = output.getNewElements(); 283 Assert.assertFalse(newElements.isEmpty()); 284 Assert.assertEquals(1, newElements.size()); 285 Assert.assertEquals(lastErrorSpan, newElements.get(0).getString()); 286 } 287 test2()288 public void test2() 289 { 290 // output multiple errors with varying styles via Ansi codes, ensure 291 // each goes into its own span with correct styles, and the final one, 292 // which also has multiple styles and spans three lines is 293 // captured and available via getNewElements. 294 ConsoleOutputWriter output = getCOW(); 295 296 output.outputToConsole("1\n", myErrorClass, isError, ignoreLineCount, false); 297 output.outputToConsole("2\n", myErrorClass, isError, ignoreLineCount, false); 298 output.outputToConsole("3\n", myErrorClass, isError, ignoreLineCount, false); 299 output.outputToConsole("4\n", myErrorClass, isError, ignoreLineCount, false); 300 301 String lastError1 = "Error in h2() : An error!\n"; 302 String lastError2 = "\033[31mOh No!\n\033[39m"; 303 String lastError3 = "\033[43m\033[31mWow!\033[39m\033[49m"; 304 305 output.outputToConsole(lastError1 + lastError2 + lastError3, 306 myErrorClass, isError, ignoreLineCount, false); 307 308 String lastError1Span = "<span class=\"myErrorClass\">" + 309 lastError1 + "</span>"; 310 String lastError2Span = "<span class=\"myErrorClass xtermColor1\">" + 311 "Oh No!" + "</span>"; 312 String lastError3Span = "<span class=\"myErrorClass xtermBgColor3 xtermColor1\">" + 313 "Wow!" + "</span>"; 314 315 String expected = 316 "<span class=\"myErrorClass\">1\n</span>" + 317 "<span class=\"myErrorClass\">2\n</span>" + 318 "<span class=\"myErrorClass\">3\n</span>" + 319 "<span class=\"myErrorClass\">4\n</span>" + 320 lastError1Span + lastError2Span + newlineErrorSpan + lastError3Span; 321 322 Assert.assertEquals(6, output.getCurrentLines()); 323 Assert.assertEquals(expected, getInnerHTML(output)); 324 325 List<Element> newElements = output.getNewElements(); 326 Assert.assertFalse(newElements.isEmpty()); 327 Assert.assertEquals(4, newElements.size()); 328 Assert.assertEquals(lastError1Span, newElements.get(0).getString()); 329 Assert.assertEquals(lastError2Span, newElements.get(1).getString()); 330 Assert.assertEquals(newlineErrorSpan, newElements.get(2).getString()); 331 Assert.assertEquals(lastError3Span, newElements.get(3).getString()); 332 } 333 test3()334 public void test3() 335 { 336 // output two regular strings, without a newline, and make sure 337 // they turn into a single span of text 338 ConsoleOutputWriter output = getCOW(); 339 340 output.outputToConsole("Hello", myClass, notError, ignoreLineCount, false); 341 output.outputToConsole("World", myClass, notError, ignoreLineCount, false); 342 343 Assert.assertEquals(0, output.getCurrentLines()); 344 345 Assert.assertEquals("<span class=\"myClass\">HelloWorld</span>", 346 getInnerHTML(output)); 347 } 348 test4()349 public void test4() 350 { 351 // output 4 regular strings followed by newlines; should end up in a 352 // single span 353 ConsoleOutputWriter output = getCOW(); 354 355 output.outputToConsole("One\n", myClass, notError, ignoreLineCount, false); 356 output.outputToConsole("Two\n", myClass, notError, ignoreLineCount, false); 357 output.outputToConsole("Three\n", myClass, notError, ignoreLineCount, false); 358 output.outputToConsole("Four\n", myClass, notError, ignoreLineCount, false); 359 360 Assert.assertEquals(4, output.getCurrentLines()); 361 362 Assert.assertEquals("<span class=\"myClass\">One\nTwo\nThree\nFour\n</span>", 363 getInnerHTML(output)); 364 365 } 366 test5()367 public void test5() 368 { 369 // output two error strings, then one regular string, then another error 370 // and make sure DOM ends up as expected 371 ConsoleOutputWriter output = getCOW(); 372 373 output.outputToConsole("1\n", myErrorClass, isError, ignoreLineCount, false); 374 output.outputToConsole("2\n", myErrorClass, isError, ignoreLineCount, false); 375 output.outputToConsole("Hello ", myClass, notError, ignoreLineCount, false); 376 output.outputToConsole("world\n", myClass, notError, ignoreLineCount, false); 377 output.outputToConsole("3\n", myErrorClass, isError, ignoreLineCount, false); 378 379 Assert.assertEquals(4, output.getCurrentLines()); 380 381 Assert.assertEquals("<span class=\"myErrorClass\">1\n</span>" + 382 "<span class=\"myErrorClass\">2\n</span>" + 383 "<span class=\"myClass\">Hello world\n</span>" + 384 "<span class=\"myErrorClass\">3\n</span>", 385 getInnerHTML(output)); 386 387 List<Element> newElements = output.getNewElements(); 388 Assert.assertEquals(1, newElements.size()); 389 Assert.assertEquals("<span class=\"myErrorClass\">3\n</span>", 390 newElements.get(0).getString()); 391 } 392 test6()393 public void test6() 394 { 395 // output 4 error messages without ansi codes; each should end up in its 396 // own span, and the final should be captured 397 ConsoleOutputWriter output = getCOW(); 398 399 output.outputToConsole("1\n", myErrorClass, isError, ignoreLineCount, false); 400 output.outputToConsole("2\n", myErrorClass, isError, ignoreLineCount, false); 401 output.outputToConsole("3\n", myErrorClass, isError, ignoreLineCount, false); 402 output.outputToConsole("4", myErrorClass, isError, ignoreLineCount, false); 403 404 Assert.assertEquals(3, output.getCurrentLines()); 405 406 Assert.assertEquals( 407 "<span class=\"myErrorClass\">1\n</span>" + 408 "<span class=\"myErrorClass\">2\n</span>" + 409 "<span class=\"myErrorClass\">3\n</span>" + 410 "<span class=\"myErrorClass\">4</span>", 411 getInnerHTML(output)); 412 413 List<Element> newElements = output.getNewElements(); 414 Assert.assertEquals(1, newElements.size()); 415 Assert.assertEquals("<span class=\"myErrorClass\">4</span>", 416 newElements.get(0).getString()); 417 } 418 test7()419 public void test7() 420 { 421 // output a single error message with ansi code in it 422 ConsoleOutputWriter output = getCOW(); 423 424 output.outputToConsole("Error in test7a() : \033[32mHi\033[39m", myErrorClass, 425 isError, ignoreLineCount, false); 426 427 Assert.assertEquals(0, output.getCurrentLines()); 428 429 Assert.assertEquals( 430 "<span class=\"myErrorClass\">Error in test7a() : </span>" + 431 "<span class=\"myErrorClass xtermColor2\">Hi</span>", 432 getInnerHTML(output)); 433 434 List<Element> newElements = output.getNewElements(); 435 Assert.assertEquals(2, newElements.size()); 436 Assert.assertEquals("<span class=\"myErrorClass\">Error in test7a() : </span>", 437 newElements.get(0).getString()); 438 Assert.assertEquals("<span class=\"myErrorClass xtermColor2\">Hi</span>", 439 newElements.get(1).getString()); 440 } 441 test8()442 public void test8() 443 { 444 // Write four error lines, then a complex multi-line error message with 445 // ansi codes. 446 ConsoleOutputWriter output = getCOW(); 447 448 output.outputToConsole("1\n", myErrorClass, isError, ignoreLineCount, false); 449 output.outputToConsole("2\n", myErrorClass, isError, ignoreLineCount, false); 450 output.outputToConsole("3\n", myErrorClass, isError, ignoreLineCount, false); 451 output.outputToConsole("4\n", myErrorClass, isError, ignoreLineCount, false); 452 output.outputToConsole( 453 "Error in test8b() : An error!\n" + 454 "\033[31mOh No!\n\033[39m" + 455 "\033[43m\033[31mA multiline error with colors.\n\033[39m\033[49m" + 456 "\033[7mAnd some inverse text.\n\033[27m" + 457 "\033[1m\033[3mThe Horror!!\033[23m\033[22m", 458 myErrorClass, isError, ignoreLineCount, false); 459 460 Assert.assertEquals(8, output.getCurrentLines()); 461 462 Assert.assertEquals( 463 "<span class=\"myErrorClass\">1\n</span>" + 464 "<span class=\"myErrorClass\">2\n</span>" + 465 "<span class=\"myErrorClass\">3\n</span>" + 466 "<span class=\"myErrorClass\">4\n</span>" + 467 "<span class=\"myErrorClass\">Error in test8b() : An error!\n</span>" + 468 "<span class=\"myErrorClass xtermColor1\">Oh No!</span>" + 469 newlineErrorSpan + 470 "<span class=\"myErrorClass xtermBgColor3 xtermColor1\">A multiline error with colors.</span>" + 471 newlineErrorSpan + 472 "<span class=\"myErrorClass xtermInvertColor xtermInvertBgColor\">And some inverse text.</span>" + 473 newlineErrorSpan + 474 "<span class=\"myErrorClass xtermBold xtermItalic\">The Horror!!</span>", 475 getInnerHTML(output)); 476 477 List<Element> newElements = output.getNewElements(); 478 Assert.assertEquals(8, newElements.size()); 479 480 Assert.assertEquals("<span class=\"myErrorClass\">Error in test8b() : An error!\n</span>", 481 newElements.get(0).getString()); 482 Assert.assertEquals("<span class=\"myErrorClass xtermColor1\">Oh No!</span>", 483 newElements.get(1).getString()); 484 Assert.assertEquals(newlineErrorSpan, newElements.get(2).getString()); 485 Assert.assertEquals("<span class=\"myErrorClass xtermBgColor3 xtermColor1\">A multiline error with colors.</span>" , 486 newElements.get(3).getString()); 487 Assert.assertEquals(newlineErrorSpan, newElements.get(4).getString()); 488 Assert.assertEquals("<span class=\"myErrorClass xtermInvertColor xtermInvertBgColor\">And some inverse text.</span>", 489 newElements.get(5).getString()); 490 Assert.assertEquals(newlineErrorSpan, newElements.get(6).getString()); 491 Assert.assertEquals("<span class=\"myErrorClass xtermBold xtermItalic\">The Horror!!</span>", 492 newElements.get(7).getString()); 493 } 494 test9()495 public void test9() 496 { 497 // write a single-line error followed by a multi-line error, no ansi codes 498 ConsoleOutputWriter output = getCOW(); 499 500 output.outputToConsole("Hello\n", myErrorClass, isError, ignoreLineCount, false); 501 output.outputToConsole("A\nB\nC", myErrorClass, isError, ignoreLineCount, false); 502 503 Assert.assertEquals(3, output.getCurrentLines()); 504 505 Assert.assertEquals( 506 "<span class=\"myErrorClass\">Hello\n</span>" + 507 "<span class=\"myErrorClass\">A\nB\nC</span>", 508 getInnerHTML(output)); 509 510 List<Element> newElements = output.getNewElements(); 511 Assert.assertEquals(1, newElements.size()); 512 Assert.assertEquals("<span class=\"myErrorClass\">A\nB\nC</span>", 513 newElements.get(0).getString()); 514 } 515 test10()516 public void test10() 517 { 518 // inline editing via \r without ansi codes 519 ConsoleOutputWriter output = getCOW(); 520 output.outputToConsole("\rfoobar", myClass, notError, ignoreLineCount, false); 521 output.outputToConsole("\rX foobar\n", myClass, notError, ignoreLineCount, false); 522 523 Assert.assertEquals(1, output.getCurrentLines()); 524 Assert.assertEquals( 525 "<span class=\"myClass\">X foobar\n</span>", 526 getInnerHTML(output)); 527 } 528 test11()529 public void test11() 530 { 531 // inline editing via \r with ansi codes 532 ConsoleOutputWriter output = getCOW(); 533 output.outputToConsole("\rfoobar", myClass, notError, ignoreLineCount, false); 534 output.outputToConsole("\r\033[32mX\033[39m \033[31mfoobar\033[39m\n", 535 myClass, notError, ignoreLineCount, false); 536 537 Assert.assertEquals(1, output.getCurrentLines()); 538 Assert.assertEquals( 539 "<span class=\"myClass xtermColor2\">X</span>" + 540 "<span class=\"myClass\"> </span>" + 541 "<span class=\"myClass xtermColor1\">foobar</span>" + 542 "<span class=\"myClass\">\n</span>", 543 getInnerHTML(output)); 544 } 545 test12()546 public void test12() 547 { 548 // inline editing via \r with ansi codes; multiple output lines, don't 549 // overwrite entire original output 550 ConsoleOutputWriter output = getCOW(); 551 output.outputToConsole("Hello\nWorld", myClass, notError, ignoreLineCount, false); 552 output.outputToConsole("\r\033[32mX\033[39m \033[31mY\033[39m\n", 553 myClass, notError, ignoreLineCount, false); 554 555 Assert.assertEquals(2, output.getCurrentLines()); 556 Assert.assertEquals( 557 "<span class=\"myClass\">Hello\n</span>" + 558 "<span class=\"myClass xtermColor2\">X</span>" + 559 "<span class=\"myClass\"> </span>" + 560 "<span class=\"myClass xtermColor1\">Y</span>" + 561 "<span class=\"myClass\">ld\n</span>", 562 getInnerHTML(output)); 563 } 564 test13()565 public void test13() 566 { 567 // inline editing via \r with ansi codes; multiple output lines, 568 // overwrite entire original output 569 ConsoleOutputWriter output = getCOW(); 570 output.outputToConsole("Hello\nWorld", myClass, notError, ignoreLineCount, false); 571 output.outputToConsole("\r\033[32m123\033[39m\033[31m45\033[39m\n", 572 myClass, notError, ignoreLineCount, false); 573 574 Assert.assertEquals(2, output.getCurrentLines()); 575 Assert.assertEquals( 576 "<span class=\"myClass\">Hello\n</span>" + 577 "<span class=\"myClass xtermColor2\">123</span>" + 578 "<span class=\"myClass\"></span>" + 579 "<span class=\"myClass xtermColor1\">45</span>" + 580 "<span class=\"myClass\">\n</span>", 581 getInnerHTML(output)); 582 } 583 test14()584 public void test14() 585 { 586 // output mixture of normal and error output, make sure DOM is correct 587 ConsoleOutputWriter output = getCOW(); 588 589 output.outputToConsole("Beginning\n", myClass, notError, ignoreLineCount, false); 590 output.outputToConsole("1\n", myErrorClass, isError, ignoreLineCount, false); 591 output.outputToConsole("2\n", myErrorClass, isError, ignoreLineCount, false); 592 output.outputToConsole("Hello ", myClass, notError, ignoreLineCount, false); 593 output.outputToConsole("world\n", myClass, notError, ignoreLineCount, false); 594 output.outputToConsole("3\n", myErrorClass, isError, ignoreLineCount, false); 595 output.outputToConsole("END", myClass, notError, ignoreLineCount, false); 596 597 Assert.assertEquals(5, output.getCurrentLines()); 598 599 Assert.assertEquals("<span class=\"myClass\">Beginning\n</span>" + 600 "<span class=\"myErrorClass\">1\n</span>" + 601 "<span class=\"myErrorClass\">2\n</span>" + 602 "<span class=\"myClass\">Hello world\n</span>" + 603 "<span class=\"myErrorClass\">3\n</span>" + 604 "<span class=\"myClass\">END</span>", 605 getInnerHTML(output)); 606 607 } 608 test15()609 public void test15() 610 { 611 // write multiple error lines followed by a multi-line error, no ansi codes 612 ConsoleOutputWriter output = getCOW(); 613 614 output.outputToConsole("1\n", myErrorClass, isError, ignoreLineCount, false); 615 output.outputToConsole("2\n", myErrorClass, isError, ignoreLineCount, false); 616 output.outputToConsole( 617 "Error in h15() : A multiline error without colors!\nOh No!", 618 myErrorClass, isError, ignoreLineCount, false); 619 620 Assert.assertEquals(3, output.getCurrentLines()); 621 622 Assert.assertEquals( 623 "<span class=\"myErrorClass\">1\n</span>" + 624 "<span class=\"myErrorClass\">2\n</span>" + 625 "<span class=\"myErrorClass\">Error in h15() : A multiline error " + 626 "without colors!\nOh No!</span>", 627 getInnerHTML(output)); 628 629 List<Element> newElements = output.getNewElements(); 630 Assert.assertEquals(1, newElements.size()); 631 Assert.assertEquals("<span class=\"myErrorClass\">Error in h15() : A " + 632 "multiline error without colors!\nOh No!</span>", 633 newElements.get(0).getString()); 634 } 635 test16()636 public void test16() 637 { 638 // write several lines of regular output 639 ConsoleOutputWriter output = getCOW(); 640 641 output.outputToConsole("one\n", myClass, notError, ignoreLineCount, false); 642 output.outputToConsole("two\n", myClass, notError, ignoreLineCount, false); 643 output.outputToConsole("three", myClass, notError, ignoreLineCount, false); 644 645 Assert.assertEquals(2, output.getCurrentLines()); 646 647 Assert.assertEquals( 648 "<span class=\"myClass\">one\ntwo\nthree</span>", 649 getInnerHTML(output)); 650 } 651 test17()652 public void test17() 653 { 654 // inline editing via \r with ansi codes; multiple output lines, don't 655 // overwrite entire original output 656 ConsoleOutputWriter output = getCOW(); 657 output.outputToConsole("✔ xxx \033[34myyy\033[39m xxx", myClass, notError, ignoreLineCount, false); 658 output.outputToConsole("\r", myClass, notError, ignoreLineCount, false); 659 output.outputToConsole("✔xxx \033[31myyy\033[39m zzz", myClass, notError, ignoreLineCount, false); 660 output.outputToConsole("\n", myClass, notError, ignoreLineCount, false); 661 output.outputToConsole("\n", myClass, notError, ignoreLineCount, false); 662 output.outputToConsole("✔ xxx \033[34myyy\033[39m xxx", myClass, notError, ignoreLineCount, false); 663 output.outputToConsole("\r", myClass, notError, ignoreLineCount, false); 664 output.outputToConsole("✔ xxx \033[31myyy\033[39m zzz", myClass, notError, ignoreLineCount, false); 665 output.outputToConsole("\n", myClass, notError, ignoreLineCount, false); 666 Assert.assertEquals(3, output.getCurrentLines()); 667 Assert.assertEquals( 668 "<span class=\"myClass\">✔xxx </span>" + 669 "<span class=\"myClass xtermColor1\"></span>" + // redundant 670 "<span class=\"myClass\"></span>" + // redundant 671 "<span class=\"myClass xtermColor1\">yyy</span>" + 672 "<span class=\"myClass xtermColor4\"></span>" + // redundant 673 "<span class=\"myClass\"></span>" + // redundant 674 "<span class=\"myClass\"> zzzx\n\n✔ xxx </span>" + 675 "<span class=\"myClass xtermColor4\"></span>" + // redundant 676 "<span class=\"myClass xtermColor1\">yyy</span>" + 677 "<span class=\"myClass\"> zzz\n</span>", 678 getInnerHTML(output)); 679 } 680 test18()681 public void test18() 682 { 683 // https://github.com/rstudio/rstudio/issues/7278 684 ConsoleOutputWriter output = getCOW(); 685 output.outputToConsole("B\033[31mx\033[39mA", myClass, notError, ignoreLineCount, false); 686 output.outputToConsole("\r ", myClass, notError, ignoreLineCount, false); 687 output.outputToConsole("\rMessage\n", myClass, notError, ignoreLineCount, false); 688 Assert.assertEquals(1, output.getCurrentLines()); 689 Assert.assertEquals("<span class=\"myClass\">Message\n</span>", getInnerHTML(output)); 690 } 691 } 692