bpo-29812: Improve testing of token and tokenize#681
Conversation
|
@ammaraskar, thanks for your PR! By analyzing the history of the files in this pull request, we identified @serhiy-storchaka, @tiran and @1st1 to be potential reviewers. |
5c9e66a to
dbce4b5
Compare
|
Weirdly enough this fails on AppVeyor (which is Windows, I believe). I'm not sure if this is something wrong with the test or an actual problem on windows, I'll look into it tomorrow when I have access to a windows computer. |
| @@ -0,0 +1,52 @@ | |||
| import token | |||
| import unittest | |||
| from test import support | |||
There was a problem hiding this comment.
Please group stdlib modules together and sort them alphabetically.
| import sys | ||
| import subprocess | ||
|
|
||
| TOKEN_FILE = support.findfile('token.py') |
There was a problem hiding this comment.
Style nit: Don't add additional spaces before =.
| rc, stderr = self._generate_tokens(os.devnull, NONEXISTENT_FILE) | ||
| self.assertNotEqual(rc, 0) | ||
| self.assertIn(b'I/O error', stderr) | ||
| self.assertIn(bytes(NONEXISTENT_FILE, encoding='ascii'), stderr) |
There was a problem hiding this comment.
NONEXISTENT_FILE doesn't contain any non-ASCII chars. Do we really need encoding='ascii'?
Also, I prefer to have a nonexistent_file local variable in the test instead of a constant.
There was a problem hiding this comment.
The encoding is required when passing a string in python3: https://docs.python.org/3/library/functions.html#bytes
https://docs.python.org/3/library/functions.html#bytearray
The reason its a b'string' is because it needs to be passed as an argument to Popen, changing it to a local might result in an easier fix for this though.
| self.assertIn(bytes(NONEXISTENT_FILE, encoding='ascii'), stderr) | ||
|
|
||
|
|
||
| if __name__ == "__main__": |
There was a problem hiding this comment.
Style use: Please use single quotes where possible.
There was a problem hiding this comment.
Will do, for reference, this was copied from https://github.com/python/cpython/blob/master/Lib/test/test_keyword.py
| stderr = proc.communicate()[1] | ||
| return proc.returncode, stderr | ||
|
|
||
| @unittest.skipIf(not os.path.exists(TOKEN_FILE), |
There was a problem hiding this comment.
I'd replace
@unittest.skipIf(not condition)with
@unittest.skipUnless(condition)| 'test only works from source build directory') | ||
| def test_real_token_file(self): | ||
| self._copy_file_without_generated_tokens(TOKEN_FILE, TEST_PY_FILE) | ||
| self.addCleanup(support.unlink, TEST_PY_FILE) |
There was a problem hiding this comment.
You can probably move the addCleanup call to inside of _copy_file_without_generated_tokens method.
| TEST_PY_FILE)) | ||
| self.assertTrue(filecmp.cmp(TOKEN_FILE, TEST_PY_FILE)) | ||
|
|
||
| def test_missing_output_file_causes_Error(self): |
There was a problem hiding this comment.
Error -> error or exception
| token.tok_name[token.ENDMARKER]) | ||
|
|
||
| def test_all_literal_tokens(self): | ||
| NON_LITERALS = {token.ENDMARKER, token.NAME, token.NUMBER, |
There was a problem hiding this comment.
NON_LITERALS -> non_literals or non_literal_tokens
| token.tok_name[token.ENDMARKER]) | ||
|
|
||
| def test_all_literal_tokens(self): | ||
| NON_LITERALS = {token.ENDMARKER, token.NAME, token.NUMBER, |
There was a problem hiding this comment.
Also, I find hanging indents much more readable and they will make future diffs less noisy:
non_literal_tokens = {
token.STRING, token.NEWLINE, token.INDENT,
...,
# You can add another line without having to modify the '}' so diffs will be less noisy.
token.N_TOKENS, token.NT_OFFSET,
}| continue | ||
| name = tok_name[tok] | ||
| self.assertIn(tok, EXACT_TOKEN_TYPES.values(), | ||
| "Literal token " + name + " not in EXACT_TOKEN_TYPES") |
There was a problem hiding this comment.
This is OK, but an alternative solution would be to use self.subTest().
963035d to
9b357ae
Compare
|
Oh also, I tried to look into what was calling the AppVeyor failure when comparing the files. From what I can tell its because os.stat is returning an incorrect value for st_size on the test environment or something https://ci.appveyor.com/project/ammaraskar/cpython/build/1.0.9#L3949 Doing a file content comparison causes it to pass. Nevermind, I don't think this is an incorrect return since the offset of the size is exactly the number of lines, I think this has to do with line endings. |
Adds a token.py generation test, and a cross-check between the token and tokenize modules. The generation test is akin to the current automated file generator tests in test_keyword and test_symbol. The newly added check in tokenize ensures that all the literal tokens in token such as ':', '...', '@=' have a corresponding entry within the EXACT_TOKEN_TYPES dict in tokenize.
Currently this code does not work on windows
when the skeleton file has unix line endings
because fp.write('\n') will have the LF
changed to CRLF due to python's newline
interpolation.
|
Hey @berkerpeksag, could you take a look at this, I updated it fixing the concerns in your review. @bitdancer could I also get a review from you seeing as you made the issue on the tracker for this? |
| import os | ||
| import subprocess | ||
| import sys | ||
| from test import support |
There was a problem hiding this comment.
Please split stdlib and non-stdlib modules.
| self.addCleanup(support.unlink, dest_file) | ||
|
|
||
| def _generate_tokens(self, skeleton_file, target_token_py_file): | ||
| proc = subprocess.Popen([sys.executable, |
There was a problem hiding this comment.
Can't we use the new subprocess.run() interface?
| with fp: | ||
| format = fp.read().split("\n") | ||
| format = fp.readlines() | ||
| nl = format[0][len(format[0].strip()):] if format else '\n' |
There was a problem hiding this comment.
I'd use more descriptive name like newline.
| continue | ||
| name = tok_name[tok] | ||
| with self.subTest(name=name): | ||
| self.assertIn(tok, EXACT_TOKEN_TYPES.values()) |
There was a problem hiding this comment.
Looking at CI logs of an earlier commit, I wonder if we should make the following message clearer:
======================================================================
FAIL: test_all_literal_tokens (test.test_tokenize.TestTokenize) (name='NL')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/travis/build/python/cpython/Lib/test/test_tokenize.py", line 1368, in test_all_literal_tokens
self.assertIn(tok, EXACT_TOKEN_TYPES.values())
AssertionError: 58 not found in dict_values([7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 52, 51, 49, 50])
|
Thanks for your review @berkerpeksag. Closing this as per discussion on bpo. |
Adds a token.py generation test, and a cross-check between the token and tokenize modules.
The generation test is akin to the current automated file generator tests in test_keyword and test_symbol. The newly added check in tokenize ensures that all the literal tokens in token such as ':', '...', '@=' have a corresponding entry within the EXACT_TOKEN_TYPES dict in tokenize.