# -*- coding: latin-1 -*- # Copyright 2018 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import absolute_import from __future__ import division from __future__ import print_function try: unicode except NameError: unicode = str unichr = chr import curses import inspect import os import sys import tempfile import unittest import app.ci_program import app.curses_util #from app.curses_util import * def debug_print_stack(*args): stack = inspect.stack()[1:] stack.reverse() lines = [] for i, frame in enumerate(stack): lines.append(u"stack %2d %14s %4s %s" % (i, os.path.split(frame[1])[1], frame[2], frame[3])) print(u"\n".join(lines)) class FakeCursesTestCase(unittest.TestCase): def setUp(self): self.cursesScreen = curses.StandardScreen() self.prg = app.ci_program.CiProgram() self.prg.setUpCurses(self.cursesScreen) # For testing, use the internal clipboard. Using the system clipboard # can create races between tests running in parallel. self.prg.clipboard.setOsHandlers(None, None) def findTextAndClick(self, timeStamp, screenText, bState): caller = inspect.stack()[1] callerText = u"\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def createEvent(display, cmdIndex): row, col = self.findText(screenText) if row < 0: output = u"%s at index %d, did not find %r" % ( callerText, cmdIndex, screenText) if self.cursesScreen.movie: print(output) else: self.fail(output) # Note that the mouse info is x,y (col, row). info = (timeStamp, col, row, 0, bState) curses.addMouseEvent(info) return curses.KEY_MOUSE return createEvent def mouseEvent(self, timeStamp, mouseRow, mouseCol, bState): """ bState may be a logical or of: curses.BUTTON1_PRESSED; curses.BUTTON1_RELEASED; ... curses.BUTTON_SHIFT curses.BUTTON_CTRL curses.BUTTON_ALT """ assert isinstance(timeStamp, int) assert isinstance(mouseRow, int) assert isinstance(mouseCol, int) assert isinstance(bState, int) # Note that the mouse info is x,y (col, row). info = (timeStamp, mouseCol, mouseRow, 0, bState) def createEvent(display, cmdIndex): curses.addMouseEvent(info) return curses.KEY_MOUSE return createEvent def addMouseInfo(self, timeStamp, mouseRow, mouseCol, bState): """ bState may be a logical or of: curses.BUTTON1_PRESSED; curses.BUTTON1_RELEASED; ... curses.BUTTON_SHIFT curses.BUTTON_CTRL curses.BUTTON_ALT """ assert isinstance(timeStamp, int) assert isinstance(mouseRow, int) assert isinstance(mouseCol, int) assert isinstance(bState, int) # Note that the mouse info is x,y (col, row). info = (timeStamp, mouseCol, mouseRow, 0, bState) def createEvent(display, cmdIndex): curses.addMouseEvent(info) return None return createEvent def call(self, *args): """Call arbitrary function as a 'fake input'.""" caller = inspect.stack()[1] callerText = u"\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def caller(display, cmdIndex): try: args[0](*args[1:]) except Exception as e: output = callerText + u' at index ' + str(cmdIndex) print(output) self.fail(e) return None return caller def displayCheck(self, *args): assert isinstance(args[0], int) assert isinstance(args[1], int) assert isinstance(args[2], list) caller = inspect.stack()[1] callerText = u"\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def displayChecker(display, cmdIndex): result = display.checkText(*args) if result is not None: output = callerText + u' at index ' + str(cmdIndex) + result if self.cursesScreen.movie: print(output) else: self.fail(output) return None return displayChecker def displayFindCheck(self, *args): """ Args: find_string (unicode): locate this string. check_string (unicode): verify this follows |find_string|. """ assert len(args) == 2 assert isinstance(args[0], unicode) assert isinstance(args[1], unicode) caller = inspect.stack()[1] callerText = u"\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def displayFindChecker(display, cmdIndex): find_string, check_string = args row, col = display.findText(find_string) result = display.checkText(row, col + len(find_string), [check_string]) if result is not None: output = callerText + u' at index ' + str(cmdIndex) + result if self.cursesScreen.movie: print(output) else: self.fail(output) return None return displayFindChecker def displayCheckNot(self, *args): """ Verify that the display does not match. """ assert isinstance(args[0], int) caller = inspect.stack()[1] callerText = "\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def displayCheckerNot(display, cmdIndex): result = display.checkText(*args) if result is None: output = callerText + u' at index ' + str(cmdIndex) if self.cursesScreen.movie: print(output) else: self.fail(output) return None return displayCheckerNot def displayCheckStyle(self, *args): """*args are (row, col, height, width, colorPair).""" (row, col, height, width, colorPair) = args assert height != 0 assert width != 0 assert colorPair is not None caller = inspect.stack()[1] callerText = u"\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def displayStyleChecker(display, cmdIndex): result = display.checkStyle(*args) if result is not None: output = callerText + u' at index ' + str(cmdIndex) + result if self.cursesScreen.movie: print(output) else: self.fail(output) return None return displayStyleChecker def findText(self, screenText): """Locate |screenText| on the display, returning row, col. """ return self.cursesScreen.test_find_text(screenText) def cursorCheck(self, expectedRow, expectedCol): assert isinstance(expectedRow, int) assert isinstance(expectedCol, int) caller = inspect.stack()[1] callerText = u"in %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def cursorChecker(display, cmdIndex): if self.cursesScreen.movie: return None win = self.prg.programWindow.focusedWindow tb = win.textBuffer screenRow, screenCol = self.cursesScreen.getyx() self.assertEqual((win.top + tb.penRow - win.scrollRow, win.left + tb.penCol - win.scrollCol), (screenRow, screenCol), callerText + u"internal mismatch") self.assertEqual((expectedRow, expectedCol), (screenRow, screenCol), callerText) return None return cursorChecker def pathToSample(self, relPath): path = os.path.dirname(os.path.dirname(__file__)) return os.path.join(path, u"sample", relPath) def prefCheck(self, *args): assert isinstance(args[0], unicode) assert isinstance(args[1], unicode) assert isinstance(args[2], (int, bool)) caller = inspect.stack()[1] callerText = u"\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def prefChecker(display, cmdIndex): result = self.prg.prefs.category(args[0])[args[1]] if result != args[2]: output = u"%s at index %s, expected %r, found %r" % ( callerText, unicode(cmdIndex), args[2], result) if self.cursesScreen.movie: print(output) else: self.fail(output) return None return prefChecker def printParserState(self): caller = inspect.stack()[1] callerText = u"in %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def redoChain(display, cmdIndex): print("Parser state", callerText) tb = self.prg.programWindow.focusedWindow.textBuffer tb.parser.debugLog(print, tb.parser.data) return None return redoChain def printRedoState(self): caller = inspect.stack()[1] callerText = u"in %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def redoState(display, cmdIndex): print("Redo state", callerText) tb = self.prg.programWindow.focusedWindow.textBuffer tb.printRedoState(print) return None return redoState def resizeScreen(self, rows, cols): assert isinstance(rows, int) assert isinstance(cols, int) def setScreenSize(display, cmdIndex): self.cursesScreen.fakeDisplay.setScreenSize(rows, cols) return curses.KEY_RESIZE return setScreenSize def setClipboard(self, text): assert isinstance(text, str) caller = inspect.stack()[1] callerText = u"in %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def copyToClipboard(display, cmdIndex): self.assertTrue(self.prg.clipboard.copy, callerText) self.prg.clipboard.copy(text) return None return copyToClipboard def setMovieMode(self, enabled): self.cursesScreen.movie = enabled self.cursesScreen.fakeInput.isVerbose = enabled def writeText(self, text): assert isinstance(text, unicode), type(text) caller = inspect.stack()[1] callerText = u"in %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def copyToClipboard(display, cmdIndex): self.assertTrue(self.prg.clipboard.copy, callerText) self.prg.clipboard.copy(text) return app.curses_util.CTRL_V return copyToClipboard def checkNotReached(self, depth=1): """Check that this step doesn't occur. E.g. verify the app exited. Args: depth (int): how many stack frames up to report as the error location. """ caller = inspect.stack()[depth] callerText = u"\n %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def checkEndOfInputs(display, cmdIndex): self.fail(callerText + "\n Unexpectedly ran out of fake inputs. Consider adding" " CTRL_Q (and 'n' if necessary).") return None return checkEndOfInputs def runWithFakeInputs(self, fakeInputs, argv=None): assert hasattr(fakeInputs, "__getitem__") or hasattr( fakeInputs, "__iter__") if argv is None: argv = ["no_argv"] sys.argv = argv self.cursesScreen.setFakeInputs(fakeInputs + [ self.checkNotReached(2), ]) self.assertTrue(self.prg) self.assertFalse(self.prg.exiting) self.prg.run() #curses.printFakeDisplay() if app.ci_program.userConsoleMessage: message = app.ci_program.userConsoleMessage app.ci_program.userConsoleMessage = None self.fail(message) # Check that the application is closed down (don't leave it running # across tests). self.assertTrue(self.prg.exiting) self.assertEqual(self.cursesScreen.fakeInput.inputsIndex, len(fakeInputs) - 1) # Handy for debugging. if 0: caller = inspect.stack()[1] callerText = u" %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) print(u'\n-------- finished', callerText) def runWithTestFile(self, kTestFile, fakeInputs): if os.path.isfile(kTestFile): os.unlink(kTestFile) self.assertFalse(os.path.isfile(kTestFile)) self.runWithFakeInputs(fakeInputs, ["ci_test_program", kTestFile]) def selectionDocumentCheck(self, expectedPenRow, expectedPenCol, expectedMarkerRow, expectedMarkerCol, expectedMode): caller = inspect.stack()[1] callerText = u"in %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def checker(display, cmdIndex): selection = self.prg.getDocumentSelection() self.assertEqual((expectedPenRow, expectedPenCol, expectedMarkerRow, expectedMarkerCol, expectedMode), selection, callerText) return checker def selectionCheck(self, expectedPenRow, expectedPenCol, expectedMarkerRow, expectedMarkerCol, expectedMode): caller = inspect.stack()[1] callerText = u"in %s:%s:%s(): " % (os.path.split(caller[1])[1], caller[2], caller[3]) def checker(display, cmdIndex): selection = self.prg.getSelection() self.assertEqual((expectedPenRow, expectedPenCol, expectedMarkerRow, expectedMarkerCol, expectedMode), selection, callerText) return checker def tearDown(self): # Disable mouse tracking in xterm. sys.stdout.write(u"\033[?1002l") # Disable Bracketed Paste Mode. sys.stdout.write(u"\033[?2004l")