Repository: nvaccess/nvda

Issue:
Can't activate Input Gestures Dialog with Focus40 Blue Braille Display
### Steps to reproduce:
> A list of the steps you take to demonstrate the problem. 

1. Go to nVDA by pressing NVDAKey+n and then choose Preferences
2. Activate the Input Gestures dialog from the Settings submenu.
3. A sound is heard and no dialog box opens. When you try to perform the same command or try to open another NVDA dialog box the error message "Another NVDA dialog ox is open" is shown and a restart of NVDA is necessary to be able to open a different dialog box.


### Expected behavior:

The Input Gestures dialog box should open.

Windows 10 Fall Creators Update, NVDA Master from today. Log follows:

```
Input: kb(desktop):rightArrow
IO - speech.speak (21:03:17.127):
Speaking [u'General settings...  g']
DEBUG - NVDAObjects.NVDAObject._get_placeholder (21:03:17.127):
Potential unimplemented child class: <NVDAObjects.IAccessible.IAccessible object at 0x06ED8F90>
DEBUG - NVDAObjects.NVDAObject._get_placeholder (21:03:17.127):
Potential unimplemented child class: <NVDAObjects.IAccessible.MenuItem object at 0x06ED8F50>
IO - braille.BrailleBuffer.update (21:03:17.127):
Braille regions text: [u'Preferences mnu ', u'General settings... g']
IO - braille.BrailleHandler.update (21:03:17.127):
Braille window dots: 6 1234 1235 15 124 15 1235 15 1345 14 15 234 - 134 1345 136 - 6 1245 15 1345 15 1235 1 123 - 234 15 2345 2345 24 1345 1245 234 256 256 256 - 1245
IO - inputCore.InputManager.executeGesture (21:03:19.118):
Input: kb(desktop):upArrow
IO - speech.speak (21:03:19.132):
Speaking [u'Input gestures...  n']
DEBUG - NVDAObjects.NVDAObject._get_placeholder (21:03:19.132):
Potential unimplemented child class: <NVDAObjects.IAccessible.MenuItem object at 0x06ED8BF0>
IO - braille.BrailleBuffer.update (21:03:19.132):
Braille regions text: [u'Preferences mnu ', u'Input gestures... n']
IO - braille.BrailleHandler.update (21:03:19.132):
Braille window dots: 6 24 1345 1234 136 2345 - 1245 15 234 2345 136 1235 15 234 256 256 256 - 1345
IO - inputCore.InputManager.executeGesture (21:03:20.460):
Input: kb(desktop):enter
ERROR - unhandled exception (21:03:20.486):
Traceback (most recent call last):
  File "gui\__init__.pyc", line 252, in onInputGesturesCommand
  File "gui\__init__.pyc", line 184, in _popupSettingsDialog
  File "gui\settingsDialogs.pyc", line 81, in __init__
  File "gui\settingsDialogs.pyc", line 1993, in makeSettings
  File "gui\settingsDialogs.pyc", line 2034, in populateTree
  File "gui\settingsDialogs.pyc", line 2048, in _formatGesture
  File "inputCore.pyc", line 769, in getDisplayTextForGestureIdentifier
  File "braille.pyc", line 2122, in getDisplayTextForIdentifier
ValueError: Invalid identifier provided: br(freedomscientific]:leftbumperbarup
IO - speech.speak (21:03:20.653):
Speaking [u'New Issue \xb7 nvaccess/nvda - Mozilla Firefox']
IO - braille.BrailleBuffer.update (21:03:20.654):
Braille regions text: [u'New Issue \xb7 nvaccess/nvda - Mozilla Firefox']
IO - braille.BrailleHandler.update (21:03:20.654):
Braille window dots: 6 1345 15 2456 - 6 24 234 234 136 15 - 4 16 - 1345 1236 1 14 14 15 234 234 456 34 1345 1236 145 1 - 36 -
IO - braille.BrailleBuffer.update (21:03:20.871):
Braille regions text: [u'mln edt ### Steps to reproduce: ']
IO - braille.BrailleHandler.update (21:03:20.872):
Braille window dots: 134 123 1345 - 15 145 2345 - 456 1456 456 1456 456 1456 - 6 234 2345 15 1234 234 - 2345 135 - 1235 15 1234 1235 135 145 136 14 15 25 -
IO - braille.BrailleHandler.update (21:03:20.874):
Braille window dots: 134 123 1345 - 15 145 2345 - 456 1456 456 1456 456 1456 - 6 234 2345 15 1234 234 - 2345 135 - 1235 15 1234 1235 135 145 136 14 15 25 -
IO - speech.speak (21:03:20.880):
Speaking [u'New Issue \xb7 nvaccess/nvda  document']
IO - speech.speak (21:03:20.890):
Speaking [u'Comment body  edit  multi line']
IO - speech.speak (21:03:20.895):
Speaking [u'blank']
IO - braille.BrailleBuffer.update (21:03:20.901):
```


