xref: /aosp_15_r20/external/tensorflow/tensorflow/python/debug/cli/debugger_cli_common_test.py (revision b6fb3261f9314811a0f4371741dbb8839866f948)
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