1# Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================== 15"""Tests for Building Blocks of the TensorFlow Debugger CLI.""" 16import os 17import stat 18import tempfile 19 20import numpy as np 21 22from tensorflow.python.client import pywrap_tf_session 23from tensorflow.python.debug.cli import debugger_cli_common 24from tensorflow.python.framework import test_util 25from tensorflow.python.platform import gfile 26from tensorflow.python.platform import googletest 27 28 29class CommandLineExitTest(test_util.TensorFlowTestCase): 30 31 def testConstructionWithoutToken(self): 32 exit_exc = debugger_cli_common.CommandLineExit() 33 34 self.assertTrue(isinstance(exit_exc, Exception)) 35 36 def testConstructionWithToken(self): 37 exit_exc = debugger_cli_common.CommandLineExit(exit_token={"foo": "bar"}) 38 39 self.assertTrue(isinstance(exit_exc, Exception)) 40 self.assertEqual({"foo": "bar"}, exit_exc.exit_token) 41 42 43class RichTextLinesTest(test_util.TensorFlowTestCase): 44 45 def testRichTextLinesConstructorComplete(self): 46 # Test RichTextLines constructor. 47 screen_output = debugger_cli_common.RichTextLines( 48 ["Roses are red", "Violets are blue"], 49 font_attr_segs={0: [(0, 5, "red")], 50 1: [(0, 7, "blue")]}, 51 annotations={0: "longer wavelength", 52 1: "shorter wavelength"}) 53 54 self.assertEqual(2, len(screen_output.lines)) 55 self.assertEqual(2, len(screen_output.font_attr_segs)) 56 self.assertEqual(1, len(screen_output.font_attr_segs[0])) 57 self.assertEqual(1, len(screen_output.font_attr_segs[1])) 58 self.assertEqual(2, len(screen_output.annotations)) 59 60 self.assertEqual(2, screen_output.num_lines()) 61 62 def testRichTextLinesConstructorWithInvalidType(self): 63 with self.assertRaisesRegex(ValueError, "Unexpected type in lines"): 64 debugger_cli_common.RichTextLines(123) 65 66 def testRichTextLinesConstructorWithString(self): 67 # Test constructing a RichTextLines object with a string, instead of a list 68 # of strings. 69 screen_output = debugger_cli_common.RichTextLines( 70 "Roses are red", 71 font_attr_segs={0: [(0, 5, "red")]}, 72 annotations={0: "longer wavelength"}) 73 74 self.assertEqual(1, len(screen_output.lines)) 75 self.assertEqual(1, len(screen_output.font_attr_segs)) 76 self.assertEqual(1, len(screen_output.font_attr_segs[0])) 77 self.assertEqual(1, len(screen_output.annotations)) 78 79 def testRichLinesAppendRichLine(self): 80 rtl = debugger_cli_common.RichTextLines( 81 "Roses are red", 82 font_attr_segs={0: [(0, 5, "red")]}) 83 rtl.append_rich_line(debugger_cli_common.RichLine("Violets are ") + 84 debugger_cli_common.RichLine("blue", "blue")) 85 self.assertEqual(2, len(rtl.lines)) 86 self.assertEqual(2, len(rtl.font_attr_segs)) 87 self.assertEqual(1, len(rtl.font_attr_segs[0])) 88 self.assertEqual(1, len(rtl.font_attr_segs[1])) 89 90 def testRichLineLenMethodWorks(self): 91 self.assertEqual(0, len(debugger_cli_common.RichLine())) 92 self.assertEqual(0, len(debugger_cli_common.RichLine(""))) 93 self.assertEqual(1, len(debugger_cli_common.RichLine("x"))) 94 self.assertEqual(6, len(debugger_cli_common.RichLine("x y z ", "blue"))) 95 96 def testRichTextLinesConstructorIncomplete(self): 97 # Test RichTextLines constructor, with incomplete keyword arguments. 98 screen_output = debugger_cli_common.RichTextLines( 99 ["Roses are red", "Violets are blue"], 100 font_attr_segs={0: [(0, 5, "red")], 101 1: [(0, 7, "blue")]}) 102 103 self.assertEqual(2, len(screen_output.lines)) 104 self.assertEqual(2, len(screen_output.font_attr_segs)) 105 self.assertEqual(1, len(screen_output.font_attr_segs[0])) 106 self.assertEqual(1, len(screen_output.font_attr_segs[1])) 107 self.assertEqual({}, screen_output.annotations) 108 109 def testModifyRichTextLinesObject(self): 110 screen_output = debugger_cli_common.RichTextLines( 111 ["Roses are red", "Violets are blue"]) 112 113 self.assertEqual(2, len(screen_output.lines)) 114 115 screen_output.lines.append("Sugar is sweet") 116 self.assertEqual(3, len(screen_output.lines)) 117 118 def testMergeRichTextLines(self): 119 screen_output_1 = debugger_cli_common.RichTextLines( 120 ["Roses are red", "Violets are blue"], 121 font_attr_segs={0: [(0, 5, "red")], 122 1: [(0, 7, "blue")]}, 123 annotations={0: "longer wavelength", 124 1: "shorter wavelength"}) 125 screen_output_2 = debugger_cli_common.RichTextLines( 126 ["Lilies are white", "Sunflowers are yellow"], 127 font_attr_segs={0: [(0, 6, "white")], 128 1: [(0, 7, "yellow")]}, 129 annotations={ 130 "metadata": "foo", 131 0: "full spectrum", 132 1: "medium wavelength" 133 }) 134 135 screen_output_1.extend(screen_output_2) 136 137 self.assertEqual(4, screen_output_1.num_lines()) 138 self.assertEqual([ 139 "Roses are red", "Violets are blue", "Lilies are white", 140 "Sunflowers are yellow" 141 ], screen_output_1.lines) 142 self.assertEqual({ 143 0: [(0, 5, "red")], 144 1: [(0, 7, "blue")], 145 2: [(0, 6, "white")], 146 3: [(0, 7, "yellow")] 147 }, screen_output_1.font_attr_segs) 148 self.assertEqual({ 149 0: [(0, 5, "red")], 150 1: [(0, 7, "blue")], 151 2: [(0, 6, "white")], 152 3: [(0, 7, "yellow")] 153 }, screen_output_1.font_attr_segs) 154 self.assertEqual({ 155 "metadata": "foo", 156 0: "longer wavelength", 157 1: "shorter wavelength", 158 2: "full spectrum", 159 3: "medium wavelength" 160 }, screen_output_1.annotations) 161 162 def testMergeRichTextLinesEmptyOther(self): 163 screen_output_1 = debugger_cli_common.RichTextLines( 164 ["Roses are red", "Violets are blue"], 165 font_attr_segs={0: [(0, 5, "red")], 166 1: [(0, 7, "blue")]}, 167 annotations={0: "longer wavelength", 168 1: "shorter wavelength"}) 169 screen_output_2 = debugger_cli_common.RichTextLines([]) 170 171 screen_output_1.extend(screen_output_2) 172 173 self.assertEqual(2, screen_output_1.num_lines()) 174 self.assertEqual(["Roses are red", "Violets are blue"], 175 screen_output_1.lines) 176 self.assertEqual({ 177 0: [(0, 5, "red")], 178 1: [(0, 7, "blue")], 179 }, screen_output_1.font_attr_segs) 180 self.assertEqual({ 181 0: [(0, 5, "red")], 182 1: [(0, 7, "blue")], 183 }, screen_output_1.font_attr_segs) 184 self.assertEqual({ 185 0: "longer wavelength", 186 1: "shorter wavelength", 187 }, screen_output_1.annotations) 188 189 def testMergeRichTextLinesEmptySelf(self): 190 screen_output_1 = debugger_cli_common.RichTextLines([]) 191 screen_output_2 = debugger_cli_common.RichTextLines( 192 ["Roses are red", "Violets are blue"], 193 font_attr_segs={0: [(0, 5, "red")], 194 1: [(0, 7, "blue")]}, 195 annotations={0: "longer wavelength", 196 1: "shorter wavelength"}) 197 198 screen_output_1.extend(screen_output_2) 199 200 self.assertEqual(2, screen_output_1.num_lines()) 201 self.assertEqual(["Roses are red", "Violets are blue"], 202 screen_output_1.lines) 203 self.assertEqual({ 204 0: [(0, 5, "red")], 205 1: [(0, 7, "blue")], 206 }, screen_output_1.font_attr_segs) 207 self.assertEqual({ 208 0: [(0, 5, "red")], 209 1: [(0, 7, "blue")], 210 }, screen_output_1.font_attr_segs) 211 self.assertEqual({ 212 0: "longer wavelength", 213 1: "shorter wavelength", 214 }, screen_output_1.annotations) 215 216 def testAppendALineWithAttributeSegmentsWorks(self): 217 screen_output_1 = debugger_cli_common.RichTextLines( 218 ["Roses are red"], 219 font_attr_segs={0: [(0, 5, "red")]}, 220 annotations={0: "longer wavelength"}) 221 222 screen_output_1.append("Violets are blue", [(0, 7, "blue")]) 223 224 self.assertEqual(["Roses are red", "Violets are blue"], 225 screen_output_1.lines) 226 self.assertEqual({ 227 0: [(0, 5, "red")], 228 1: [(0, 7, "blue")], 229 }, screen_output_1.font_attr_segs) 230 231 def testPrependALineWithAttributeSegmentsWorks(self): 232 screen_output_1 = debugger_cli_common.RichTextLines( 233 ["Roses are red"], 234 font_attr_segs={0: [(0, 5, "red")]}, 235 annotations={0: "longer wavelength"}) 236 237 screen_output_1.prepend("Violets are blue", font_attr_segs=[(0, 7, "blue")]) 238 239 self.assertEqual(["Violets are blue", "Roses are red"], 240 screen_output_1.lines) 241 self.assertEqual({ 242 0: [(0, 7, "blue")], 243 1: [(0, 5, "red")], 244 }, screen_output_1.font_attr_segs) 245 246 def testWriteToFileSucceeds(self): 247 screen_output = debugger_cli_common.RichTextLines( 248 ["Roses are red", "Violets are blue"], 249 font_attr_segs={0: [(0, 5, "red")], 250 1: [(0, 7, "blue")]}) 251 252 fd, file_path = tempfile.mkstemp() 253 os.close(fd) # file opened exclusively, so we need to close this 254 # a better fix would be to make the API take a fd 255 screen_output.write_to_file(file_path) 256 257 with gfile.Open(file_path, "r") as f: 258 self.assertEqual("Roses are red\nViolets are blue\n", f.read()) 259 260 # Clean up. 261 gfile.Remove(file_path) 262 263 def testAttemptToWriteToADirectoryFails(self): 264 screen_output = debugger_cli_common.RichTextLines( 265 ["Roses are red", "Violets are blue"], 266 font_attr_segs={0: [(0, 5, "red")], 267 1: [(0, 7, "blue")]}) 268 269 with self.assertRaises(Exception): 270 screen_output.write_to_file("/") 271 272 def testAttemptToWriteToFileInNonexistentDirectoryFails(self): 273 screen_output = debugger_cli_common.RichTextLines( 274 ["Roses are red", "Violets are blue"], 275 font_attr_segs={0: [(0, 5, "red")], 276 1: [(0, 7, "blue")]}) 277 278 file_path = os.path.join(tempfile.mkdtemp(), "foo", "bar.txt") 279 with self.assertRaises(Exception): 280 screen_output.write_to_file(file_path) 281 282 283class CommandHandlerRegistryTest(test_util.TensorFlowTestCase): 284 285 def setUp(self): 286 self._intentional_error_msg = "Intentionally raised exception" 287 288 def _noop_handler(self, argv, screen_info=None): 289 # A handler that does nothing other than returning "Done." 290 return debugger_cli_common.RichTextLines(["Done."]) 291 292 def _handler_raising_exception(self, argv, screen_info=None): 293 # A handler that intentionally raises an exception. 294 raise RuntimeError(self._intentional_error_msg) 295 296 def _handler_returning_wrong_type(self, argv, screen_info=None): 297 # A handler that returns a wrong type, instead of the correct type 298 # (RichTextLines). 299 return "Hello" 300 301 def _echo_screen_cols(self, argv, screen_info=None): 302 # A handler that uses screen_info. 303 return debugger_cli_common.RichTextLines( 304 ["cols = %d" % screen_info["cols"]]) 305 306 def _exiting_handler(self, argv, screen_info=None): 307 """A handler that exits with an exit token.""" 308 309 if argv: 310 exit_token = argv[0] 311 else: 312 exit_token = None 313 314 raise debugger_cli_common.CommandLineExit(exit_token=exit_token) 315 316 def testRegisterEmptyCommandPrefix(self): 317 registry = debugger_cli_common.CommandHandlerRegistry() 318 319 # Attempt to register an empty-string as a command prefix should trigger 320 # an exception. 321 with self.assertRaisesRegex(ValueError, "Empty command prefix"): 322 registry.register_command_handler("", self._noop_handler, "") 323 324 def testRegisterAndInvokeHandler(self): 325 registry = debugger_cli_common.CommandHandlerRegistry() 326 registry.register_command_handler("noop", self._noop_handler, "") 327 328 self.assertTrue(registry.is_registered("noop")) 329 self.assertFalse(registry.is_registered("beep")) 330 331 cmd_output = registry.dispatch_command("noop", []) 332 self.assertEqual(["Done."], cmd_output.lines) 333 334 # Attempt to invoke an unregistered command prefix should trigger an 335 # exception. 336 with self.assertRaisesRegex(ValueError, "No handler is registered"): 337 registry.dispatch_command("beep", []) 338 339 # Empty command prefix should trigger an exception. 340 with self.assertRaisesRegex(ValueError, "Prefix is empty"): 341 registry.dispatch_command("", []) 342 343 def testExitingHandler(self): 344 """Test that exit exception is correctly raised.""" 345 346 registry = debugger_cli_common.CommandHandlerRegistry() 347 registry.register_command_handler("exit", self._exiting_handler, "") 348 349 self.assertTrue(registry.is_registered("exit")) 350 351 exit_token = None 352 try: 353 registry.dispatch_command("exit", ["foo"]) 354 except debugger_cli_common.CommandLineExit as e: 355 exit_token = e.exit_token 356 357 self.assertEqual("foo", exit_token) 358 359 def testInvokeHandlerWithScreenInfo(self): 360 registry = debugger_cli_common.CommandHandlerRegistry() 361 362 # Register and invoke a command handler that uses screen_info. 363 registry.register_command_handler("cols", self._echo_screen_cols, "") 364 365 cmd_output = registry.dispatch_command( 366 "cols", [], screen_info={"cols": 100}) 367 self.assertEqual(["cols = 100"], cmd_output.lines) 368 369 def testRegisterAndInvokeHandlerWithAliases(self): 370 registry = debugger_cli_common.CommandHandlerRegistry() 371 registry.register_command_handler( 372 "noop", self._noop_handler, "", prefix_aliases=["n", "NOOP"]) 373 374 # is_registered() should work for full prefix and aliases. 375 self.assertTrue(registry.is_registered("noop")) 376 self.assertTrue(registry.is_registered("n")) 377 self.assertTrue(registry.is_registered("NOOP")) 378 379 cmd_output = registry.dispatch_command("n", []) 380 self.assertEqual(["Done."], cmd_output.lines) 381 382 cmd_output = registry.dispatch_command("NOOP", []) 383 self.assertEqual(["Done."], cmd_output.lines) 384 385 def testHandlerWithWrongReturnType(self): 386 registry = debugger_cli_common.CommandHandlerRegistry() 387 registry.register_command_handler("wrong_return", 388 self._handler_returning_wrong_type, "") 389 390 # If the command handler fails to return a RichTextLines instance, an error 391 # should be triggered. 392 with self.assertRaisesRegex( 393 ValueError, 394 "Return value from command handler.*is not None or a RichTextLines " 395 "instance"): 396 registry.dispatch_command("wrong_return", []) 397 398 def testRegisterDuplicateHandlers(self): 399 registry = debugger_cli_common.CommandHandlerRegistry() 400 registry.register_command_handler("noop", self._noop_handler, "") 401 402 # Registering the same command prefix more than once should trigger an 403 # exception. 404 with self.assertRaisesRegex( 405 ValueError, "A handler is already registered for command prefix"): 406 registry.register_command_handler("noop", self._noop_handler, "") 407 408 cmd_output = registry.dispatch_command("noop", []) 409 self.assertEqual(["Done."], cmd_output.lines) 410 411 def testRegisterDuplicateAliases(self): 412 registry = debugger_cli_common.CommandHandlerRegistry() 413 registry.register_command_handler( 414 "noop", self._noop_handler, "", prefix_aliases=["n"]) 415 416 # Clash with existing alias. 417 with self.assertRaisesRegex(ValueError, 418 "clashes with existing prefixes or aliases"): 419 registry.register_command_handler( 420 "cols", self._echo_screen_cols, "", prefix_aliases=["n"]) 421 422 # The name clash should have prevent the handler from being registered. 423 self.assertFalse(registry.is_registered("cols")) 424 425 # Aliases can also clash with command prefixes. 426 with self.assertRaisesRegex(ValueError, 427 "clashes with existing prefixes or aliases"): 428 registry.register_command_handler( 429 "cols", self._echo_screen_cols, "", prefix_aliases=["noop"]) 430 431 self.assertFalse(registry.is_registered("cols")) 432 433 def testDispatchHandlerRaisingException(self): 434 registry = debugger_cli_common.CommandHandlerRegistry() 435 registry.register_command_handler("raise_exception", 436 self._handler_raising_exception, "") 437 438 # The registry should catch and wrap exceptions that occur during command 439 # handling. 440 cmd_output = registry.dispatch_command("raise_exception", []) 441 # The error output contains a stack trace. 442 # So the line count should be >= 2. 443 self.assertGreater(len(cmd_output.lines), 2) 444 self.assertTrue(cmd_output.lines[0].startswith( 445 "Error occurred during handling of command")) 446 self.assertTrue(cmd_output.lines[1].endswith(self._intentional_error_msg)) 447 448 def testRegisterNonCallableHandler(self): 449 registry = debugger_cli_common.CommandHandlerRegistry() 450 451 # Attempt to register a non-callable handler should fail. 452 with self.assertRaisesRegex(ValueError, "handler is not callable"): 453 registry.register_command_handler("non_callable", 1, "") 454 455 def testRegisterHandlerWithInvalidHelpInfoType(self): 456 registry = debugger_cli_common.CommandHandlerRegistry() 457 458 with self.assertRaisesRegex(ValueError, "help_info is not a str"): 459 registry.register_command_handler("noop", self._noop_handler, ["foo"]) 460 461 def testGetHelpFull(self): 462 registry = debugger_cli_common.CommandHandlerRegistry() 463 registry.register_command_handler( 464 "noop", 465 self._noop_handler, 466 "No operation.\nI.e., do nothing.", 467 prefix_aliases=["n", "NOOP"]) 468 registry.register_command_handler( 469 "cols", 470 self._echo_screen_cols, 471 "Show screen width in number of columns.", 472 prefix_aliases=["c"]) 473 474 help_lines = registry.get_help().lines 475 476 # The help info should list commands in alphabetically sorted order, 477 # regardless of order in which the commands are registered. 478 self.assertEqual("cols", help_lines[0]) 479 self.assertTrue(help_lines[1].endswith("Aliases: c")) 480 self.assertFalse(help_lines[2]) 481 self.assertTrue(help_lines[3].endswith( 482 "Show screen width in number of columns.")) 483 484 self.assertFalse(help_lines[4]) 485 self.assertFalse(help_lines[5]) 486 487 # The default help command should appear in the help output. 488 self.assertEqual("help", help_lines[6]) 489 490 self.assertEqual("noop", help_lines[12]) 491 self.assertTrue(help_lines[13].endswith("Aliases: n, NOOP")) 492 self.assertFalse(help_lines[14]) 493 self.assertTrue(help_lines[15].endswith("No operation.")) 494 self.assertTrue(help_lines[16].endswith("I.e., do nothing.")) 495 496 def testGetHelpSingleCommand(self): 497 registry = debugger_cli_common.CommandHandlerRegistry() 498 registry.register_command_handler( 499 "noop", 500 self._noop_handler, 501 "No operation.\nI.e., do nothing.", 502 prefix_aliases=["n", "NOOP"]) 503 registry.register_command_handler( 504 "cols", 505 self._echo_screen_cols, 506 "Show screen width in number of columns.", 507 prefix_aliases=["c"]) 508 509 # Get help info for one of the two commands, using full prefix. 510 help_lines = registry.get_help("cols").lines 511 512 self.assertTrue(help_lines[0].endswith("cols")) 513 self.assertTrue(help_lines[1].endswith("Aliases: c")) 514 self.assertFalse(help_lines[2]) 515 self.assertTrue(help_lines[3].endswith( 516 "Show screen width in number of columns.")) 517 518 # Get help info for one of the two commands, using alias. 519 help_lines = registry.get_help("c").lines 520 521 self.assertTrue(help_lines[0].endswith("cols")) 522 self.assertTrue(help_lines[1].endswith("Aliases: c")) 523 self.assertFalse(help_lines[2]) 524 self.assertTrue(help_lines[3].endswith( 525 "Show screen width in number of columns.")) 526 527 # Get help info for a nonexistent command. 528 help_lines = registry.get_help("foo").lines 529 530 self.assertEqual("Invalid command prefix: \"foo\"", help_lines[0]) 531 532 def testHelpCommandWithoutIntro(self): 533 registry = debugger_cli_common.CommandHandlerRegistry() 534 registry.register_command_handler( 535 "noop", 536 self._noop_handler, 537 "No operation.\nI.e., do nothing.", 538 prefix_aliases=["n", "NOOP"]) 539 registry.register_command_handler( 540 "cols", 541 self._echo_screen_cols, 542 "Show screen width in number of columns.", 543 prefix_aliases=["c"]) 544 545 # Get help for all commands. 546 output = registry.dispatch_command("help", []) 547 self.assertEqual(["cols", " Aliases: c", "", 548 " Show screen width in number of columns.", "", "", 549 "help", " Aliases: h", "", " Print this help message.", 550 "", "", "noop", " Aliases: n, NOOP", "", 551 " No operation.", " I.e., do nothing.", "", "", 552 "version", " Aliases: ver", "", 553 " Print the versions of TensorFlow and its key " 554 "dependencies.", "", ""], 555 output.lines) 556 557 # Get help for one specific command prefix. 558 output = registry.dispatch_command("help", ["noop"]) 559 self.assertEqual(["noop", " Aliases: n, NOOP", "", " No operation.", 560 " I.e., do nothing."], output.lines) 561 562 # Get help for a nonexistent command prefix. 563 output = registry.dispatch_command("help", ["foo"]) 564 self.assertEqual(["Invalid command prefix: \"foo\""], output.lines) 565 566 def testHelpCommandWithIntro(self): 567 registry = debugger_cli_common.CommandHandlerRegistry() 568 registry.register_command_handler( 569 "noop", 570 self._noop_handler, 571 "No operation.\nI.e., do nothing.", 572 prefix_aliases=["n", "NOOP"]) 573 574 help_intro = debugger_cli_common.RichTextLines( 575 ["Introductory comments.", ""]) 576 registry.set_help_intro(help_intro) 577 578 output = registry.dispatch_command("help", []) 579 self.assertEqual(help_intro.lines + [ 580 "help", " Aliases: h", "", " Print this help message.", "", "", 581 "noop", " Aliases: n, NOOP", "", " No operation.", 582 " I.e., do nothing.", "", "", 583 "version", " Aliases: ver", "", 584 " Print the versions of TensorFlow and its key dependencies.", "", "" 585 ], output.lines) 586 587 588class RegexFindTest(test_util.TensorFlowTestCase): 589 590 def setUp(self): 591 self._orig_screen_output = debugger_cli_common.RichTextLines( 592 ["Roses are red", "Violets are blue"]) 593 594 def testRegexFindWithoutExistingFontAttrSegs(self): 595 new_screen_output = debugger_cli_common.regex_find(self._orig_screen_output, 596 "are", "yellow") 597 598 self.assertEqual(2, len(new_screen_output.font_attr_segs)) 599 self.assertEqual([(6, 9, "yellow")], new_screen_output.font_attr_segs[0]) 600 self.assertEqual([(8, 11, "yellow")], new_screen_output.font_attr_segs[1]) 601 602 # Check field in annotations carrying a list of matching line indices. 603 self.assertEqual([0, 1], new_screen_output.annotations[ 604 debugger_cli_common.REGEX_MATCH_LINES_KEY]) 605 606 def testRegexFindWithExistingFontAttrSegs(self): 607 # Add a font attribute segment first. 608 self._orig_screen_output.font_attr_segs[0] = [(9, 12, "red")] 609 self.assertEqual(1, len(self._orig_screen_output.font_attr_segs)) 610 611 new_screen_output = debugger_cli_common.regex_find(self._orig_screen_output, 612 "are", "yellow") 613 self.assertEqual(2, len(new_screen_output.font_attr_segs)) 614 615 self.assertEqual([(6, 9, "yellow"), (9, 12, "red")], 616 new_screen_output.font_attr_segs[0]) 617 618 self.assertEqual([0, 1], new_screen_output.annotations[ 619 debugger_cli_common.REGEX_MATCH_LINES_KEY]) 620 621 def testRegexFindWithNoMatches(self): 622 new_screen_output = debugger_cli_common.regex_find(self._orig_screen_output, 623 "infrared", "yellow") 624 625 self.assertEqual({}, new_screen_output.font_attr_segs) 626 self.assertEqual([], new_screen_output.annotations[ 627 debugger_cli_common.REGEX_MATCH_LINES_KEY]) 628 629 def testInvalidRegex(self): 630 with self.assertRaisesRegex(ValueError, "Invalid regular expression"): 631 debugger_cli_common.regex_find(self._orig_screen_output, "[", "yellow") 632 633 def testRegexFindOnPrependedLinesWorks(self): 634 rich_lines = debugger_cli_common.RichTextLines(["Violets are blue"]) 635 rich_lines.prepend(["Roses are red"]) 636 searched_rich_lines = debugger_cli_common.regex_find( 637 rich_lines, "red", "bold") 638 self.assertEqual( 639 {0: [(10, 13, "bold")]}, searched_rich_lines.font_attr_segs) 640 641 rich_lines = debugger_cli_common.RichTextLines(["Violets are blue"]) 642 rich_lines.prepend(["A poem"], font_attr_segs=[(0, 1, "underline")]) 643 searched_rich_lines = debugger_cli_common.regex_find( 644 rich_lines, "poem", "italic") 645 self.assertEqual( 646 {0: [(0, 1, "underline"), (2, 6, "italic")]}, 647 searched_rich_lines.font_attr_segs) 648 649 650class WrapScreenOutputTest(test_util.TensorFlowTestCase): 651 652 def setUp(self): 653 self._orig_screen_output = debugger_cli_common.RichTextLines( 654 ["Folk song:", "Roses are red", "Violets are blue"], 655 font_attr_segs={1: [(0, 5, "red"), (6, 9, "gray"), (10, 12, "red"), 656 (12, 13, "crimson")], 657 2: [(0, 7, "blue"), (8, 11, "gray"), (12, 14, "blue"), 658 (14, 16, "indigo")]}, 659 annotations={1: "longer wavelength", 660 2: "shorter wavelength"}) 661 662 def testNoActualWrapping(self): 663 # Large column limit should lead to no actual wrapping. 664 out, new_line_indices = debugger_cli_common.wrap_rich_text_lines( 665 self._orig_screen_output, 100) 666 667 self.assertEqual(self._orig_screen_output.lines, out.lines) 668 self.assertEqual(self._orig_screen_output.font_attr_segs, 669 out.font_attr_segs) 670 self.assertEqual(self._orig_screen_output.annotations, out.annotations) 671 self.assertEqual(new_line_indices, [0, 1, 2]) 672 673 def testWrappingWithAttrCutoff(self): 674 out, new_line_indices = debugger_cli_common.wrap_rich_text_lines( 675 self._orig_screen_output, 11) 676 677 # Add non-row-index field to out. 678 out.annotations["metadata"] = "foo" 679 680 # Check wrapped text. 681 self.assertEqual(5, len(out.lines)) 682 self.assertEqual("Folk song:", out.lines[0]) 683 self.assertEqual("Roses are r", out.lines[1]) 684 self.assertEqual("ed", out.lines[2]) 685 self.assertEqual("Violets are", out.lines[3]) 686 self.assertEqual(" blue", out.lines[4]) 687 688 # Check wrapped font_attr_segs. 689 self.assertFalse(0 in out.font_attr_segs) 690 self.assertEqual([(0, 5, "red"), (6, 9, "gray"), (10, 11, "red")], 691 out.font_attr_segs[1]) 692 self.assertEqual([(0, 1, "red"), (1, 2, "crimson")], out.font_attr_segs[2]) 693 self.assertEqual([(0, 7, "blue"), (8, 11, "gray")], out.font_attr_segs[3]) 694 self.assertEqual([(1, 3, "blue"), (3, 5, "indigo")], out.font_attr_segs[4]) 695 696 # Check annotations. 697 self.assertFalse(0 in out.annotations) 698 self.assertEqual("longer wavelength", out.annotations[1]) 699 self.assertFalse(2 in out.annotations) 700 self.assertEqual("shorter wavelength", out.annotations[3]) 701 self.assertFalse(4 in out.annotations) 702 703 # Chec that the non-row-index field is present in output. 704 self.assertEqual("foo", out.annotations["metadata"]) 705 706 self.assertEqual(new_line_indices, [0, 1, 3]) 707 708 def testWrappingWithMultipleAttrCutoff(self): 709 self._orig_screen_output = debugger_cli_common.RichTextLines( 710 ["Folk song:", "Roses are red", "Violets are blue"], 711 font_attr_segs={1: [(0, 12, "red")], 712 2: [(1, 16, "blue")]}, 713 annotations={1: "longer wavelength", 714 2: "shorter wavelength"}) 715 716 out, new_line_indices = debugger_cli_common.wrap_rich_text_lines( 717 self._orig_screen_output, 5) 718 719 # Check wrapped text. 720 self.assertEqual(9, len(out.lines)) 721 self.assertEqual("Folk ", out.lines[0]) 722 self.assertEqual("song:", out.lines[1]) 723 self.assertEqual("Roses", out.lines[2]) 724 self.assertEqual(" are ", out.lines[3]) 725 self.assertEqual("red", out.lines[4]) 726 self.assertEqual("Viole", out.lines[5]) 727 self.assertEqual("ts ar", out.lines[6]) 728 self.assertEqual("e blu", out.lines[7]) 729 self.assertEqual("e", out.lines[8]) 730 731 # Check wrapped font_attr_segs. 732 self.assertFalse(0 in out.font_attr_segs) 733 self.assertFalse(1 in out.font_attr_segs) 734 self.assertEqual([(0, 5, "red")], out.font_attr_segs[2]) 735 self.assertEqual([(0, 5, "red")], out.font_attr_segs[3]) 736 self.assertEqual([(0, 2, "red")], out.font_attr_segs[4]) 737 self.assertEqual([(1, 5, "blue")], out.font_attr_segs[5]) 738 self.assertEqual([(0, 5, "blue")], out.font_attr_segs[6]) 739 self.assertEqual([(0, 5, "blue")], out.font_attr_segs[7]) 740 self.assertEqual([(0, 1, "blue")], out.font_attr_segs[8]) 741 742 # Check annotations. 743 self.assertFalse(0 in out.annotations) 744 self.assertFalse(1 in out.annotations) 745 self.assertEqual("longer wavelength", out.annotations[2]) 746 self.assertFalse(3 in out.annotations) 747 self.assertFalse(4 in out.annotations) 748 self.assertEqual("shorter wavelength", out.annotations[5]) 749 self.assertFalse(6 in out.annotations) 750 self.assertFalse(7 in out.annotations) 751 self.assertFalse(8 in out.annotations) 752 753 self.assertEqual(new_line_indices, [0, 2, 5]) 754 755 def testWrappingInvalidArguments(self): 756 with self.assertRaisesRegex(ValueError, 757 "Invalid type of input screen_output"): 758 debugger_cli_common.wrap_rich_text_lines("foo", 12) 759 760 with self.assertRaisesRegex(ValueError, "Invalid type of input cols"): 761 debugger_cli_common.wrap_rich_text_lines( 762 debugger_cli_common.RichTextLines(["foo", "bar"]), "12") 763 764 def testWrappingEmptyInput(self): 765 out, new_line_indices = debugger_cli_common.wrap_rich_text_lines( 766 debugger_cli_common.RichTextLines([]), 10) 767 768 self.assertEqual([], out.lines) 769 self.assertEqual([], new_line_indices) 770 771 772class SliceRichTextLinesTest(test_util.TensorFlowTestCase): 773 774 def setUp(self): 775 self._original = debugger_cli_common.RichTextLines( 776 ["Roses are red", "Violets are blue"], 777 font_attr_segs={0: [(0, 5, "red")], 778 1: [(0, 7, "blue")]}, 779 annotations={ 780 0: "longer wavelength", 781 1: "shorter wavelength", 782 "foo_metadata": "bar" 783 }) 784 785 def testSliceBeginning(self): 786 sliced = self._original.slice(0, 1) 787 788 self.assertEqual(["Roses are red"], sliced.lines) 789 self.assertEqual({0: [(0, 5, "red")]}, sliced.font_attr_segs) 790 791 # Non-line-number metadata should be preserved. 792 self.assertEqual({ 793 0: "longer wavelength", 794 "foo_metadata": "bar" 795 }, sliced.annotations) 796 797 self.assertEqual(1, sliced.num_lines()) 798 799 def testSliceEnd(self): 800 sliced = self._original.slice(1, 2) 801 802 self.assertEqual(["Violets are blue"], sliced.lines) 803 804 # The line index should have changed from 1 to 0. 805 self.assertEqual({0: [(0, 7, "blue")]}, sliced.font_attr_segs) 806 self.assertEqual({ 807 0: "shorter wavelength", 808 "foo_metadata": "bar" 809 }, sliced.annotations) 810 811 self.assertEqual(1, sliced.num_lines()) 812 813 def testAttemptSliceWithNegativeIndex(self): 814 with self.assertRaisesRegex(ValueError, "Encountered negative index"): 815 self._original.slice(0, -1) 816 817 818class TabCompletionRegistryTest(test_util.TensorFlowTestCase): 819 820 def setUp(self): 821 self._tc_reg = debugger_cli_common.TabCompletionRegistry() 822 823 # Register the items in an unsorted order deliberately, to test the sorted 824 # output from get_completions(). 825 self._tc_reg.register_tab_comp_context( 826 ["print_tensor", "pt"], 827 ["node_b:1", "node_b:2", "node_a:1", "node_a:2"]) 828 self._tc_reg.register_tab_comp_context(["node_info"], 829 ["node_c", "node_b", "node_a"]) 830 831 def testTabCompletion(self): 832 # The returned completions should have sorted order. 833 self.assertEqual( 834 (["node_a:1", "node_a:2", "node_b:1", "node_b:2"], "node_"), 835 self._tc_reg.get_completions("print_tensor", "node_")) 836 837 self.assertEqual((["node_a:1", "node_a:2", "node_b:1", "node_b:2"], 838 "node_"), self._tc_reg.get_completions("pt", "")) 839 840 self.assertEqual((["node_a:1", "node_a:2"], "node_a:"), 841 self._tc_reg.get_completions("print_tensor", "node_a")) 842 843 self.assertEqual((["node_a:1"], "node_a:1"), 844 self._tc_reg.get_completions("pt", "node_a:1")) 845 846 self.assertEqual(([], ""), 847 self._tc_reg.get_completions("print_tensor", "node_a:3")) 848 849 self.assertEqual((None, None), self._tc_reg.get_completions("foo", "node_")) 850 851 def testExtendCompletionItems(self): 852 self.assertEqual( 853 (["node_a:1", "node_a:2", "node_b:1", "node_b:2"], "node_"), 854 self._tc_reg.get_completions("print_tensor", "node_")) 855 self.assertEqual((["node_a", "node_b", "node_c"], "node_"), 856 self._tc_reg.get_completions("node_info", "node_")) 857 858 self._tc_reg.extend_comp_items("print_tensor", ["node_A:1", "node_A:2"]) 859 860 self.assertEqual((["node_A:1", "node_A:2", "node_a:1", "node_a:2", 861 "node_b:1", "node_b:2"], "node_"), 862 self._tc_reg.get_completions("print_tensor", "node_")) 863 864 # Extending the completions for one of the context's context words should 865 # have taken effect on other context words of the same context as well. 866 self.assertEqual((["node_A:1", "node_A:2", "node_a:1", "node_a:2", 867 "node_b:1", "node_b:2"], "node_"), 868 self._tc_reg.get_completions("pt", "node_")) 869 self.assertEqual((["node_a", "node_b", "node_c"], "node_"), 870 self._tc_reg.get_completions("node_info", "node_")) 871 872 def testExtendCompletionItemsNonexistentContext(self): 873 with self.assertRaisesRegex(KeyError, 874 "Context word \"foo\" has not been registered"): 875 self._tc_reg.extend_comp_items("foo", ["node_A:1", "node_A:2"]) 876 877 def testRemoveCompletionItems(self): 878 self.assertEqual( 879 (["node_a:1", "node_a:2", "node_b:1", "node_b:2"], "node_"), 880 self._tc_reg.get_completions("print_tensor", "node_")) 881 self.assertEqual((["node_a", "node_b", "node_c"], "node_"), 882 self._tc_reg.get_completions("node_info", "node_")) 883 884 self._tc_reg.remove_comp_items("pt", ["node_a:1", "node_a:2"]) 885 886 self.assertEqual((["node_b:1", "node_b:2"], "node_b:"), 887 self._tc_reg.get_completions("print_tensor", "node_")) 888 self.assertEqual((["node_a", "node_b", "node_c"], "node_"), 889 self._tc_reg.get_completions("node_info", "node_")) 890 891 def testRemoveCompletionItemsNonexistentContext(self): 892 with self.assertRaisesRegex(KeyError, 893 "Context word \"foo\" has not been registered"): 894 self._tc_reg.remove_comp_items("foo", ["node_a:1", "node_a:2"]) 895 896 def testDeregisterContext(self): 897 self.assertEqual( 898 (["node_a:1", "node_a:2", "node_b:1", "node_b:2"], "node_"), 899 self._tc_reg.get_completions("print_tensor", "node_")) 900 self.assertEqual((["node_a", "node_b", "node_c"], "node_"), 901 self._tc_reg.get_completions("node_info", "node_")) 902 903 self._tc_reg.deregister_context(["print_tensor"]) 904 905 self.assertEqual((None, None), 906 self._tc_reg.get_completions("print_tensor", "node_")) 907 908 # The alternative context word should be unaffected. 909 self.assertEqual( 910 (["node_a:1", "node_a:2", "node_b:1", "node_b:2"], "node_"), 911 self._tc_reg.get_completions("pt", "node_")) 912 913 def testDeregisterNonexistentContext(self): 914 self.assertEqual( 915 (["node_a:1", "node_a:2", "node_b:1", "node_b:2"], "node_"), 916 self._tc_reg.get_completions("print_tensor", "node_")) 917 self.assertEqual((["node_a", "node_b", "node_c"], "node_"), 918 self._tc_reg.get_completions("node_info", "node_")) 919 920 self._tc_reg.deregister_context(["print_tensor"]) 921 922 with self.assertRaisesRegex( 923 KeyError, 924 "Cannot deregister unregistered context word \"print_tensor\""): 925 self._tc_reg.deregister_context(["print_tensor"]) 926 927 928class CommandHistoryTest(test_util.TensorFlowTestCase): 929 930 def setUp(self): 931 self._fd, self._history_file_path = tempfile.mkstemp() 932 self._cmd_hist = debugger_cli_common.CommandHistory( 933 limit=3, history_file_path=self._history_file_path) 934 935 def tearDown(self): 936 if os.path.isfile(self._history_file_path): 937 os.close(self._fd) 938 os.remove(self._history_file_path) 939 940 def _restoreFileReadWritePermissions(self, file_path): 941 os.chmod(file_path, 942 (stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR | 943 stat.S_IWGRP | stat.S_IWOTH)) 944 945 def testLookUpMostRecent(self): 946 self.assertEqual([], self._cmd_hist.most_recent_n(3)) 947 948 self._cmd_hist.add_command("list_tensors") 949 self._cmd_hist.add_command("node_info node_a") 950 951 self.assertEqual(["node_info node_a"], self._cmd_hist.most_recent_n(1)) 952 self.assertEqual(["list_tensors", "node_info node_a"], 953 self._cmd_hist.most_recent_n(2)) 954 self.assertEqual(["list_tensors", "node_info node_a"], 955 self._cmd_hist.most_recent_n(3)) 956 957 self._cmd_hist.add_command("node_info node_b") 958 959 self.assertEqual(["node_info node_b"], self._cmd_hist.most_recent_n(1)) 960 self.assertEqual(["node_info node_a", "node_info node_b"], 961 self._cmd_hist.most_recent_n(2)) 962 self.assertEqual(["list_tensors", "node_info node_a", "node_info node_b"], 963 self._cmd_hist.most_recent_n(3)) 964 self.assertEqual(["list_tensors", "node_info node_a", "node_info node_b"], 965 self._cmd_hist.most_recent_n(4)) 966 967 # Go over the limit. 968 self._cmd_hist.add_command("node_info node_a") 969 970 self.assertEqual(["node_info node_a"], self._cmd_hist.most_recent_n(1)) 971 self.assertEqual(["node_info node_b", "node_info node_a"], 972 self._cmd_hist.most_recent_n(2)) 973 self.assertEqual( 974 ["node_info node_a", "node_info node_b", "node_info node_a"], 975 self._cmd_hist.most_recent_n(3)) 976 self.assertEqual( 977 ["node_info node_a", "node_info node_b", "node_info node_a"], 978 self._cmd_hist.most_recent_n(4)) 979 980 def testLookUpPrefix(self): 981 self._cmd_hist.add_command("node_info node_b") 982 self._cmd_hist.add_command("list_tensors") 983 self._cmd_hist.add_command("node_info node_a") 984 985 self.assertEqual(["node_info node_b", "node_info node_a"], 986 self._cmd_hist.lookup_prefix("node_info", 10)) 987 988 self.assertEqual(["node_info node_a"], self._cmd_hist.lookup_prefix( 989 "node_info", 1)) 990 991 self.assertEqual([], self._cmd_hist.lookup_prefix("print_tensor", 10)) 992 993 def testAddNonStrCommand(self): 994 with self.assertRaisesRegex( 995 TypeError, "Attempt to enter non-str entry to command history"): 996 self._cmd_hist.add_command(["print_tensor node_a:0"]) 997 998 def testRepeatingCommandsDoNotGetLoggedRepeatedly(self): 999 self._cmd_hist.add_command("help") 1000 self._cmd_hist.add_command("help") 1001 1002 self.assertEqual(["help"], self._cmd_hist.most_recent_n(2)) 1003 1004 def testLoadingCommandHistoryFileObeysLimit(self): 1005 self._cmd_hist.add_command("help 1") 1006 self._cmd_hist.add_command("help 2") 1007 self._cmd_hist.add_command("help 3") 1008 self._cmd_hist.add_command("help 4") 1009 1010 cmd_hist_2 = debugger_cli_common.CommandHistory( 1011 limit=3, history_file_path=self._history_file_path) 1012 self.assertEqual(["help 2", "help 3", "help 4"], 1013 cmd_hist_2.most_recent_n(3)) 1014 1015 with open(self._history_file_path, "rt") as f: 1016 self.assertEqual( 1017 ["help 2\n", "help 3\n", "help 4\n"], f.readlines()) 1018 1019 def testCommandHistoryHandlesReadingIOErrorGraciously(self): 1020 with open(self._history_file_path, "wt") as f: 1021 f.write("help\n") 1022 1023 # Change file to not readable by anyone. 1024 os.chmod(self._history_file_path, 0) 1025 1026 # The creation of a CommandHistory object should not error out. 1027 debugger_cli_common.CommandHistory( 1028 limit=3, history_file_path=self._history_file_path) 1029 1030 self._restoreFileReadWritePermissions(self._history_file_path) 1031 1032 1033class MenuNodeTest(test_util.TensorFlowTestCase): 1034 1035 def testCommandTypeConstructorSucceeds(self): 1036 menu_node = debugger_cli_common.MenuItem("water flower", "water_flower") 1037 1038 self.assertEqual("water flower", menu_node.caption) 1039 self.assertEqual("water_flower", menu_node.content) 1040 1041 def testDisableWorks(self): 1042 menu_node = debugger_cli_common.MenuItem("water flower", "water_flower") 1043 self.assertTrue(menu_node.is_enabled()) 1044 1045 menu_node.disable() 1046 self.assertFalse(menu_node.is_enabled()) 1047 menu_node.enable() 1048 self.assertTrue(menu_node.is_enabled()) 1049 1050 def testConstructAsDisabledWorks(self): 1051 menu_node = debugger_cli_common.MenuItem( 1052 "water flower", "water_flower", enabled=False) 1053 self.assertFalse(menu_node.is_enabled()) 1054 1055 menu_node.enable() 1056 self.assertTrue(menu_node.is_enabled()) 1057 1058 1059class MenuTest(test_util.TensorFlowTestCase): 1060 1061 def setUp(self): 1062 self.menu = debugger_cli_common.Menu() 1063 self.assertEqual(0, self.menu.num_items()) 1064 1065 self.node1 = debugger_cli_common.MenuItem("water flower", "water_flower") 1066 self.node2 = debugger_cli_common.MenuItem( 1067 "measure wavelength", "measure_wavelength") 1068 self.menu.append(self.node1) 1069 self.menu.append(self.node2) 1070 self.assertEqual(2, self.menu.num_items()) 1071 1072 def testFormatAsSingleLineWithStrItemAttrsWorks(self): 1073 output = self.menu.format_as_single_line( 1074 prefix="Menu: ", divider=", ", enabled_item_attrs="underline") 1075 self.assertEqual(["Menu: water flower, measure wavelength, "], output.lines) 1076 self.assertEqual((6, 18, [self.node1, "underline"]), 1077 output.font_attr_segs[0][0]) 1078 self.assertEqual((20, 38, [self.node2, "underline"]), 1079 output.font_attr_segs[0][1]) 1080 self.assertEqual({}, output.annotations) 1081 1082 def testFormatAsSingleLineWithListItemAttrsWorks(self): 1083 output = self.menu.format_as_single_line( 1084 prefix="Menu: ", divider=", ", enabled_item_attrs=["underline", "bold"]) 1085 self.assertEqual(["Menu: water flower, measure wavelength, "], output.lines) 1086 self.assertEqual((6, 18, [self.node1, "underline", "bold"]), 1087 output.font_attr_segs[0][0]) 1088 self.assertEqual((20, 38, [self.node2, "underline", "bold"]), 1089 output.font_attr_segs[0][1]) 1090 self.assertEqual({}, output.annotations) 1091 1092 def testFormatAsSingleLineWithNoneItemAttrsWorks(self): 1093 output = self.menu.format_as_single_line(prefix="Menu: ", divider=", ") 1094 self.assertEqual(["Menu: water flower, measure wavelength, "], output.lines) 1095 self.assertEqual((6, 18, [self.node1]), output.font_attr_segs[0][0]) 1096 self.assertEqual((20, 38, [self.node2]), output.font_attr_segs[0][1]) 1097 self.assertEqual({}, output.annotations) 1098 1099 def testInsertNode(self): 1100 self.assertEqual(["water flower", "measure wavelength"], 1101 self.menu.captions()) 1102 1103 node2 = debugger_cli_common.MenuItem("write poem", "write_poem") 1104 self.menu.insert(1, node2) 1105 self.assertEqual(["water flower", "write poem", "measure wavelength"], 1106 self.menu.captions()) 1107 1108 output = self.menu.format_as_single_line(prefix="Menu: ", divider=", ") 1109 self.assertEqual(["Menu: water flower, write poem, measure wavelength, "], 1110 output.lines) 1111 1112 def testFormatAsSingleLineWithDisabledNode(self): 1113 node2 = debugger_cli_common.MenuItem( 1114 "write poem", "write_poem", enabled=False) 1115 self.menu.append(node2) 1116 1117 output = self.menu.format_as_single_line( 1118 prefix="Menu: ", divider=", ", disabled_item_attrs="bold") 1119 self.assertEqual(["Menu: water flower, measure wavelength, write poem, "], 1120 output.lines) 1121 self.assertEqual((6, 18, [self.node1]), output.font_attr_segs[0][0]) 1122 self.assertEqual((20, 38, [self.node2]), output.font_attr_segs[0][1]) 1123 self.assertEqual((40, 50, ["bold"]), output.font_attr_segs[0][2]) 1124 1125 1126class GetTensorFlowVersionLinesTest(test_util.TensorFlowTestCase): 1127 1128 def testGetVersionWithoutDependencies(self): 1129 out = debugger_cli_common.get_tensorflow_version_lines() 1130 self.assertEqual(2, len(out.lines)) 1131 self.assertEqual("TensorFlow version: %s" % pywrap_tf_session.__version__, 1132 out.lines[0]) 1133 1134 def testGetVersionWithDependencies(self): 1135 out = debugger_cli_common.get_tensorflow_version_lines(True) 1136 self.assertIn("TensorFlow version: %s" % pywrap_tf_session.__version__, 1137 out.lines) 1138 self.assertIn(" numpy: %s" % np.__version__, out.lines) 1139 1140 1141if __name__ == "__main__": 1142 googletest.main() 1143