Parse out testing_done field from commit messages
Review Request #9890 — Created April 24, 2018 and discarded
Previously, one had to manually add comments in the testing done section
online for any testing they did for a given patch. However, now if one
was to post something with"Testing done:"
in the commit message it will
split that out into the Testing Done section when posting.
Ran unit tests.
Created a test repository on a local server and posted the following review
requests testing cases with:
1.Summary, Description and Testing Done
2.Summary and Description
With a previous Summary, Description and Testing Done:
1.Update Summary
2.Update Description
3.Update Testing Done
With a previous Summary and Description (no Testing Done):
1.Add Testing Done
Testing different ways of 'Testing Done' inputs:
1.'testing done'
2.'testing done:'
3.'Testing done'
4.'Testing done:'
5.'testing Done'
6.'testing Done:'
7.'tEstIng DOnE:'
Description | From | Last Updated |
---|---|---|
Summary suggestion: "Parse out testing_done field from commit messages" |
brennie | |
Can you write your testing done without "I" statements, e.g. "Ran unit tests." "Created a test repository..." |
brennie | |
What about the case of a commit message like: Testing done: foo |
brennie | |
What happens in the case of a commit message like: A commit message Testing done: Testing done: I did tests |
brennie | |
You seem to have posted two changes here. |
brennie | |
Can you re-flow this as: result = { 'key': 'value', } |
brennie | |
How about: "Determine if the commit message contains testing done information." |
brennie | |
You can add: only_testing_done = i == 1 and remove the computation for it below. This will be a cheaper … |
brennie | |
Comments should never have first-person statements. They are not the justification for your changes -- the commit message is. You … |
brennie | |
These sorts of comments are unnecessary because they can be inferred from the if statements. |
brennie | |
Comments should be full sentences: they should begin with a capital letter and end with a period. |
brennie | |
Missing module-level docstring. |
brennie | |
Docstring should be of the format: "Unit tests for rbtools.clients.SCMClient" |
brennie | |
Docstring should be of the form """Testing SCMClient.get_commit_message when ...""" If it goes over 79 characters and wraps, the trailing … |
brennie | |
ALL_CAPS is reserved for module-level and class-level constants. This would be fine as commit_message |
brennie | |
Single triple-quotes ('''). Double quotes are reserved for docstrings only. Same below. |
brennie | |
Only module-level and class-level constnats should be ALL_CAPS. This is fine as commit_message. |
brennie | |
We're using these immediately, so I don't know that this is necessary. Same below. |
brennie | |
We are doing this in every test. We can do: def setUp(self): super(SCMBaseClientTests, self).setUp() self.client = SCMClient() and use self.client … |
brennie | |
Instead of this, how about: self.assertEqual( client.get_commit_message([]), { 'summary': lines[0], 'description': '', 'testing_done': lines[3], }) That way we test all … |
brennie | |
"commit message" Missing a period. |
brennie | |
"commit message" Missing a period. |
brennie | |
Is this still a todo? |
brennie | |
Undo this. |
brennie | |
"summary, description, or testing done" |
brennie | |
instead of saying the field names over again, how about: "override the options for each guessed field" (in lieu of … |
brennie | |
Leftover debugging? :) |
brennie | |
We're doing this thrice, so we should probably refactor this into a method. |
brennie | |
"Return" over get. Can you add args/ returns to this docstring? On second thought, you may want to hold off … |
brennie | |
``summary``, ``description``, and ``testing_done`` |
brennie | |
:py:class:`difflib.SequenceMatcher` |
brennie | |
:py:class:`Score` |
brennie | |
I assume {field}_pair is a tuple, so can we do: summary_score = SequenceMatcher(None, *summary_pair).ratio() description_score = SequenceMatcher(None, *description_pair).ratio() testing_done_score = … |
brennie | |
This needs to be updated with args, returns, etc but needs to be based of David's docstring change. |
brennie | |
Leftover testing code? :) |
brennie | |
Can we format this as: result = { 'summary': lines[0], 'description': b'', 'testing_done': b'', } |
brennie | |
Blnank line between these. |
brennie | |
This comment is redundant. |
brennie | |
This needs to be in the if r.match(line) block above. |
brennie | |
Instead of doing .strip() on each line, how about we remove those calls and do: return { key: value.strip() for … |
brennie | |
We've already set summary. |
brennie | |
We've already set summary. |
brennie | |
We've already set summary. |
brennie | |
We've already set summary. |
brennie | |
Module docstrings need to be first, e.g. """Unit tests for rbtools.clients.SCMClient.""" from __future__ import unicode_literals # .... |
brennie | |
No period. |
brennie | |
No period. |
brennie | |
No period. |
brennie | |
No period. |
brennie | |
No period. |
brennie | |
No period. |
brennie | |
No period. |
brennie | |
Mind adding a comment same as above to indicate what this is doing? Also those comments are missing periods, could … |
brennie | |
No blank line here. |
brennie | |
Leftover debugging? |
brennie | |
Docstring summaries should be written in the imperitive mood (i.e., they give an order or command). In this case, it … |
brennie | |
Revert this too. |
brennie | |
Can you revert changes to this file? See my comment in rbtools/utils/review_request.py for the reasoning. |
brennie | |
Can you revert the changes to this file? I don't think we want to match on testing done, since a … |
brennie | |
"is description and [i:] is testing done" |
brennie | |
This is unnecessary with the above assignment. |
brennie | |
We are doing this when we assign result above. |
brennie | |
WIth the above comment, this would be elif not summary |
brennie | |
Docstrings should be in the following format: """Single line summary. Multi-line description. """ This can just be """Unit tests for … |
brennie | |
Blank line between these. |
brennie | |
unicode. |
brennie | |
Missing type: bool |
brennie | |
bool |
brennie | |
) on previous line |
brennie | |
) on previous line |
brennie | |
) on previous line |
brennie | |
Instead of enumerating all these cases, how about: if guess_summary: self.options.summary = guessed_summary if guess_description: if not guess_summary: # If … |
brennie | |
Can you revert all changes to this file? git checkout master -- rbtools/utils/match_score.py |
brennie | |
Can you revert all changes to this file? git checkout master -- rbtools/utils/review_request.py |
brennie | |
We can optimize this to only check for a testing done when we have a description. We also don't need … |
brennie | |
Why is this lines[0:] now? This used to be lines[1:]. As it stands, i doesn't match up with the actual … |
brennie | |
# We have a clear "Summary\n\nDescription"-style commit message so we can split out the description. |
brennie | |
What if len(lines) == 2 and lines[1] is not empty, e.g. This is a summary this is a description This … |
brennie | |
Commends should be a full sentence, beginning with a capital letter and ending with a period. |
brennie | |
# We do not have a clear "Summary\n\nDescription"-style commit message so we assume everyting is the description. |
brennie | |
Your code below does enumerate on description. However, it is a blank string in the case where we only have … |
brennie | |
We should break here. |
brennie | |
"SCMself.client"? |
brennie | |
"SCMself.client"? |
brennie | |
"SCMself.client"? |
brennie | |
"SCMself.client"? |
brennie | |
"SCMself.client"? |
brennie | |
"SCMself.client"? |
brennie | |
No period. |
brennie | |
"a summary." |
brennie | |
I know I suggested the max() solution, but let's make this more clear by doing the following: if i == … |
brennie | |
, after description |
brennie | |
Re-flow these to take full advantage of the 79 character limit. |
brennie | |
Let's call this field_value |
brennie | |
"Whether or not we are posting a new review request." |
brennie | |
Whether or not the given field should be guessed. |
brennie | |
Since this method is being modified with new results, the documentation should be updated to use the modern docstring format. … |
chipx86 | |
I don't believe b'' is correct. We're working with Unicode strings everywhere else, and setting them when we set result['description'] … |
chipx86 | |
This should go in the second if block below, instead of pass. We don't need to default it to anything … |
chipx86 | |
Blank line between these. |
chipx86 | |
Blank line between these. |
chipx86 | |
I'd rather we explicitly strip as we set above, and just return result here. That way, we're not doing any … |
chipx86 | |
These should all be test_get_commit_message_with_.... The get_ part is missing from the test names. |
chipx86 | |
The dedenting and alignment and everything makes this a bit less ideal for readability. Can you change all these to … |
chipx86 | |
Can you compare against the explicit strings we're expecting? That helps with readability and prevents issues down the road as … |
chipx86 | |
There's an alignment issue here. It's also lacking the extended_help from the other --guess-* flags. |
chipx86 | |
-g really should be implying all the guess options. |
chipx86 | |
It's better to start with a "truthy" version first, inversing this if/else. So: if guess_summary: ... else: ... |
chipx86 | |
Can you change this to do: '%s\n\n%s' % (guessed_summary, guessed_description) |
chipx86 | |
Can we reword this as: The first line of the commit messages of the given revisions will be used as … |
brennie | |
Can we reformat this as: result['testing_done'] = \ '\n'.join(description[i + 1:]).strip() |
brennie |
- Summary:
-
Added a feature to parse out "Testing Done" in the commit message and submit it as testing_done field.Add a feature to parse out "Testing Done" in the commit message and submit it as testing_done field.
- Testing Done:
-
+ I ran unit tests.
+ + I created a test repository on a local server and posted the following review
+ requests testing cases with: + 1. Summary, Description and Testing Done
+ 2. Summary and Description
+ + With a previous Summary, Description and Testing Done:
+ 1. Update Summary
+ 2. Update Description
+ 3. Update Testing Done
+ + + + With a previuos Summary and Decription (no Testing Done):
+ 1. Add Testing Done
+ + Testing different ways of 'Testing Done' inputs:
+ 1. 'testing done'
+ 2. 'testing done:'
+ 3. 'Testing done'
+ 4. 'Testing done:'
+ 5. 'testing Done'
+ 6. 'testing Done:'
+ 7. 'tEstIng DOnE:'
- Testing Done:
-
I ran unit tests.
I created a test repository on a local server and posted the following review
requests testing cases with: 1. Summary, Description and Testing Done
2. Summary and Description
With a previous Summary, Description and Testing Done:
1. Update Summary
2. Update Description
3. Update Testing Done
~ With a previuos Summary and Decription (no Testing Done):
~ With a previous Summary and Description (no Testing Done):
1. Add Testing Done
Testing different ways of 'Testing Done' inputs:
1. 'testing done'
2. 'testing done:'
3. 'Testing done'
4. 'Testing done:'
5. 'testing Done'
6. 'testing Done:'
7. 'tEstIng DOnE:'
-
-
-
-
-
You can add:
only_testing_done = i == 1
and remove the computation for it below. This will be a cheaper computation.
We will also need to set it to False before in the
else
statement. -
Comments should never have first-person statements. They are not the justification for your changes -- the commit message is.
You can explain something is, but not why something changed. An explanation of why something changed doesn't make sense when you're looking at a file -- it only makes sense when you're looking at the commit as a whole. And when you're doing that, you can read the commit message for justification.
-
-
-
-
-
Docstring should be of the form
"""Testing SCMClient.get_commit_message when ..."""
If it goes over 79 characters and wraps, the trailing
"""
should be on its own line.Same below.
-
ALL_CAPS
is reserved for module-level and class-level constants. This would be fine ascommit_message
-
-
-
-
We are doing this in every test. We can do:
def setUp(self): super(SCMBaseClientTests, self).setUp() self.client = SCMClient()
and use
self.client
in all the tests. We like to keep all common set-up / teardown logic in these methods. See unittest.TestCase docs for information aboutsetUp
, etc. -
Instead of this, how about:
self.assertEqual( client.get_commit_message([]), { 'summary': lines[0], 'description': '', 'testing_done': lines[3], })
That way we test all three at once and test that there are no other keys returned.
Same below.
-
-
-
-
-
-
instead of saying the field names over again, how about:
"override the options for each guessed field" (in lieu of "get the ... options")
-
-
-
"Return" over get.
Can you add args/ returns to this docstring?
On second thought, you may want to hold off on these docstring changes since I believe David has a pending change that updates all docstrings in RBTools to bring them up to spec. Once that lands, you can rebase off of it.
-
-
-
-
I assume
{field}_pair
is a tuple, so can we do:summary_score = SequenceMatcher(None, *summary_pair).ratio() description_score = SequenceMatcher(None, *description_pair).ratio() testing_done_score = SequenceMatcher(None, *testing_done_pair).ratio()
Ideally, we could also do this in a dict comprehension and splat into the
Score
intializer:raw_scores = { field_name: SequenceMatcher(None, *field_pair).ratio() for field_name, field_pair in ( ('summary_score', summary_pair), ('description_score', description_pair), ('testing_done_score', testing_done_pair), ) } return Score(**raw_scores)
-
-
- Summary:
-
Add a feature to parse out "Testing Done" in the commit message and submit it as testing_done field.Parse out testing_done field from commit messages
- Commit:
-
ea9ccc35f6859bacf33fd8e4a26d20027af5ae73941e4ba15abb2941b9d2efa43af3db04bfa08234
Checks run (2 succeeded)
-
-
-
-
-
-
Instead of doing .strip() on each line, how about we remove those calls and do:
return { key: value.strip() for key, value in six.iteritems(result) }
-
-
-
-
-
Module docstrings need to be first, e.g.
"""Unit tests for rbtools.clients.SCMClient.""" from __future__ import unicode_literals # ....
-
-
-
-
-
-
-
-
Mind adding a comment same as above to indicate what this is doing? Also those comments are missing periods, could you add them?
-
-
-
Docstring summaries should be written in the imperitive mood (i.e., they give an order or command). In this case, it would be "Check" over "Checks", e.g.
Check if the guess value for a given field is True or False
However, we could also write this as:
Determine whether or not we should guess a given field.
Also missing args/returns.
-
Can you revert changes to this file? See my comment in
rbtools/utils/review_request.py
for the reasoning. -
Can you revert the changes to this file? I don't think we want to match on testing done, since a LOT of changes have "ran unit tests" as testing done, we would get lots of false positives.
- Commit:
-
941e4ba15abb2941b9d2efa43af3db04bfa082342d68dc0e06264762e2e66492a808b8578fa47f9a
Checks run (2 succeeded)
- Description:
-
Previously, one had to manually add comments in the testing done section
online for any testing they did for a given patch. However, now if one ~ was to post something with "Testing done:" in the commit message it will ~ was to post something with "Testing done:"
in the commit message it willsplit that out into the Testing Done section when posting.
-
-
Can you write your testing done without "I" statements, e.g.
"Ran unit tests."
"Created a test repository..." -
-
-
-
-
Docstrings should be in the following format:
"""Single line summary. Multi-line description. """
This can just be
"""Unit tests for rbtools.clients.SCMClient."""
-
-
-
-
-
-
-
-
Instead of enumerating all these cases, how about:
if guess_summary: self.options.summary = guessed_summary if guess_description: if not guess_summary: # If we're guessing the description but not the summary # (for example, if --summary was included), we probably # don't want to strip off the summary line of the # commit message. if guessed_description.startswith(guessed_summary): self.options.description = guessed_description else: self.options.description = \ guessed_summary + '\n\n' + guessed_description else: self.options.description = guessed_description if guess_testing_done: self.options.testing_done = guessed_testing_done
-
-
- Testing Done:
-
~ I ran unit tests.
~ Ran unit tests.
~ I created a test repository on a local server and posted the following review
~ Created a test repository on a local server and posted the following review
requests testing cases with: 1. Summary, Description and Testing Done
2. Summary and Description
With a previous Summary, Description and Testing Done:
1. Update Summary
2. Update Description
3. Update Testing Done
With a previous Summary and Description (no Testing Done):
1. Add Testing Done
Testing different ways of 'Testing Done' inputs:
1. 'testing done'
2. 'testing done:'
3. 'Testing done'
4. 'Testing done:'
5. 'testing Done'
6. 'testing Done:'
7. 'tEstIng DOnE:'
- Commit:
-
2d68dc0e06264762e2e66492a808b8578fa47f9a02360642d53fc949d1152f69a2973a4b2318e232
Checks run (2 succeeded)
-
Just two comments. This is looking really good! :)
-
We can optimize this to only check for a testing done when we have a description. We also don't need to enumerate all cases if we do the work of parsing out the testing done when we find it.
if len(lines) >= 3 and lines[0] and not lines[1]: # We have a description. This may also contain a testing done. description = lines[2:] for i, line in enumerate(description): if r.match(line): result['testing_done'] = '\n'.join(description[i:]) description = description[:i - 1] result['description'] = '\n'.join(description)
This code will be a lot easier to follow and understand.
-
Why is this
lines[0:]
now? This used to belines[1:]
. As it stands,i
doesn't match up with the actual line number so tests should be failing.
- Commit:
-
02360642d53fc949d1152f69a2973a4b2318e2329998fc9c689db112fc04a923e708ea8d78412264
Checks run (2 succeeded)
- Commit:
-
9998fc9c689db112fc04a923e708ea8d784122648bc66727ea684276a8259397503f66b8348146b0
Checks run (2 succeeded)
-
-
# We have a clear "Summary\n\nDescription"-style commit message so we can split out the description.
-
What if
len(lines) == 2
andlines[1]
is not empty, e.g.This is a summary this is a description
This should end up with
result = { 'description': "This is a summary.\nThis is a description.', 'summary': 'This is a summary.', 'testing_done': '', }
-
-
# We do not have a clear "Summary\n\nDescription"-style commit message so we assume everyting is the description.
- Commit:
-
8bc66727ea684276a8259397503f66b8348146b0177b6e44f7be8a699fe527520947c8a8d915f21b
Checks run (2 succeeded)
-
-
-
What happens in the case of a commit message like:
A commit message Testing done: Testing done: I did tests
-
Your code below does
enumerate
ondescription
. However, it is a blank string in the case where we only have summary and our loop expects a list!I suggest we do
description = []
here and do the following on lines 298--304:if description: for i, line in enumerate(description): if r.match(line): # ... break result['description'] = '\n'.join(description)
This way we skip the case where description is a
-
-
-
-
-
-
-
-
- Commit:
-
177b6e44f7be8a699fe527520947c8a8d915f21b999c1c26330c826fff89fb24439fd7b76265f05c
Checks run (2 succeeded)
- Commit:
-
999c1c26330c826fff89fb24439fd7b76265f05c0c0f2d4a8098e1a544caad2d07d2b6238b443040
Checks run (2 succeeded)
-
-
Since this method is being modified with new results, the documentation should be updated to use the modern docstring format. You'd need to make the following changes:
- Change "Returns" to "Return" in the summary (we're moving to that style everywhere else).
- Add an "Args" section detailing what
revisions
is. - Add a "Returns" section detailing what's in the dictionary and what string types to expect.
See https://www.notion.so/reviewboard/Writing-Codebase-Documentation-e16312b5f061437cb73cbfa369ac3cb5
-
I don't believe
b''
is correct. We're working with Unicode strings everywhere else, and setting them when we setresult['description']
below. -
This should go in the second
if
block below, instead ofpass
. We don't need to default it to anything above. -
-
-
I'd rather we explicitly strip as we set above, and just return
result
here. That way, we're not doing any unnecessary rebuilding of a dictionary, and we're very clear with what's going into it. -
-
The dedenting and alignment and everything makes this a bit less ideal for readability. Can you change all these to this form:
commit_message = ( b'This is the summary.\n' b'\n' b'Testing Done:\n' b'....\n' )
That may seem more "noisy" (the
b
prefixes and\n
), but with unit tests it's very important to clearly see the data you're working with, and so it's an advantage in this case. We know where every newline is and where the start of every line is, and code doesn't start to run into other code below it due to the indentation. -
Can you compare against the explicit strings we're expecting? That helps with readability and prevents issues down the road as logic changes.
This comment applies to all tests.
-
There's an alignment issue here.
It's also lacking the
extended_help
from the other--guess-*
flags. -
-
It's better to start with a "truthy" version first, inversing this if/else. So:
if guess_summary: ... else: ...
-
- Commit:
-
0c0f2d4a8098e1a544caad2d07d2b6238b44304095c7a5f1005d1be149fafbd461993689d413b93a