-
-
Notifications
You must be signed in to change notification settings - Fork 654
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System test with robot framework #8481
Conversation
Requires: robotFramework - the system test framework pyautogui - for controlling keyboard/mouse To install: pip install robotframework pip install pyautogui
Ensure this is run from a cmd prompt. Other shells may cause issues, for instance, NVDA does not get correct focus when run from cygwin.
Remote server implemenation and external abstracted library.
Running from tests/system/ did not reduce the number of suites mentioned in Robots reports
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Congratulations with this huge work. Note that I haven't yet looked into the system testing framework itself, as it looks a bit complex and I'd have to dive into the matter a little more. Some initial remarks though.
appveyor.yml
Outdated
@@ -109,6 +111,58 @@ build_script: | |||
- 7z a -tzip -r ..\output\symbols.zip *.dl_ *.ex_ *.pd_ | |||
- cd .. | |||
|
|||
before_test: | |||
- py -m pip install robotframework | |||
- py -m pip install robotremoteserver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a particular reason why this installed twice, also after running submodule update?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a mistake, for future reference, the other is on line 65
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, I remember now. For a little while we were packaging robotRemoteServer with the install of NVDA so that it could be used by the addon. Now we package it within the addon. I will remove this line.
appveyor.yml
Outdated
- py -m pip install robotframework | ||
- py -m pip install robotremoteserver | ||
- py -m pip install pyautogui | ||
- py -m pip install nose |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also install them in one run, I'd imagine that could be slightly faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes perhaps so, I'm not sure how much time this will save. It might even make sense to convert this to a requirements file in source control. It would make it easier for developers to ready their own systems to run the system tests.
import extensionPoints | ||
|
||
# inform those who want to know that NVDA has finished starting up. | ||
postNvdaStartup = extensionPoints.Action() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that, according to earlier convention discussions we had, this should be post_NVDAStartup. Also note that it might make sense to write NVDA in upper case, as in NVDAObjects, NVDAHelper
source/speech.py
Outdated
import extensionPoints | ||
|
||
# inform those who want to know that there is new speech | ||
preSpeech = extensionPoints.Action() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this might have to be pre_speech according to the conventions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the conventions were discussed on PR #7606, though the underscores were dropped from that diff. I would be happy with the underscores and certainly want to keep it consistent, perhaps it should be made official on the wiki so that everyone is on the same page. @josephsl would you be on board with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are the performance impacts for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There may be a minor performance impact here, very little compared to the rest of what happens before speech goes to the synth.
Which raises a bigger question about the approach of using an extension point to gather the speech, should we be capturing the behaviour that happens between this line, and the eventual getSynth().speak(speechSequence)
?
I think we probably should, otherwise there is no way for us to test things like auto language / dialect switching or symbol levels.
Perhaps, instead of an extension point for speech, the addon should install and select a dummy synthesizer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
both could be useful actually. The synth captures the output and the extension point the first call.
@@ -0,0 +1,14 @@ | |||
WELCOME_DIALOG_TEXT = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might break if a user runs the system tests with a system language other than English, in which the user default language differs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's likely that there will be lots of ways that the tests will not pass on various different systems. To start with, we will be using AppVeyor as the target. Over time, if people are trying to run the tests locally, then the tests will become more tolerant of variations in the underlying system.
Ah I see so keep on reporting funnies then.
Brian
|
Hi, I already have tested this branch before this PR was sent. Regarding variable names: as @LeonarddeR pointed out, there are inconsistencies throughout NVDA as to whether underscores should be included and what not. The best precedence for underscores is event_* definitions, which clearly denotes that the code is part of an event. Also, there are cases where camel case is used for actions, such as postSave method in settings panels. Personally, I have mixed feelings about it. On one hand, keeping it camel case makes it clear that we want to keep things consistent. adding an underscore may help with readability, but people will still be confused as to what is what. From programming perspective, it doesn't matter as long as the target machine is aware of its states and what it is doing at the moment. There are other angles to look at. I do know that this is the first time I'm applying rhetorical and communication principles in a pull request, but I think it is time that I mention this due to implications of this change: I believe that source code is a communicative object. This means people can glimpse the following from source code: mindset, organizational culture, history and direction of a project, dominant messages the project wants to send via code and what not. A well thought-out source code serves to indicate not only the intentions of the developer who wrote it, but also the overall culture and messages an organization has and wishes to spread. |
Hi, Based on what I wrote above, I propose keeping it camel case for now until we get consensus from other stakeholders that we should change this. The chief reason for this is in order to always remember that community is important. Personally, I'll follow the majority opinion if it benefits not only devleopers, but also others. Thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very cool. a few comments.
@@ -20,6 +20,7 @@ environment: | |||
|
|||
init: | |||
- ps: | | |||
iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a comment here describing why this is downloaded might help potential future NV access devs if we lose access to the knowledge of why this was originally done.
if(!$env:release) { | ||
$nvdaLauncherFile+="_snapshot" | ||
} | ||
$nvdaLauncherFile+="_${env:version}.exe" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a couple of things.
- is this compatible with try builds?
- What if we offered a python function or something that gave us a launcher file name so we didn't have to do this, and later build the file name in appVeyor? we'd just call py versionBuilder.py, which would import things and use the environment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the tests run on try builds, against an install of NVDA. I can't speak to your second question, it might be best for @michaelDCurran to answer that one.
import extensionPoints | ||
|
||
# inform those who want to know that NVDA has finished starting up. | ||
postNvdaStartup = extensionPoints.Action() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be added to the changes file. This is useful for addons potentially.
source/speech.py
Outdated
import extensionPoints | ||
|
||
# inform those who want to know that there is new speech | ||
preSpeech = extensionPoints.Action() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are the performance impacts for this?
@@ -20,6 +20,11 @@ unitTests = env.SConscript("unit/sconscript", exports=["env"]) | |||
env.Depends(unitTests, sourceDir) | |||
env.AlwaysBuild(unitTests) | |||
|
|||
systemTests = env.SConscript("system/sconscript", exports=["env"]) | |||
env.Depends(systemTests, sourceDir) | |||
env.AlwaysBuild(systemTests) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are these always built?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment, the system tests only run if you explicitly give the option when starting scons. Then we want the tests to run, even if there has been no changes to dependencies.
_pJoin(systemTestSourceDir, "libraries", systemTestSpyAddonName+".py"), | ||
_pJoin(profileSysTestSpyPackageStagingDir, "__init__.py") | ||
) | ||
opSys.copy_file( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the difference between this and shutil.copyfile?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Within the system tests I have preferred to use methods available in the system test framework, since they tend to provide more robustness, checks, and reporting when something is not right. This makes it easier to track down the problem in the system test log. Even when things go correctly, there are entries (including timestamps) added to the system test log which can be quite helpful.
import threading | ||
from systemTestUtils import _blockUntilConditionMet | ||
from logHandler import log | ||
from time import clock as _timer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this name get changed to _timer instead of _clock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No real reason, except that originally I was importing from timeit import default_timer as _timer
|
||
# Private helper methods | ||
def _flattenCommandsSeparatingWithNewline(self, commandArray): | ||
f = [c for commands in commandArray for newlineJoined in [commands, [u"\n"]] for c in newlineJoined] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a work of art.
def _getJoinedBaseStringsFromCommands(self, speechCommandArray): | ||
wsChars = whitespaceMinusSlashN | ||
baseStrings = [c.strip(wsChars) for c in speechCommandArray if isinstance(c, basestring)] | ||
return ''.join(baseStrings).strip() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this need restripped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It possibly doesn't need to be there. There is the possibility of a trailing/leading newline character, we want to preserve newline characters within the string. But not at the ends, since they are harder to notice, and can make defining the expected text a frustrating experience.
return self._spy.getSpeechSinceIndex(speechIndex) | ||
|
||
def reset_all_speech_index(self): | ||
self._allSpeechStartIndex = self._spy.getIndexOfLastSpeech() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name suggests this should reset the speech. Does this actually do anything but return the last index?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, perhaps it's because its not clear that "all speech" is a grouping. This resets the "all speech" index, meaning that when you next call get_all_speech
it will return any new speech added after the index was reset. It's important that we don't actually clear the internal buffer, since tests may need to hold on to speech indexes and be able to refer to them later, without them being invalidated.
@josephsl and @LeonarddeR re: underscore vs camel-case @josephsl I agree with you that source code is a communicative object, and that it's written for people not machines (otherwise we would all be writing in some form of machine code). Code is read many more times than it is written, and as such, being clear is important. My vote would be on using underscore, because it more clearly defines the pattern. While aiming for consistency across the code base is important, consistency for a paradigm is more so. I couldn't find any opposition to the use of underscores in #7606 where we have already had an extended discussion of this topic. So as the next action, I suggest we all convert our various extension point introducing PR's to use underscores, and I will add a wiki page to document the decision and rationale for the naming scheme. |
Hi, I’ll abide by this decision (will modify the PR once the wiki goes up). Thanks.
|
@josephsl https://github.com/nvaccess/nvda/wiki/Contributing#identifier-names has now been edited. |
Hi, applying to #7606 now. Thanks.
|
This captures all NVDA manipulation of speech before it is given to synthesizers.
be440a9
Link to issue number:
#708
Summary of the issue:
Introduction of a system testing framework for NVDA.
Description of how this pull request fixes the issue:
Based on the Robot Framework, tests are written in
robot
files. The tests are discovered and run by the Framework. Each tests sets up an environment for NVDA to run in, starts NVDA as a separate process, and connects to a server running as an NVDA addon. The addon provides access to NVDA internals eg speech commands. The test code then interacts with NVDA as a user would by sending keypresses and checks that the behaviour is as expected using data from the addon.For more information, see the readme in this PR.
Testing performed:
Run locally against source and installed NVDA, and on appveyor
Known issues with pull request:
Tests currently only look at simple startup/shutdown behaviour.
Developers will need to manually install dependencies via pip in order to be able to run the system tests locally.
Change log entry:
Developers:
A system test framework has been introduced for NVDA. (#708)
A new extension point action for NVDA post startup is now available.