[email protected] 1 Automated Debugging with git bisect TDD Camille Bell Agile Coach / Rails Developer [email protected] Twi3er @agilecamille h6p://www.slideshare.net/Camille_Bell/
Nov 07, 2014
Automated Debugging with git bisect
TDD Camille Bell
Agile Coach / Rails Developer
Twi3er @agilecamille h6p://www.slideshare.net/Camille_Bell/
How do you find a bug in a large code base with lots of commits?
1st 2nd 3rd 4th 97th 98th 99th 100th
Debugging OpNons
• Manually inspect all the code and hope you
find the bug "
• Logically eliminate some of the code, inspect
all the rest and hope you find the bug
• Use automaNon to find your bug fast
Steps to Target the Buggy Code
• Create new or use an exisNng automated bug detector to disNnguish between bug free code from buggy code (usually an automated test)
• Determine the bug commit range – Use git log if needed
• Use git bisect – Run the test – Use git diff to narrow bug to code lines
AutomaNng Bug DetecNon
• Perhaps you already have a good automated test, but weren’t running it with every commit. Use that test (and set up a CI server soon).
• If not create a new test that should fail (RED) when the bug is present and pass (GREEN) when the bug is fixed.
– Write the test in whatever test language makes sense (e.g. RSpec for low level Ruby, Cucumber high level behavior, Jasmine for JavaScript, JUnit for Java, NUnit for C#, etc.)
Verify the Test Catches the Bug
• Run the test on the latest buggy code and watch it fail (RED).
• Go back to a known good commit, run the test and watch it pass (GREEN).
$ rspec . –color F.
Failures:
1) Account depositing to account produces the correct balance when 9 dollars is deposited the balance is 10 dollars Failure/Error: @account.balance.should == 1000 expected: 1000 got: 999.9999 (using ==) # ./account_spec.rb:15:in `block (3 levels) in <top (required)>'
Finished in 0.00058 seconds 2 examples, 1 failure
Failed examples:
rspec ./account_spec.rb:12 # Account depositing to account produces the correct balance when 9 dollars is deposited the balance is 10 dollars $
Verifying Failure on Known Bad Commit
$ rspec . –color ..
Finished in 0.00047 seconds 2 examples, 0 failures $
Verifying Success on a Known Good Commit
Running git bisect
• Start with a known bad git repository – $ git bisect start – $ git bisect bad – $ git bisect good <good_commit>
• Run your tests • If the test passes – $ git bisect good
• If the test fails – $ git bisect bad
• Repeat unNl you find the bad commit
Use either tag or git commit #
If needed checkout a bad git commit #
""""" """
Determine the Bug Commit Range: • The last known commit without the bug
• The first commit aeer the bug observed
StarNng Good Commit
StarNng Bad
Commit
Bug Inserted Within These 8 Commits
git bisect does a binary search through your commit range.
""""" """
Good Commit
Bad Commit
bisect StarNng Commit
bisect begins with the commit halfway between the known good commit and the known bad commit
Assume the 1st test on bisect failed.
""""" """
Good Commit
Bad Commit
bisect Next
Commit
Then bisect would select the commit between the known good commit and the last bad bisect
Bad Commit
Can ignore these Commits
Bug inserted Within These 4 Commits
Assume the 2nd test on bisect passed.
""" """
Good Commit
Bad Commit
Then bisect would select the commit between the last known good commit and the last bad bisect
Bad Commit
Can ignore these Commits
Bug inserted Within These 2 Commits
Can ignore these Commits
Good Commit
bisect Next
Commit
If the 3rd test on bisect failed.
""" """
Good Commit
Bad Commit
Then that commit is where the bug was inserted.
Bad Commit
Can ignore these Commits
Bug inserted Within These 2 Commits
Can ignore these Commits
Good Commit
Final Commit Failed Test
If the 3rd test on bisect passed.
"""""
Good Commit
Bad Commit
Then the next commit is where the bug was inserted.
Bad Commit
Can ignore these Commits
Bug inserted Within These 2 Commits
Can ignore these Commits
Good Commit
Final Commit Passed Test
Example Test require_relaNve 'account'
describe Account do
before do @starNng_balance_in_pennies = 100 @account = Account.new(@starNng_balance_in_pennies) end
context "deposiNng to account produces the correct balance" do
it "when 9 dollars is deposited the balance is 10 dollars" do deposit_amount_in_pennies = 900 @account.deposit(deposit_amount_in_pennies) @account.balance.should == 1000 end end
end
SomeNmes the Commit Messages from git log Pinpoint the Bug
$ git log --pretty="%h - %s" 561bb3a - added withdrawal and deposit messages 6ca7ee1 - added error c546d1f - added to_s 1974592 - added withdrawal 7a8c508 - Initial commit
But usually the log only provides a range
Known Good Commit
Known Bad
Commit
StarNng up git bisect with Test on the Middle git Commit
$ git bisect start Already on 'master’ $ git bisect bad $ git bisect good 7a8c508 Bisecting: 1 revision left to test after this (roughly 1 step) [c546d1f89b5b0c14ab160e227fc83d62fb780e6f] added to_s $ rspec . --color ..
Finished in 0.00071 seconds 2 examples, 0 failures $ git bisect good Bisecting: 0 revisions left to test after this (roughly 0 steps) [6ca7ee1909bf0b3f7344feee25a5b44a97602e2c] added error
Known Good Commit
Known Bad Commit
If the tests pass, Tell git bisect good
Otherwise git bisect bad
C O M M I T
Test on the final git commit $ rspec . --color F.
Failures:
1) Account depositing to account produces the correct balance when 9 dollars is deposited the balance is 10 dollars Failure/Error: @account.balance.should == 1000 expected: 1000 got: 999.9999 (using ==) # ./account_spec.rb:15:in `block (3 levels) in <top (required)>'
Finished in 0.00053 seconds 2 examples, 1 failure
Failed examples:
rspec ./account_spec.rb:12 # Account depositing to account produces the correct balance when 9 dollars is deposited the balance is 10 dollars $
$ git diff c546d1f 6ca7ee1 diff --git a/account.rb b/account.rb index a979e55..0a572ef 100644 --- a/account.rb +++ b/account.rb @@ -5,7 +5,7 @@ class Account end
def deposit(new_deposit)
- @balance += new_deposit + @balance += (new_deposit - 0.0001) end
def withdrawl(new_withdrawl) $
git diff Targets the Bug Even More Good Commit Just Before Bug
Commit Where Bug First Appeared
Bug inserted in one or
more of the + lines.
Thank You for Listening
Camille Bell
Agile Coach / Rails Developer
Twi3er @agilecamille h6p://www.slideshare.net/Camille_Bell/