| 1 | #!/usr/bin/env python2
 | 
| 2 | """
 | 
| 3 | history_test.py: Tests for history.py
 | 
| 4 | """
 | 
| 5 | from __future__ import print_function
 | 
| 6 | 
 | 
| 7 | import unittest
 | 
| 8 | import sys
 | 
| 9 | 
 | 
| 10 | from core import test_lib
 | 
| 11 | from core import util
 | 
| 12 | from osh import history  # module under test
 | 
| 13 | from frontend import parse_lib
 | 
| 14 | 
 | 
| 15 | # TODO: This can be replaced by the real thing!  Call read_history_file
 | 
| 16 | 
 | 
| 17 | 
 | 
| 18 | class _MockReadlineHistory(object):
 | 
| 19 | 
 | 
| 20 |     def __init__(self, items):
 | 
| 21 |         self.items = items
 | 
| 22 | 
 | 
| 23 |     def get_current_history_length(self):
 | 
| 24 |         return len(self.items)
 | 
| 25 | 
 | 
| 26 |     def get_history_item(self, one_based_index):
 | 
| 27 |         try:
 | 
| 28 |             return self.items[one_based_index - 1]
 | 
| 29 |         except IndexError:
 | 
| 30 |             return None  # matches what readline does
 | 
| 31 | 
 | 
| 32 | 
 | 
| 33 | def _MakeHistoryEvaluator(history_items):
 | 
| 34 |     parse_ctx = test_lib.InitParseContext()
 | 
| 35 |     parse_ctx.Init_Trail(parse_lib.Trail())
 | 
| 36 | 
 | 
| 37 |     debug_f = util.DebugFile(sys.stdout)
 | 
| 38 |     readline = _MockReadlineHistory(history_items)
 | 
| 39 |     return history.Evaluator(readline, parse_ctx, debug_f)
 | 
| 40 | 
 | 
| 41 | 
 | 
| 42 | class HistoryEvaluatorTest(unittest.TestCase):
 | 
| 43 | 
 | 
| 44 |     def testInvalidHistoryItems(self):
 | 
| 45 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 46 |             '(',
 | 
| 47 |         ])
 | 
| 48 |         # If you can't parse a command, then it uses the "trail", which is somewhat
 | 
| 49 |         # ill-defined, but causes an error in this case.
 | 
| 50 |         self.assertRaises(util.HistoryError, hist_ev.Eval, 'echo !$')
 | 
| 51 | 
 | 
| 52 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 53 |             'a( )',
 | 
| 54 |         ])
 | 
| 55 |         self.assertRaises(util.HistoryError, hist_ev.Eval, 'echo !$')
 | 
| 56 | 
 | 
| 57 |     def testReplacements(self):
 | 
| 58 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 59 |             'echo 1',
 | 
| 60 |             'echo ${two:-}',
 | 
| 61 |             'ls /echo/',
 | 
| 62 |         ])
 | 
| 63 | 
 | 
| 64 |         self.assertEqual('echo hi', hist_ev.Eval('echo hi'))
 | 
| 65 | 
 | 
| 66 |         # Search for prefix
 | 
| 67 |         self.assertEqual('echo ${two:-}\n', hist_ev.Eval('!echo\n'))
 | 
| 68 |         # Search for substring
 | 
| 69 |         self.assertEqual('echo ${two:-} ', hist_ev.Eval('!?two '))
 | 
| 70 | 
 | 
| 71 |         # Indexes and negative indexes
 | 
| 72 |         self.assertEqual('echo 1', hist_ev.Eval('!1'))
 | 
| 73 |         self.assertEqual('ls /echo/', hist_ev.Eval('!-1'))
 | 
| 74 |         self.assertEqual('echo ${two:-}', hist_ev.Eval('!-2'))
 | 
| 75 | 
 | 
| 76 |         self.assertRaises(util.HistoryError, hist_ev.Eval, 'echo !-999')
 | 
| 77 |         self.assertRaises(util.HistoryError, hist_ev.Eval, '!999')
 | 
| 78 | 
 | 
| 79 |         self.assertEqual('ls /echo/', hist_ev.Eval('!!'))
 | 
| 80 | 
 | 
| 81 |         self.assertEqual('echo /echo/', hist_ev.Eval('echo !$'))
 | 
| 82 | 
 | 
| 83 |     def testBug(self):
 | 
| 84 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 85 |             'echo ${two:-}',
 | 
| 86 |         ])
 | 
| 87 |         self.assertEqual('echo ${two:-}', hist_ev.Eval('echo !$'))
 | 
| 88 | 
 | 
| 89 |         # Commented out
 | 
| 90 |         self.assertEqual('echo hi  # !$', hist_ev.Eval('echo hi  # !$'))
 | 
| 91 | 
 | 
| 92 |         # This is not technically a comment, but it's hard to re-lex.
 | 
| 93 |         self.assertEqual('echo hi#!$', hist_ev.Eval('echo hi#!$'))
 | 
| 94 | 
 | 
| 95 |         # Workaround: the comment char can be single-quoted.
 | 
| 96 |         self.assertEqual("echo 'hi#'${two:-}", hist_ev.Eval("echo 'hi#'!$"))
 | 
| 97 | 
 | 
| 98 |     def testParsing(self):
 | 
| 99 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 100 |             'echo 1',
 | 
| 101 |             'echo $three ${4:-} "${five@P}"',
 | 
| 102 |         ])
 | 
| 103 |         self.assertEqual('echo "${five@P}"', hist_ev.Eval('echo !$'))
 | 
| 104 |         self.assertEqual('echo $three', hist_ev.Eval('echo !^'))
 | 
| 105 |         self.assertEqual('echo -n $three ${4:-} "${five@P}"',
 | 
| 106 |                          hist_ev.Eval('echo -n !*'))
 | 
| 107 | 
 | 
| 108 |     def testNonCommands(self):
 | 
| 109 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 110 |             'echo hi | wc -l',
 | 
| 111 |         ])
 | 
| 112 |         self.assertEqual('echo -l', hist_ev.Eval('echo !$'))
 | 
| 113 | 
 | 
| 114 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 115 |             'for i in 1 2 3; do echo xx; done',
 | 
| 116 |         ])
 | 
| 117 |         self.assertEqual('echo xx', hist_ev.Eval('echo !$'))
 | 
| 118 | 
 | 
| 119 |         hist_ev = _MakeHistoryEvaluator([
 | 
| 120 |             '{ echo yy; }',
 | 
| 121 |         ])
 | 
| 122 |         self.assertEqual('echo yy', hist_ev.Eval('echo !$'))
 | 
| 123 | 
 | 
| 124 | 
 | 
| 125 | if __name__ == '__main__':
 | 
| 126 |     unittest.main()
 |