Discussion:
Ugh, this is caused by #7517. Let me look into this later this week.

Do you by any chance have custom gestures for your Focus blue?
Ah, this is actually not a bug in #7517, but a typo in the freedomscientific driver that has now been cached. Nevertheless, this ValueError should not stop the input gestures dialog from launching. I will create a pr either tomorrow or thursday.

Resolving patch:
diff --git a/source/braille.py b/source/braille.py
--- a/source/braille.py
+++ b/source/braille.py
@@ -285,7 +285,13 @@ def NVDAObjectHasUsefulText(obj):
 def _getDisplayDriver(name):
 	return __import__("brailleDisplayDrivers.%s" % name, globals(), locals(), ("brailleDisplayDrivers",)).BrailleDisplayDriver
 
-def getDisplayList():
+def getDisplayList(excludeNegativeChecks=True):
+	"""Gets a list of available display driver names with their descriptions.
+	@param excludeNegativeChecks: excludes all drivers for which the check method returns C{False}.
+	@type excludeNegativeChecks: bool
+	@return: list of tuples with driver names and descriptions.
+	@rtype: [(str,unicode)]
+	"""
 	displayList = []
 	# The display that should be placed at the end of the list.
 	lastDisplay = None
@@ -299,7 +305,7 @@ def getDisplayList():
 				exc_info=True)
 			continue
 		try:
-			if display.check():
+			if not excludeNegativeChecks or display.check():
 				if display.name == "noBraille":
 					lastDisplay = (display.name, display.description)
 				else:
@@ -2119,7 +2125,8 @@ def _get_keyNames(self):
 	def getDisplayTextForIdentifier(cls, identifier):
 		idParts = cls.ID_PARTS_REGEX.search(identifier)
 		if not idParts:
-			raise ValueError("Invalid identifier provided: %s"%identifier)
+			log.error("Invalid braille gesture identifier: %s"%identifier)
+			return handler.display.description, "malformed:%s"%identifier
 		modelName = idParts.group(3)
 		key = idParts.group(4)
 		if modelName: # The identifier contains a model name
diff --git a/source/brailleDisplayDrivers/freedomScientific.py b/source/brailleDisplayDrivers/freedomScientific.py
--- a/source/brailleDisplayDrivers/freedomScientific.py
+++ b/source/brailleDisplayDrivers/freedomScientific.py
@@ -229,7 +229,7 @@ def script_toggleRightWizWheelAction(self,gesture):
 	gestureMap=inputCore.GlobalGestureMap({
 		"globalCommands.GlobalCommands" : {
 			"braille_routeTo":("br(freedomScientific):routing",),
-			"braille_scrollBack" : ("br(freedomScientific):leftAdvanceBar", "br(freedomScientific]:leftBumperBarUp","br(freedomScientific):rightBumperBarUp",),
+			"braille_scrollBack" : ("br(freedomScientific):leftAdvanceBar", "br(freedomScientific):leftBumperBarUp","br(freedomScientific):rightBumperBarUp",),
 			"braille_scrollForward" : ("br(freedomScientific):rightAdvanceBar","br(freedomScientific):leftBumperBarDown","br(freedomScientific):rightBumperBarDown",),
 			"braille_previousLine" : ("br(freedomScientific):leftRockerBarUp", "br(freedomScientific):rightRockerBarUp",),
 			"braille_nextLine" : ("br(freedomScientific):leftRockerBarDown", "br(freedomScientific):rightRockerBarDown",),


Test patch:
diff --git a/tests/unit/test_brailleDisplayDrivers.py b/tests/unit/test_brailleDisplayDrivers.py
new file mode 100644
--- /dev/null
+++ b/tests/unit/test_brailleDisplayDrivers.py
@@ -0,0 +1,25 @@
+#tests/unit/test_brailleDisplayDrivers.py
+#A part of NonVisual Desktop Access (NVDA)
+#This file is covered by the GNU General Public License.
+#See the file COPYING for more details.
+#Copyright (C) 2017 NV Access Limited, Leonard de Ruijter
+
+"""Unit tests for braille display drivers.
+"""
+
+import unittest
+import braille
+
+class TestGestureMap(unittest.TestCase):
+	"""Tests the integrity of braille display driver gesture maps."""
+
+	def test_identifiers(self):
+		"""Checks whether all defined braille display gestures contain valid braille display key identifiers."""
+		for name, description in braille.getDisplayList(excludeNegativeChecks=False):
+			driver=braille._getDisplayDriver(name)
+			gmap=driver.gestureMap
+			if not gmap:
+				continue
+			for cls, gesture, scriptName in gmap.getScriptsForAllGestures():
+				if gesture.startswith("br"):
+					self.assertRegexpMatches(gesture, braille.BrailleDisplayGesture.ID_PARTS_REGEX)