<commit_before>package gospy_test

import (
	. "github.com/cfmobile/gospy"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"fmt"
	"errors"
)

const (
	kOriginalStringReturn = "original string value"
	kOriginalIntReturn = 12345
	kOriginalFloatReturn = float64(123.45)
	kOriginalBoolReturn = true
)

var kOriginalErrorReturn = errors.New("some error")

var _ = Describe("GoSpy", func() {
	var subject *GoSpy

	var functionToSpy func(string, int, bool) (string, int, float64, bool, error)
	var panicked bool

	BeforeEach(func() {
	    subject = nil
		panicked = false
		functionToSpy = func(string, int, bool) (string, int, float64, bool, error) {
			return kOriginalStringReturn,
				kOriginalIntReturn,
				kOriginalFloatReturn,
				kOriginalBoolReturn,
				kOriginalErrorReturn
		}
	})

	panicRecover := func() {
		panicked = recover() != nil
	}

	Describe("Constructors", func() {

		var constructorSuccessTests = func() {
			It("should not have panicked", func() {
				Expect(panicked).To(BeFalse())
			})

			It("should have returned a valid *GoSpy object", func() {
				Expect(subject).NotTo(BeNil())
			})
		}

		var constructorFailTests = func() {
			It("should have panicked", func() {
				Expect(panicked).To(BeTrue())
			})

			It("should not have returned a valid *GoSpy object", func() {
				Expect(subject).To(BeNil())
			})
		}

		var itShouldMakeTheFunctionReturnDefaultValues = func() {
			It("should have modified the behaviour of the function to return default type values for each of the return values", func() {
				stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("something", 10, false)

				Expect(stringResult).To(Equal(""))
				Expect(intResult).To(Equal(0))
				Expect(floatResult).To(Equal(0.0))
				Expect(boolResult).To(Equal(false))
				Expect(errorResult).To(BeNil())
			})
		}

	    Describe("Spy", func() {

	        Context("when calling Spy() with a valid function pointer", func() {
				BeforeEach(func() {
					defer panicRecover()
				    subject = Spy(&functionToSpy)
				})

				constructorSuccessTests()

				It("should not have affected the function's behaviour", func() {
					stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("something", 10, false)

					Expect(stringResult).To(Equal(kOriginalStringReturn))
					Expect(intResult).To(Equal(kOriginalIntReturn))
					Expect(floatResult).To(Equal(kOriginalFloatReturn))
					Expect(boolResult).To(Equal(kOriginalBoolReturn))
					Expect(errorResult).To(Equal(kOriginalErrorReturn))
				})
	        })

			Context("when calling Spy() with a function var", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = Spy(functionToSpy)
			    })

				constructorFailTests()
			})

			Context("when calling Spy() with any other unexpected type", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					someVar := "some random var"
					subject = Spy(&someVar)
			    })

				constructorFailTests()
			})
	    })

		Describe("SpyAndFake", func() {

			Context("when calling SpyAndFake() with a valid function pointer", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFake(&functionToSpy)
			    })

				constructorSuccessTests()

				itShouldMakeTheFunctionReturnDefaultValues()
			})

			Context("when calling SpyAndFake() with a function object", func() {
				BeforeEach(func() {
				    defer panicRecover()
					subject = SpyAndFake(functionToSpy)
				})

				constructorFailTests()
			})

			Context("when calling SpyAndFake() with any other unexpected type", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					someVar := "some random var"
					subject = SpyAndFake(&someVar)
			    })

				constructorFailTests()
			})
		})

		Describe("SpyAndFakeWithReturn", func() {

			Context("when calling SpyAndFakeWithReturn() with a valid function pointer and valid mock return values", func() {
				mockStringValue := "mock value"
				mockIntValue := 1
				mockFloatValue := 2.0
				mockBoolValue := false
				mockErrorValue := errors.New("mock error")

				BeforeEach(func() {
				    defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy, mockStringValue, mockIntValue, mockFloatValue, mockBoolValue, mockErrorValue)
				})

				constructorSuccessTests()

				It("should have altered the function to just return the mock values specified", func() {
					stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("something", 10, false)

					Expect(stringResult).To(Equal(mockStringValue))
					Expect(intResult).To(Equal(mockIntValue))
					Expect(floatResult).To(Equal(mockFloatValue))
					Expect(boolResult).To(Equal(mockBoolValue))
					Expect(errorResult).To(Equal(mockErrorValue))
				})
			})

			Context("when calling SpyAndFakeWithReturn() with no fake return values while the monitored function expects return values", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy)
			    })

				constructorSuccessTests()

				itShouldMakeTheFunctionReturnDefaultValues()
			})

			Context("when calling SpyAndFakeWithReturn() with an invalid first argument (not a function pointer)", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(functionToSpy, "mock", 1, 2.0, false, nil)
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithReturn() with an incorrect number of arguments (not matching the number of return values in the monitored function)", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy, "mock", 1)
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithReturn() with an incorrect variable type for any of the mock return values", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy, 0, 1, 2.0, false, nil)
			    })

				constructorFailTests()
			})
		})

		Describe("SpyAndFakeWithFunc", func() {

			Context("when calling SpyAndFakeWithFunc() with a valid target and valid mock function", func() {
				mockStringValue := "mock value"
				mockIntValue := 1
				mockFloatValue := 2.0
				mockBoolValue := false
				mockErrorValue := errors.New("mock error")

				mockFunction := func(s string, i int, b bool) (string, int, float64, bool, error) {
					// Return error if b is false
					if b {
						return mockStringValue, mockIntValue, mockFloatValue, mockBoolValue, nil
					} else {
						return "", 0, 0.0, false, mockErrorValue
					}
				}

				BeforeEach(func() {
				    defer panicRecover()
					subject = SpyAndFakeWithFunc(&functionToSpy, mockFunction)
				})

				constructorSuccessTests()

				It("should modify the monitored function's behaviour to the mock function's", func() {
					stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("", 0, true)

					Expect(stringResult).To(Equal(mockStringValue))
					Expect(intResult).To(Equal(mockIntValue))
					Expect(floatResult).To(Equal(mockFloatValue))
					Expect(boolResult).To(Equal(mockBoolValue))
					Expect(errorResult).To(BeNil())

					stringResult, intResult, floatResult, boolResult, errorResult = functionToSpy("", 0, false)

					Expect(stringResult).To(BeEmpty())
					Expect(intResult).To(BeZero())
					Expect(floatResult).To(BeZero())
					Expect(boolResult).To(BeFalse())
					Expect(errorResult).To(Equal(mockErrorValue))
				})
		    })

			Context("when calling SpyAndFakeWithFunc() with a functionPtr as the mock function", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					mockFunc := func(s string, i int, b bool) (string, int, float64, bool, error) {
						return "", 0, 0.0, false, nil
					}
					subject = SpyAndFakeWithFunc(&functionToSpy, &mockFunc)
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithFunc() with a mock function that doesn't have a matching signature with the target's", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithFunc(&functionToSpy, func() {})
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithFunc() with a non-functionPtr target", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithFunc(functionToSpy, func(s string, i int, b bool) (string, int, float64, bool, error) {
						return "", 0, 0.0, false, nil
					})
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithFunc() with an incompatible type as the mock function", func() {
				BeforeEach(func() {
				    defer panicRecover()
					someVar := "some random var"
					subject = SpyAndFakeWithFunc(&functionToSpy, someVar)
				})

				constructorFailTests()
			})
		})
	})

	Context("when a valid GoSpy object is created", func() {
		var expectedCalledState bool
		var expectedCallCount int
		var expectedCallList CallList

		// Definition of common tests for each scenario
		var goSpyResetTests = func() {
			Context("when Reset() is called", func() {
				BeforeEach(func() {
					subject.Reset()
				})

				It("should zero the call count", func() {
					Expect(subject.CallCount()).To(BeZero())
				})

				It("should return a nil call list", func() {
					Expect(subject.Calls()).To(BeNil())
				})

				It("should have reset the call indicator", func() {
					Expect(subject.Called()).To(BeFalse())
				})
			})
		}

		var goSpyRestoreTests = func(existingCallCount int, existingCallList CallList) {
			Context("when Restore() is called", func() {
				BeforeEach(func() {
					subject.Restore()
				})

				It("should not have affected the existing call count", func() {
					Expect(subject.CallCount()).To(Equal(existingCallCount))
				})

				It("should not have affected the call list", func() {
					Expect(subject.Calls()).To(Equal(existingCallList))
				})

				It("should no longer monitor subsequent calls to the function", func() {
					Expect(subject.CallCount()).To(Equal(existingCallCount))

					functionToSpy("another call", 101, true)

					Expect(subject.CallCount()).To(Equal(existingCallCount))
					Expect(subject.Calls()).NotTo(ContainElement(ArgList{"another call", 101, true}))
				})
			})
		}

		var goSpyCalledTest = func(expectedCalledState bool) {
			wasCalled := "was"
			if !expectedCalledState {
				wasCalled = "was not"
			}

			It(fmt.Sprintf("should indicate that the function %s Called()", wasCalled), func() {
				Expect(subject.Called()).To(Equal(expectedCalledState))
			})
		}

		var goSpyCallCountTest = func(expectedCallCount int) {
			It(fmt.Sprintf("should indicate a CallCount() of %d", expectedCallCount), func() {
				Expect(subject.CallCount()).To(Equal(expectedCallCount))
			})
		}

		var goSpyCallsTest = func(expectedCallList CallList) {
			msg := "an expected and ordered"
			if expectedCallList == nil {
				msg = "a nil"
			}

			It(fmt.Sprintf("should contain %s list of Calls()", msg), func() {
			    Expect(subject.Calls()).To(Equal(expectedCallList))
			})
		}

		BeforeEach(func() {
			subject = Spy(&functionToSpy)
		})

		Context("as soon as it's created", func() {
			expectedCalledState = false
		    expectedCallCount = 0
			expectedCallList = nil

			goSpyCalledTest(expectedCalledState)

			goSpyCallCountTest(expectedCallCount)

			goSpyCallsTest(expectedCallList)

			goSpyResetTests()

			goSpyRestoreTests(expectedCallCount, expectedCallList)

			Context("when ArgsForCall() is called with no calls in the Spy", func() {
				BeforeEach(func() {
					defer panicRecover()
					subject.ArgsForCall(0)
				})

				It("should panic", func() {
					Expect(panicked).To(BeTrue())
				})
			})
		})

		Context("and the monitored function is called once", func() {
			expectedCalledState = true
			expectedCallCount = 1
			expectedArgList := ArgList{"test value", 101, true}
			expectedCallList = CallList{expectedArgList}

			BeforeEach(func() {
			    functionToSpy("test value", 101, true)
			})

			goSpyCalledTest(expectedCalledState)

			goSpyCallCountTest(expectedCallCount)

			goSpyCallsTest(expectedCallList)

			goSpyResetTests()

			goSpyRestoreTests(expectedCallCount, expectedCallList)

			It("ArgsForCall() should return the arguments that were used in the call", func() {
			    Expect(subject.ArgsForCall(0)).To(Equal(expectedArgList))
			})
		})

		Context("and the monitored function is called several times", func() {
			expectedCalledState = true
			expectedCallCount = 3
			expectedCallList = CallList{
				{"call 1", 1, true},
				{"call 2", 2, false},
				{"call 3", 3, true},
			}

			BeforeEach(func() {
			    functionToSpy("call 1", 1, true)
				functionToSpy("call 2", 2, false)
				functionToSpy("call 3", 3, true)
			})

			goSpyCalledTest(expectedCalledState)

			goSpyCallCountTest(expectedCallCount)

			goSpyCallsTest(expectedCallList)

			goSpyResetTests()

			goSpyRestoreTests(expectedCallCount, expectedCallList)

			It("ArgsForCall(n) should return the arguments for the n-th call (0-based index) ", func() {
			    Expect(subject.ArgsForCall(0)).To(Equal(expectedCallList[0]))
			    Expect(subject.ArgsForCall(1)).To(Equal(expectedCallList[1]))
			    Expect(subject.ArgsForCall(2)).To(Equal(expectedCallList[2]))
			})
		})
	})
})
<commit_msg>RS - Removed unnecessary variables with larger scope than needed<commit_after>package gospy_test

import (
	. "github.com/cfmobile/gospy"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"fmt"
	"errors"
)

const (
	kOriginalStringReturn = "original string value"
	kOriginalIntReturn = 12345
	kOriginalFloatReturn = float64(123.45)
	kOriginalBoolReturn = true
)

var kOriginalErrorReturn = errors.New("some error")

var _ = Describe("GoSpy", func() {
	var subject *GoSpy

	var functionToSpy func(string, int, bool) (string, int, float64, bool, error)
	var panicked bool

	BeforeEach(func() {
	    subject = nil
		panicked = false
		functionToSpy = func(string, int, bool) (string, int, float64, bool, error) {
			return kOriginalStringReturn,
				kOriginalIntReturn,
				kOriginalFloatReturn,
				kOriginalBoolReturn,
				kOriginalErrorReturn
		}
	})

	panicRecover := func() {
		panicked = recover() != nil
	}

	Describe("Constructors", func() {

		var constructorSuccessTests = func() {
			It("should not have panicked", func() {
				Expect(panicked).To(BeFalse())
			})

			It("should have returned a valid *GoSpy object", func() {
				Expect(subject).NotTo(BeNil())
			})
		}

		var constructorFailTests = func() {
			It("should have panicked", func() {
				Expect(panicked).To(BeTrue())
			})

			It("should not have returned a valid *GoSpy object", func() {
				Expect(subject).To(BeNil())
			})
		}

		var itShouldMakeTheFunctionReturnDefaultValues = func() {
			It("should have modified the behaviour of the function to return default type values for each of the return values", func() {
				stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("something", 10, false)

				Expect(stringResult).To(Equal(""))
				Expect(intResult).To(Equal(0))
				Expect(floatResult).To(Equal(0.0))
				Expect(boolResult).To(Equal(false))
				Expect(errorResult).To(BeNil())
			})
		}

	    Describe("Spy", func() {

	        Context("when calling Spy() with a valid function pointer", func() {
				BeforeEach(func() {
					defer panicRecover()
				    subject = Spy(&functionToSpy)
				})

				constructorSuccessTests()

				It("should not have affected the function's behaviour", func() {
					stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("something", 10, false)

					Expect(stringResult).To(Equal(kOriginalStringReturn))
					Expect(intResult).To(Equal(kOriginalIntReturn))
					Expect(floatResult).To(Equal(kOriginalFloatReturn))
					Expect(boolResult).To(Equal(kOriginalBoolReturn))
					Expect(errorResult).To(Equal(kOriginalErrorReturn))
				})
	        })

			Context("when calling Spy() with a function var", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = Spy(functionToSpy)
			    })

				constructorFailTests()
			})

			Context("when calling Spy() with any other unexpected type", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					someVar := "some random var"
					subject = Spy(&someVar)
			    })

				constructorFailTests()
			})
	    })

		Describe("SpyAndFake", func() {

			Context("when calling SpyAndFake() with a valid function pointer", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFake(&functionToSpy)
			    })

				constructorSuccessTests()

				itShouldMakeTheFunctionReturnDefaultValues()
			})

			Context("when calling SpyAndFake() with a function object", func() {
				BeforeEach(func() {
				    defer panicRecover()
					subject = SpyAndFake(functionToSpy)
				})

				constructorFailTests()
			})

			Context("when calling SpyAndFake() with any other unexpected type", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					someVar := "some random var"
					subject = SpyAndFake(&someVar)
			    })

				constructorFailTests()
			})
		})

		Describe("SpyAndFakeWithReturn", func() {

			Context("when calling SpyAndFakeWithReturn() with a valid function pointer and valid mock return values", func() {
				mockStringValue := "mock value"
				mockIntValue := 1
				mockFloatValue := 2.0
				mockBoolValue := false
				mockErrorValue := errors.New("mock error")

				BeforeEach(func() {
				    defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy, mockStringValue, mockIntValue, mockFloatValue, mockBoolValue, mockErrorValue)
				})

				constructorSuccessTests()

				It("should have altered the function to just return the mock values specified", func() {
					stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("something", 10, false)

					Expect(stringResult).To(Equal(mockStringValue))
					Expect(intResult).To(Equal(mockIntValue))
					Expect(floatResult).To(Equal(mockFloatValue))
					Expect(boolResult).To(Equal(mockBoolValue))
					Expect(errorResult).To(Equal(mockErrorValue))
				})
			})

			Context("when calling SpyAndFakeWithReturn() with no fake return values while the monitored function expects return values", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy)
			    })

				constructorSuccessTests()

				itShouldMakeTheFunctionReturnDefaultValues()
			})

			Context("when calling SpyAndFakeWithReturn() with an invalid first argument (not a function pointer)", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(functionToSpy, "mock", 1, 2.0, false, nil)
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithReturn() with an incorrect number of arguments (not matching the number of return values in the monitored function)", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy, "mock", 1)
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithReturn() with an incorrect variable type for any of the mock return values", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithReturn(&functionToSpy, 0, 1, 2.0, false, nil)
			    })

				constructorFailTests()
			})
		})

		Describe("SpyAndFakeWithFunc", func() {

			Context("when calling SpyAndFakeWithFunc() with a valid target and valid mock function", func() {
				mockStringValue := "mock value"
				mockIntValue := 1
				mockFloatValue := 2.0
				mockBoolValue := false
				mockErrorValue := errors.New("mock error")

				mockFunction := func(s string, i int, b bool) (string, int, float64, bool, error) {
					// Return error if b is false
					if b {
						return mockStringValue, mockIntValue, mockFloatValue, mockBoolValue, nil
					} else {
						return "", 0, 0.0, false, mockErrorValue
					}
				}

				BeforeEach(func() {
				    defer panicRecover()
					subject = SpyAndFakeWithFunc(&functionToSpy, mockFunction)
				})

				constructorSuccessTests()

				It("should modify the monitored function's behaviour to the mock function's", func() {
					stringResult, intResult, floatResult, boolResult, errorResult := functionToSpy("", 0, true)

					Expect(stringResult).To(Equal(mockStringValue))
					Expect(intResult).To(Equal(mockIntValue))
					Expect(floatResult).To(Equal(mockFloatValue))
					Expect(boolResult).To(Equal(mockBoolValue))
					Expect(errorResult).To(BeNil())

					stringResult, intResult, floatResult, boolResult, errorResult = functionToSpy("", 0, false)

					Expect(stringResult).To(BeEmpty())
					Expect(intResult).To(BeZero())
					Expect(floatResult).To(BeZero())
					Expect(boolResult).To(BeFalse())
					Expect(errorResult).To(Equal(mockErrorValue))
				})
		    })

			Context("when calling SpyAndFakeWithFunc() with a functionPtr as the mock function", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					mockFunc := func(s string, i int, b bool) (string, int, float64, bool, error) {
						return "", 0, 0.0, false, nil
					}
					subject = SpyAndFakeWithFunc(&functionToSpy, &mockFunc)
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithFunc() with a mock function that doesn't have a matching signature with the target's", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithFunc(&functionToSpy, func() {})
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithFunc() with a non-functionPtr target", func() {
			    BeforeEach(func() {
			        defer panicRecover()
					subject = SpyAndFakeWithFunc(functionToSpy, func(s string, i int, b bool) (string, int, float64, bool, error) {
						return "", 0, 0.0, false, nil
					})
			    })

				constructorFailTests()
			})

			Context("when calling SpyAndFakeWithFunc() with an incompatible type as the mock function", func() {
				BeforeEach(func() {
				    defer panicRecover()
					someVar := "some random var"
					subject = SpyAndFakeWithFunc(&functionToSpy, someVar)
				})

				constructorFailTests()
			})
		})
	})

	Context("when a valid GoSpy object is created", func() {

		// Definition of common tests for each scenario
		var goSpyResetTests = func() {
			Context("when Reset() is called", func() {
				BeforeEach(func() {
					subject.Reset()
				})

				It("should zero the call count", func() {
					Expect(subject.CallCount()).To(BeZero())
				})

				It("should return a nil call list", func() {
					Expect(subject.Calls()).To(BeNil())
				})

				It("should have reset the call indicator", func() {
					Expect(subject.Called()).To(BeFalse())
				})
			})
		}

		var goSpyRestoreTests = func(existingCallCount int, existingCallList CallList) {
			Context("when Restore() is called", func() {
				BeforeEach(func() {
					subject.Restore()
				})

				It("should not have affected the existing call count", func() {
					Expect(subject.CallCount()).To(Equal(existingCallCount))
				})

				It("should not have affected the call list", func() {
					Expect(subject.Calls()).To(Equal(existingCallList))
				})

				It("should no longer monitor subsequent calls to the function", func() {
					Expect(subject.CallCount()).To(Equal(existingCallCount))

					functionToSpy("another call", 101, true)

					Expect(subject.CallCount()).To(Equal(existingCallCount))
					Expect(subject.Calls()).NotTo(ContainElement(ArgList{"another call", 101, true}))
				})
			})
		}

		var goSpyCalledTest = func(expectedCalledState bool) {
			wasCalled := "was"
			if !expectedCalledState {
				wasCalled = "was not"
			}

			It(fmt.Sprintf("should indicate that the function %s Called()", wasCalled), func() {
				Expect(subject.Called()).To(Equal(expectedCalledState))
			})
		}

		var goSpyCallCountTest = func(expectedCallCount int) {
			It(fmt.Sprintf("should indicate a CallCount() of %d", expectedCallCount), func() {
				Expect(subject.CallCount()).To(Equal(expectedCallCount))
			})
		}

		var goSpyCallsTest = func(expectedCallList CallList) {
			msg := "an expected and ordered"
			if expectedCallList == nil {
				msg = "a nil"
			}

			It(fmt.Sprintf("should contain %s list of Calls()", msg), func() {
			    Expect(subject.Calls()).To(Equal(expectedCallList))
			})
		}

		BeforeEach(func() {
			subject = Spy(&functionToSpy)
		})

		Context("as soon as it's created", func() {
			expectedCalledState := false
		    expectedCallCount := 0
			expectedCallList := CallList(nil)

			goSpyCalledTest(expectedCalledState)

			goSpyCallCountTest(expectedCallCount)

			goSpyCallsTest(expectedCallList)

			goSpyResetTests()

			goSpyRestoreTests(expectedCallCount, expectedCallList)

			Context("when ArgsForCall() is called with no calls in the Spy", func() {
				BeforeEach(func() {
					defer panicRecover()
					subject.ArgsForCall(0)
				})

				It("should panic", func() {
					Expect(panicked).To(BeTrue())
				})
			})
		})

		Context("and the monitored function is called once", func() {
			expectedCalledState := true
			expectedCallCount := 1
			expectedArgList := ArgList{"test value", 101, true}
			expectedCallList := CallList{expectedArgList}

			BeforeEach(func() {
			    functionToSpy("test value", 101, true)
			})

			goSpyCalledTest(expectedCalledState)

			goSpyCallCountTest(expectedCallCount)

			goSpyCallsTest(expectedCallList)

			goSpyResetTests()

			goSpyRestoreTests(expectedCallCount, expectedCallList)

			It("ArgsForCall() should return the arguments that were used in the call", func() {
			    Expect(subject.ArgsForCall(0)).To(Equal(expectedArgList))
			})
		})

		Context("and the monitored function is called several times", func() {
			expectedCalledState := true
			expectedCallCount := 3
			expectedCallList := CallList{
				{"call 1", 1, true},
				{"call 2", 2, false},
				{"call 3", 3, true},
			}

			BeforeEach(func() {
			    functionToSpy("call 1", 1, true)
				functionToSpy("call 2", 2, false)
				functionToSpy("call 3", 3, true)
			})

			goSpyCalledTest(expectedCalledState)

			goSpyCallCountTest(expectedCallCount)

			goSpyCallsTest(expectedCallList)

			goSpyResetTests()

			goSpyRestoreTests(expectedCallCount, expectedCallList)

			It("ArgsForCall(n) should return the arguments for the n-th call (0-based index) ", func() {
			    Expect(subject.ArgsForCall(0)).To(Equal(expectedCallList[0]))
			    Expect(subject.ArgsForCall(1)).To(Equal(expectedCallList[1]))
			    Expect(subject.ArgsForCall(2)).To(Equal(expectedCallList[2]))
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The gotip command compiles and runs the go command from the development tree.
//
// To install, run:
//
//     $ go get golang.org/dl/gotip
//     $ gotip download
//
// And then use the gotip command as if it were your normal go command.
//
// To update, run "gotip download" again.
package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

func init() {
	http.DefaultTransport = &userAgentTransport{http.DefaultTransport}
}

func main() {
	log.SetFlags(0)

	root, err := goroot("gotip")
	if err != nil {
		log.Fatalf("gotip: %v", err)
	}

	if len(os.Args) == 2 && os.Args[1] == "download" {
		if err := installTip(root); err != nil {
			log.Fatalf("gotip: %v", err)
		}
		log.Printf("Success. You may now run 'gotip'!")
		os.Exit(0)
	}

	gobin := filepath.Join(root, "bin", "go"+exe())
	if _, err := os.Stat(gobin); err != nil {
		log.Fatalf("gotip: not downloaded. Run 'gotip download' to install to %v", root)
	}

	cmd := exec.Command(gobin, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	newPath := filepath.Join(root, "bin")
	if p := os.Getenv("PATH"); p != "" {
		newPath += string(filepath.ListSeparator) + p
	}
	cmd.Env = dedupEnv(caseInsensitiveEnv, append(os.Environ(), "GOROOT="+root, "PATH="+newPath))
	if err := cmd.Run(); err != nil {
		if _, ok := err.(*exec.ExitError); ok {
			// TODO: return the same exit status maybe.
			os.Exit(1)
		}
		log.Fatalf("gotip: failed to execute %v: %v", gobin, err)
	}
	os.Exit(0)
}

func installTip(root string) error {
	git := func(args ...string) error {
		cmd := exec.Command("git", args...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Dir = root
		return cmd.Run()
	}

	if _, err := os.Stat(filepath.Join(root, ".git")); err != nil {
		if err := os.MkdirAll(root, 0755); err != nil {
			return fmt.Errorf("failed to create repository: %v", err)
		}
		if err := git("clone", "--depth=1", "https://go.googlesource.com/go", root); err != nil {
			return fmt.Errorf("failed to clone git repository: %v", err)
		}
	} else {
		log.Printf("Updating the go development tree...")
		if err := git("fetch", "origin"); err != nil {
			return fmt.Errorf("failed to fetch git repository updates: %v", err)
		}
	}

	// Use checkout and a detached HEAD, because it will refuse to overwrite
	// local changes, and warn if commits are being left behind, but will not
	// mind if master is force-pushed upstream.
	if err := git("-c", "advice.detachedHead=false", "checkout", "origin/master"); err != nil {
		return fmt.Errorf("failed to checkout git repository: %v", err)
	}
	// It shouldn't be the case, but in practice sometimes binary artifacts
	// generated by earlier Go versions interfere with the build.
	//
	// Ask the user what to do about them if they are not gitignored. They might
	// be artifacts that used to be ignored in previous versions, or precious
	// uncommitted source files.
	if err := git("clean", "-i", "-d"); err != nil {
		return fmt.Errorf("failed to cleanup git repository: %v", err)
	}
	// Wipe away probably boring ignored files without bothering the user.
	if err := git("clean", "-q", "-f", "-d", "-X"); err != nil {
		return fmt.Errorf("failed to cleanup git repository: %v", err)
	}

	cmd := exec.Command(filepath.Join(root, "src", makeScript()))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Dir = filepath.Join(root, "src")
	if runtime.GOOS == "windows" {
		// Workaround make.bat not autodetecting GOROOT_BOOTSTRAP. Issue 28641.
		goroot, err := exec.Command("go", "env", "GOROOT").Output()
		if err != nil {
			return fmt.Errorf("failed to detect an existing go installation for bootstrap: %v", err)
		}
		cmd.Env = append(os.Environ(), "GOROOT_BOOTSTRAP="+strings.TrimSpace(string(goroot)))
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to build go: %v", err)
	}

	return nil
}

func makeScript() string {
	switch runtime.GOOS {
	case "plan9":
		return "make.rc"
	case "windows":
		return "make.bat"
	default:
		return "make.bash"
	}
}

const caseInsensitiveEnv = runtime.GOOS == "windows"

func exe() string {
	if runtime.GOOS == "windows" {
		return ".exe"
	}
	return ""
}

func goroot(version string) (string, error) {
	home, err := homedir()
	if err != nil {
		return "", fmt.Errorf("failed to get home directory: %v", err)
	}
	return filepath.Join(home, "sdk", version), nil
}

func homedir() (string, error) {
	// This could be replaced with os.UserHomeDir, but it was introduced too
	// recently, and we want this to work with go as packaged by Linux
	// distributions. Note that user.Current is not enough as it does not
	// prioritize $HOME. See also Issue 26463.
	switch runtime.GOOS {
	case "plan9":
		return "", fmt.Errorf("%q not yet supported", runtime.GOOS)
	case "windows":
		if dir := os.Getenv("USERPROFILE"); dir != "" {
			return dir, nil
		}
		return "", errors.New("can't find user home directory; %USERPROFILE% is empty")
	default:
		if dir := os.Getenv("HOME"); dir != "" {
			return dir, nil
		}
		if u, err := user.Current(); err == nil && u.HomeDir != "" {
			return u.HomeDir, nil
		}
		return "", errors.New("can't find user home directory; $HOME is empty")
	}
}

type userAgentTransport struct {
	rt http.RoundTripper
}

func (uat userAgentTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	r.Header.Set("User-Agent", "golang-x-build-version/devel")
	return uat.rt.RoundTrip(r)
}

// dedupEnv returns a copy of env with any duplicates removed, in favor of
// later values.
// Items are expected to be on the normal environment "key=value" form.
// If caseInsensitive is true, the case of keys is ignored.
//
// This function is unnecessary when the binary is
// built with Go 1.9+, but keep it around for now until Go 1.8
// is no longer seen in the wild in common distros.
//
// This is copied verbatim from golang.org/x/build/envutil.Dedup at CL 10301
// (commit a91ae26).
func dedupEnv(caseInsensitive bool, env []string) []string {
	out := make([]string, 0, len(env))
	saw := map[string]int{} // to index in the array
	for _, kv := range env {
		eq := strings.Index(kv, "=")
		if eq < 1 {
			out = append(out, kv)
			continue
		}
		k := kv[:eq]
		if caseInsensitive {
			k = strings.ToLower(k)
		}
		if dupIdx, isDup := saw[k]; isDup {
			out[dupIdx] = kv
		} else {
			saw[k] = len(out)
			out = append(out, kv)
		}
	}
	return out
}
<commit_msg>gotip: don't set User-Agent for http.DefaultTransport<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The gotip command compiles and runs the go command from the development tree.
//
// To install, run:
//
//     $ go get golang.org/dl/gotip
//     $ gotip download
//
// And then use the gotip command as if it were your normal go command.
//
// To update, run "gotip download" again.
package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

func main() {
	log.SetFlags(0)

	root, err := goroot("gotip")
	if err != nil {
		log.Fatalf("gotip: %v", err)
	}

	if len(os.Args) == 2 && os.Args[1] == "download" {
		if err := installTip(root); err != nil {
			log.Fatalf("gotip: %v", err)
		}
		log.Printf("Success. You may now run 'gotip'!")
		os.Exit(0)
	}

	gobin := filepath.Join(root, "bin", "go"+exe())
	if _, err := os.Stat(gobin); err != nil {
		log.Fatalf("gotip: not downloaded. Run 'gotip download' to install to %v", root)
	}

	cmd := exec.Command(gobin, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	newPath := filepath.Join(root, "bin")
	if p := os.Getenv("PATH"); p != "" {
		newPath += string(filepath.ListSeparator) + p
	}
	cmd.Env = dedupEnv(caseInsensitiveEnv, append(os.Environ(), "GOROOT="+root, "PATH="+newPath))
	if err := cmd.Run(); err != nil {
		if _, ok := err.(*exec.ExitError); ok {
			// TODO: return the same exit status maybe.
			os.Exit(1)
		}
		log.Fatalf("gotip: failed to execute %v: %v", gobin, err)
	}
	os.Exit(0)
}

func installTip(root string) error {
	git := func(args ...string) error {
		cmd := exec.Command("git", args...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Dir = root
		return cmd.Run()
	}

	if _, err := os.Stat(filepath.Join(root, ".git")); err != nil {
		if err := os.MkdirAll(root, 0755); err != nil {
			return fmt.Errorf("failed to create repository: %v", err)
		}
		if err := git("clone", "--depth=1", "https://go.googlesource.com/go", root); err != nil {
			return fmt.Errorf("failed to clone git repository: %v", err)
		}
	} else {
		log.Printf("Updating the go development tree...")
		if err := git("fetch", "origin"); err != nil {
			return fmt.Errorf("failed to fetch git repository updates: %v", err)
		}
	}

	// Use checkout and a detached HEAD, because it will refuse to overwrite
	// local changes, and warn if commits are being left behind, but will not
	// mind if master is force-pushed upstream.
	if err := git("-c", "advice.detachedHead=false", "checkout", "origin/master"); err != nil {
		return fmt.Errorf("failed to checkout git repository: %v", err)
	}
	// It shouldn't be the case, but in practice sometimes binary artifacts
	// generated by earlier Go versions interfere with the build.
	//
	// Ask the user what to do about them if they are not gitignored. They might
	// be artifacts that used to be ignored in previous versions, or precious
	// uncommitted source files.
	if err := git("clean", "-i", "-d"); err != nil {
		return fmt.Errorf("failed to cleanup git repository: %v", err)
	}
	// Wipe away probably boring ignored files without bothering the user.
	if err := git("clean", "-q", "-f", "-d", "-X"); err != nil {
		return fmt.Errorf("failed to cleanup git repository: %v", err)
	}

	cmd := exec.Command(filepath.Join(root, "src", makeScript()))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Dir = filepath.Join(root, "src")
	if runtime.GOOS == "windows" {
		// Workaround make.bat not autodetecting GOROOT_BOOTSTRAP. Issue 28641.
		goroot, err := exec.Command("go", "env", "GOROOT").Output()
		if err != nil {
			return fmt.Errorf("failed to detect an existing go installation for bootstrap: %v", err)
		}
		cmd.Env = append(os.Environ(), "GOROOT_BOOTSTRAP="+strings.TrimSpace(string(goroot)))
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to build go: %v", err)
	}

	return nil
}

func makeScript() string {
	switch runtime.GOOS {
	case "plan9":
		return "make.rc"
	case "windows":
		return "make.bat"
	default:
		return "make.bash"
	}
}

const caseInsensitiveEnv = runtime.GOOS == "windows"

func exe() string {
	if runtime.GOOS == "windows" {
		return ".exe"
	}
	return ""
}

func goroot(version string) (string, error) {
	home, err := homedir()
	if err != nil {
		return "", fmt.Errorf("failed to get home directory: %v", err)
	}
	return filepath.Join(home, "sdk", version), nil
}

func homedir() (string, error) {
	// This could be replaced with os.UserHomeDir, but it was introduced too
	// recently, and we want this to work with go as packaged by Linux
	// distributions. Note that user.Current is not enough as it does not
	// prioritize $HOME. See also Issue 26463.
	switch runtime.GOOS {
	case "plan9":
		return "", fmt.Errorf("%q not yet supported", runtime.GOOS)
	case "windows":
		if dir := os.Getenv("USERPROFILE"); dir != "" {
			return dir, nil
		}
		return "", errors.New("can't find user home directory; %USERPROFILE% is empty")
	default:
		if dir := os.Getenv("HOME"); dir != "" {
			return dir, nil
		}
		if u, err := user.Current(); err == nil && u.HomeDir != "" {
			return u.HomeDir, nil
		}
		return "", errors.New("can't find user home directory; $HOME is empty")
	}
}

// dedupEnv returns a copy of env with any duplicates removed, in favor of
// later values.
// Items are expected to be on the normal environment "key=value" form.
// If caseInsensitive is true, the case of keys is ignored.
//
// This function is unnecessary when the binary is
// built with Go 1.9+, but keep it around for now until Go 1.8
// is no longer seen in the wild in common distros.
//
// This is copied verbatim from golang.org/x/build/envutil.Dedup at CL 10301
// (commit a91ae26).
func dedupEnv(caseInsensitive bool, env []string) []string {
	out := make([]string, 0, len(env))
	saw := map[string]int{} // to index in the array
	for _, kv := range env {
		eq := strings.Index(kv, "=")
		if eq < 1 {
			out = append(out, kv)
			continue
		}
		k := kv[:eq]
		if caseInsensitive {
			k = strings.ToLower(k)
		}
		if dupIdx, isDup := saw[k]; isDup {
			out[dupIdx] = kv
		} else {
			saw[k] = len(out)
			out = append(out, kv)
		}
	}
	return out
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package x509

import (
	"runtime"
	"testing"
	"time"
)

func TestSystemRoots(t *testing.T) {
	switch runtime.GOARCH {
	case "arm", "arm64":
		t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
	}

	t0 := time.Now()
	sysRoots := systemRootsPool() // actual system roots
	sysRootsDuration := time.Since(t0)

	t1 := time.Now()
	execRoots, err := execSecurityRoots() // non-cgo roots
	execSysRootsDuration := time.Since(t1)

	if err != nil {
		t.Fatalf("failed to read system roots: %v", err)
	}

	t.Logf("    cgo sys roots: %v", sysRootsDuration)
	t.Logf("non-cgo sys roots: %v", execSysRootsDuration)

	for _, tt := range []*CertPool{sysRoots, execRoots} {
		if tt == nil {
			t.Fatal("no system roots")
		}
		// On Mavericks, there are 212 bundled certs, at least
		// there was at one point in time on one machine.
		// (Maybe it was a corp laptop with extra certs?)
		// Other OS X users report
		// 135, 142, 145...  Let's try requiring at least 100,
		// since this is just a sanity check.
		t.Logf("got %d roots", len(tt.certs))
		if want, have := 100, len(tt.certs); have < want {
			t.Fatalf("want at least %d system roots, have %d", want, have)
		}
	}

	// Check that the two cert pools are roughly the same;
	// |A∩B| > max(|A|, |B|) / 2 should be a reasonably robust check.

	isect := make(map[string]bool, len(sysRoots.certs))
	for _, c := range sysRoots.certs {
		isect[string(c.Raw)] = true
	}

	have := 0
	for _, c := range execRoots.certs {
		if isect[string(c.Raw)] {
			have++
		}
	}

	var want int
	if nsys, nexec := len(sysRoots.certs), len(execRoots.certs); nsys > nexec {
		want = nsys / 2
	} else {
		want = nexec / 2
	}

	if have < want {
		t.Errorf("insufficient overlap between cgo and non-cgo roots; want at least %d, have %d", want, have)
	}
}
<commit_msg>[release-branch.go1.9] crypto/x509: skip TestSystemRoots<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package x509

import (
	"runtime"
	"testing"
	"time"
)

func TestSystemRoots(t *testing.T) {
	switch runtime.GOARCH {
	case "arm", "arm64":
		t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
	}

	switch runtime.GOOS {
	case "darwin":
		t.Skipf("skipping on %s/%s until cgo part of golang.org/issue/16532 has been implemented.", runtime.GOOS, runtime.GOARCH)
	}

	t0 := time.Now()
	sysRoots := systemRootsPool() // actual system roots
	sysRootsDuration := time.Since(t0)

	t1 := time.Now()
	execRoots, err := execSecurityRoots() // non-cgo roots
	execSysRootsDuration := time.Since(t1)

	if err != nil {
		t.Fatalf("failed to read system roots: %v", err)
	}

	t.Logf("    cgo sys roots: %v", sysRootsDuration)
	t.Logf("non-cgo sys roots: %v", execSysRootsDuration)

	for _, tt := range []*CertPool{sysRoots, execRoots} {
		if tt == nil {
			t.Fatal("no system roots")
		}
		// On Mavericks, there are 212 bundled certs, at least
		// there was at one point in time on one machine.
		// (Maybe it was a corp laptop with extra certs?)
		// Other OS X users report
		// 135, 142, 145...  Let's try requiring at least 100,
		// since this is just a sanity check.
		t.Logf("got %d roots", len(tt.certs))
		if want, have := 100, len(tt.certs); have < want {
			t.Fatalf("want at least %d system roots, have %d", want, have)
		}
	}

	// Check that the two cert pools are roughly the same;
	// |A∩B| > max(|A|, |B|) / 2 should be a reasonably robust check.

	isect := make(map[string]bool, len(sysRoots.certs))
	for _, c := range sysRoots.certs {
		isect[string(c.Raw)] = true
	}

	have := 0
	for _, c := range execRoots.certs {
		if isect[string(c.Raw)] {
			have++
		}
	}

	var want int
	if nsys, nexec := len(sysRoots.certs), len(execRoots.certs); nsys > nexec {
		want = nsys / 2
	} else {
		want = nexec / 2
	}

	if have < want {
		t.Errorf("insufficient overlap between cgo and non-cgo roots; want at least %d, have %d", want, have)
	}
}
<|endoftext|>


<commit_before>package torrent

import (
	"io"
	"net"
	"sync"
	"testing"
	"time"

	"github.com/anacrolix/missinggo/pubsub"
	"github.com/bradfitz/iter"
	"github.com/stretchr/testify/require"

	"github.com/anacrolix/torrent/metainfo"
	pp "github.com/anacrolix/torrent/peer_protocol"
	"github.com/anacrolix/torrent/storage"
)

// Ensure that no race exists between sending a bitfield, and a subsequent
// Have that would potentially alter it.
func TestSendBitfieldThenHave(t *testing.T) {
	r, w := io.Pipe()
	cl := Client{
		config: &ClientConfig{DownloadRateLimiter: unlimited},
	}
	cl.initLogger()
	c := cl.newConnection(nil, false)
	c.setTorrent(cl.newTorrent(metainfo.Hash{}, nil))
	c.t.setInfo(&metainfo.Info{
		Pieces: make([]byte, metainfo.HashSize*3),
	})
	c.r = r
	c.w = w
	go c.writer(time.Minute)
	c.mu().Lock()
	c.t.completedPieces.Add(1)
	c.PostBitfield( /*[]bool{false, true, false}*/ )
	c.mu().Unlock()
	c.mu().Lock()
	c.Have(2)
	c.mu().Unlock()
	b := make([]byte, 15)
	n, err := io.ReadFull(r, b)
	c.mu().Lock()
	// This will cause connection.writer to terminate.
	c.closed.Set()
	c.mu().Unlock()
	require.NoError(t, err)
	require.EqualValues(t, 15, n)
	// Here we see that the bitfield doesn't have piece 2 set, as that should
	// arrive in the following Have message.
	require.EqualValues(t, "\x00\x00\x00\x02\x05@\x00\x00\x00\x05\x04\x00\x00\x00\x02", string(b))
}

type torrentStorage struct {
	writeSem sync.Mutex
}

func (me *torrentStorage) Close() error { return nil }

func (me *torrentStorage) Piece(mp metainfo.Piece) storage.PieceImpl {
	return me
}

func (me *torrentStorage) Completion() storage.Completion {
	return storage.Completion{}
}

func (me *torrentStorage) MarkComplete() error {
	return nil
}

func (me *torrentStorage) MarkNotComplete() error {
	return nil
}

func (me *torrentStorage) ReadAt([]byte, int64) (int, error) {
	panic("shouldn't be called")
}

func (me *torrentStorage) WriteAt(b []byte, _ int64) (int, error) {
	if len(b) != defaultChunkSize {
		panic(len(b))
	}
	me.writeSem.Unlock()
	return len(b), nil
}

func BenchmarkConnectionMainReadLoop(b *testing.B) {
	cl := &Client{
		config: &ClientConfig{
			DownloadRateLimiter: unlimited,
		},
	}
	ts := &torrentStorage{}
	t := &Torrent{
		cl:                cl,
		storage:           &storage.Torrent{ts},
		pieceStateChanges: pubsub.NewPubSub(),
	}
	require.NoError(b, t.setInfo(&metainfo.Info{
		Pieces:      make([]byte, 20),
		Length:      1 << 20,
		PieceLength: 1 << 20,
	}))
	t.setChunkSize(defaultChunkSize)
	t.pendingPieces.Set(0, PiecePriorityNormal.BitmapPriority())
	r, w := net.Pipe()
	cn := cl.newConnection(r, true)
	cn.setTorrent(t)
	mrlErr := make(chan error)
	cl.mu.Lock()
	go func() {
		err := cn.mainReadLoop()
		if err != nil {
			mrlErr <- err
		}
		close(mrlErr)
	}()
	msg := pp.Message{
		Type:  pp.Piece,
		Piece: make([]byte, defaultChunkSize),
	}
	wb, err := msg.MarshalBinary()
	require.NoError(b, err)
	b.SetBytes(int64(len(msg.Piece)))
	ts.writeSem.Lock()
	for range iter.N(b.N) {
		cl.mu.Lock()
		t.pieces[0].dirtyChunks.Clear()
		cl.mu.Unlock()
		n, err := w.Write(wb)
		require.NoError(b, err)
		require.EqualValues(b, len(wb), n)
		ts.writeSem.Lock()
	}
	w.Close()
	require.NoError(b, <-mrlErr)
	require.EqualValues(b, b.N, cn.stats.ChunksReadUseful.Int64())
}
<commit_msg>Fix BenchmarkConnectionMainReadLoop<commit_after>package torrent

import (
	"io"
	"net"
	"sync"
	"testing"
	"time"

	"github.com/anacrolix/missinggo/pubsub"
	"github.com/bradfitz/iter"
	"github.com/stretchr/testify/require"

	"github.com/anacrolix/torrent/metainfo"
	pp "github.com/anacrolix/torrent/peer_protocol"
	"github.com/anacrolix/torrent/storage"
)

// Ensure that no race exists between sending a bitfield, and a subsequent
// Have that would potentially alter it.
func TestSendBitfieldThenHave(t *testing.T) {
	r, w := io.Pipe()
	cl := Client{
		config: &ClientConfig{DownloadRateLimiter: unlimited},
	}
	cl.initLogger()
	c := cl.newConnection(nil, false)
	c.setTorrent(cl.newTorrent(metainfo.Hash{}, nil))
	c.t.setInfo(&metainfo.Info{
		Pieces: make([]byte, metainfo.HashSize*3),
	})
	c.r = r
	c.w = w
	go c.writer(time.Minute)
	c.mu().Lock()
	c.t.completedPieces.Add(1)
	c.PostBitfield( /*[]bool{false, true, false}*/ )
	c.mu().Unlock()
	c.mu().Lock()
	c.Have(2)
	c.mu().Unlock()
	b := make([]byte, 15)
	n, err := io.ReadFull(r, b)
	c.mu().Lock()
	// This will cause connection.writer to terminate.
	c.closed.Set()
	c.mu().Unlock()
	require.NoError(t, err)
	require.EqualValues(t, 15, n)
	// Here we see that the bitfield doesn't have piece 2 set, as that should
	// arrive in the following Have message.
	require.EqualValues(t, "\x00\x00\x00\x02\x05@\x00\x00\x00\x05\x04\x00\x00\x00\x02", string(b))
}

type torrentStorage struct {
	writeSem sync.Mutex
}

func (me *torrentStorage) Close() error { return nil }

func (me *torrentStorage) Piece(mp metainfo.Piece) storage.PieceImpl {
	return me
}

func (me *torrentStorage) Completion() storage.Completion {
	return storage.Completion{}
}

func (me *torrentStorage) MarkComplete() error {
	return nil
}

func (me *torrentStorage) MarkNotComplete() error {
	return nil
}

func (me *torrentStorage) ReadAt([]byte, int64) (int, error) {
	panic("shouldn't be called")
}

func (me *torrentStorage) WriteAt(b []byte, _ int64) (int, error) {
	if len(b) != defaultChunkSize {
		panic(len(b))
	}
	me.writeSem.Unlock()
	return len(b), nil
}

func BenchmarkConnectionMainReadLoop(b *testing.B) {
	cl := &Client{
		config: &ClientConfig{
			DownloadRateLimiter: unlimited,
		},
	}
	ts := &torrentStorage{}
	t := &Torrent{
		cl:                cl,
		storage:           &storage.Torrent{ts},
		pieceStateChanges: pubsub.NewPubSub(),
	}
	require.NoError(b, t.setInfo(&metainfo.Info{
		Pieces:      make([]byte, 20),
		Length:      1 << 20,
		PieceLength: 1 << 20,
	}))
	t.setChunkSize(defaultChunkSize)
	t.pendingPieces.Set(0, PiecePriorityNormal.BitmapPriority())
	r, w := net.Pipe()
	cn := cl.newConnection(r, true)
	cn.setTorrent(t)
	mrlErr := make(chan error)
	msg := pp.Message{
		Type:  pp.Piece,
		Piece: make([]byte, defaultChunkSize),
	}
	go func() {
		cl.mu.Lock()
		err := cn.mainReadLoop()
		if err != nil {
			mrlErr <- err
		}
		close(mrlErr)
	}()
	wb := msg.MustMarshalBinary()
	b.SetBytes(int64(len(msg.Piece)))
	go func() {
		defer w.Close()
		ts.writeSem.Lock()
		for range iter.N(b.N) {
			cl.mu.Lock()
			// The chunk must be written to storage everytime, to ensure the
			// writeSem is unlocked.
			t.pieces[0].dirtyChunks.Clear()
			cn.validReceiveChunks = map[request]struct{}{newRequestFromMessage(&msg): struct{}{}}
			cl.mu.Unlock()
			n, err := w.Write(wb)
			require.NoError(b, err)
			require.EqualValues(b, len(wb), n)
			ts.writeSem.Lock()
		}
	}()
	require.NoError(b, <-mrlErr)
	require.EqualValues(b, b.N, cn.stats.ChunksReadUseful.Int64())
}
<|endoftext|>


<commit_before>package flags

import (
	"testing"
)

func TestGroupInline(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`

		Group struct {
			G bool `short:"g"`
		} `group:"Grouped Options"`
	}{}

	p, ret := assertParserSuccess(t, &opts, "-v", "-g")

	assertStringArray(t, ret, []string{})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}

	if !opts.Group.G {
		t.Errorf("Expected Group.G to be true")
	}

	if p.Command.Group.Find("Grouped Options") == nil {
		t.Errorf("Expected to find group `Grouped Options'")
	}
}

func TestGroupAdd(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`
	}{}

	var grp = struct {
		G bool `short:"g"`
	}{}

	p := NewParser(&opts, Default)
	g, err := p.AddGroup("Grouped Options", "", &grp)

	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
		return
	}

	ret, err := p.ParseArgs([]string{"-v", "-g", "rest"})

	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
		return
	}

	assertStringArray(t, ret, []string{"rest"})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}

	if !grp.G {
		t.Errorf("Expected Group.G to be true")
	}

	if p.Command.Group.Find("Grouped Options") != g {
		t.Errorf("Expected to find group `Grouped Options'")
	}

	if p.Groups()[1] != g {
		t.Errorf("Espected group #v,	 but got #v", g, p.Groups()[0])
	}

	if g.Options()[0].ShortName != 'g' {
		t.Errorf("Expected short name `g' but got %v", g.Options()[0].ShortName)
	}
}

func TestGroupNestedInline(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`

		Group struct {
			G bool `short:"g"`

			Nested struct {
				N string `long:"n"`
			} `group:"Nested Options"`
		} `group:"Grouped Options"`
	}{}

	p, ret := assertParserSuccess(t, &opts, "-v", "-g", "--n", "n", "rest")

	assertStringArray(t, ret, []string{"rest"})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}

	if !opts.Group.G {
		t.Errorf("Expected Group.G to be true")
	}

	assertString(t, opts.Group.Nested.N, "n")

	if p.Command.Group.Find("Grouped Options") == nil {
		t.Errorf("Expected to find group `Grouped Options'")
	}

	if p.Command.Group.Find("Nested Options") == nil {
		t.Errorf("Expected to find group `Nested Options'")
	}
}

func TestDuplicateShortFlags(t *testing.T) {
	var opts struct {
		Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
		Variables []string `short:"v" long:"variable" description:"Set a variable value."`
	}

	args := []string{
		"--verbose",
		"-v", "123",
		"-v", "456",
	}

	_, err := flags.ParseArgs(&opts, args)

	if err == nil {
		t.Errorf("Expected an error with type ErrDuplicatedFlag")
	} else {
		err2 := err.(*flags.Error)
		if err2.Type != flags.ErrDuplicatedFlag {
			t.Errorf("Expected an error with type ErrDuplicatedFlag")
		}
	}
}

func TestDuplicateLongFlags(t *testing.T) {
	var opts struct {
		Test1 []bool `short:"a" long:"testing" description:"Test 1"`
		Test2 []string `short:"b" long:"testing" description:"Test 2."`
	}

	args := []string{
		"--testing",
	}

	_, err := flags.ParseArgs(&opts, args)

	if err == nil {
		t.Errorf("Expected an error with type ErrDuplicatedFlag")
	} else {
		err2 := err.(*flags.Error)
		if err2.Type != flags.ErrDuplicatedFlag {
			t.Errorf("Expected an error with type ErrDuplicatedFlag")
		}
	}
}
<commit_msg>Fix duplicate tests for tests being in package flags<commit_after>package flags

import (
	"testing"
)

func TestGroupInline(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`

		Group struct {
			G bool `short:"g"`
		} `group:"Grouped Options"`
	}{}

	p, ret := assertParserSuccess(t, &opts, "-v", "-g")

	assertStringArray(t, ret, []string{})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}

	if !opts.Group.G {
		t.Errorf("Expected Group.G to be true")
	}

	if p.Command.Group.Find("Grouped Options") == nil {
		t.Errorf("Expected to find group `Grouped Options'")
	}
}

func TestGroupAdd(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`
	}{}

	var grp = struct {
		G bool `short:"g"`
	}{}

	p := NewParser(&opts, Default)
	g, err := p.AddGroup("Grouped Options", "", &grp)

	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
		return
	}

	ret, err := p.ParseArgs([]string{"-v", "-g", "rest"})

	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
		return
	}

	assertStringArray(t, ret, []string{"rest"})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}

	if !grp.G {
		t.Errorf("Expected Group.G to be true")
	}

	if p.Command.Group.Find("Grouped Options") != g {
		t.Errorf("Expected to find group `Grouped Options'")
	}

	if p.Groups()[1] != g {
		t.Errorf("Espected group #v,	 but got #v", g, p.Groups()[0])
	}

	if g.Options()[0].ShortName != 'g' {
		t.Errorf("Expected short name `g' but got %v", g.Options()[0].ShortName)
	}
}

func TestGroupNestedInline(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`

		Group struct {
			G bool `short:"g"`

			Nested struct {
				N string `long:"n"`
			} `group:"Nested Options"`
		} `group:"Grouped Options"`
	}{}

	p, ret := assertParserSuccess(t, &opts, "-v", "-g", "--n", "n", "rest")

	assertStringArray(t, ret, []string{"rest"})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}

	if !opts.Group.G {
		t.Errorf("Expected Group.G to be true")
	}

	assertString(t, opts.Group.Nested.N, "n")

	if p.Command.Group.Find("Grouped Options") == nil {
		t.Errorf("Expected to find group `Grouped Options'")
	}

	if p.Command.Group.Find("Nested Options") == nil {
		t.Errorf("Expected to find group `Nested Options'")
	}
}

func TestDuplicateShortFlags(t *testing.T) {
	var opts struct {
		Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
		Variables []string `short:"v" long:"variable" description:"Set a variable value."`
	}

	args := []string{
		"--verbose",
		"-v", "123",
		"-v", "456",
	}

	_, err := ParseArgs(&opts, args)

	if err == nil {
		t.Errorf("Expected an error with type ErrDuplicatedFlag")
	} else {
		err2 := err.(*Error)
		if err2.Type != ErrDuplicatedFlag {
			t.Errorf("Expected an error with type ErrDuplicatedFlag")
		}
	}
}

func TestDuplicateLongFlags(t *testing.T) {
	var opts struct {
		Test1 []bool `short:"a" long:"testing" description:"Test 1"`
		Test2 []string `short:"b" long:"testing" description:"Test 2."`
	}

	args := []string{
		"--testing",
	}

	_, err := ParseArgs(&opts, args)

	if err == nil {
		t.Errorf("Expected an error with type ErrDuplicatedFlag")
	} else {
		err2 := err.(*Error)
		if err2.Type != ErrDuplicatedFlag {
			t.Errorf("Expected an error with type ErrDuplicatedFlag")
		}
	}
}
<|endoftext|>


<commit_before>package control

import (
	"bufio"
	"crypto/rsa"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/intelsdilabs/pulse/control/plugin"
	"io"
	"log"
	"os/exec"
	"time"
)

// control private key (RSA private key)
// control public key (RSA public key)
// Plugin token = token generated by plugin and passed to control
// Session token = plugin seed encrypted by control private key, verified by plugin using control public key
//

const (
	// LoadedPlugin Types enum
	CollectorPlugin pluginType = iota
	PublisherPlugin

	// LoadedPlugin States
	DetectedState pluginState = "detected"
	LoadingState  pluginState = "loading"
	LoadedState   pluginState = "loaded"
	UnloadedState pluginState = "unloaded"
)

//
type pluginState string

type pluginType int

type loadedPlugins []LoadedPlugin

type executablePlugins []ExecutablePlugin

// A interface representing an executable plugin.
type PluginExecutor interface {
	Kill() error
	Wait() error
	ResponseReader() io.Reader
}

// Represents a plugin loaded or loading into control
type LoadedPlugin struct {
	Path  string
	Type  pluginType
	State pluginState
}

type pluginControl struct {
	// TODO, going to need coordination on changing of these
	LoadedPlugins  loadedPlugins
	RunningPlugins executablePlugins
	Started        bool

	loadRequestsChan chan LoadedPlugin

	controlPrivKey *rsa.PrivateKey
	controlPubKey  *rsa.PublicKey
}

func (p *pluginControl) GenerateArgs(daemon bool) plugin.Arg {
	a := plugin.Arg{
		ControlPubKey: p.controlPubKey,
		PluginLogPath: "/tmp",
		RunAsDaemon:   daemon,
	}
	return a
}

func Control() *pluginControl {
	c := new(pluginControl)
	c.loadRequestsChan = make(chan LoadedPlugin)
	// privatekey, err := rsa.GenerateKey(rand.Reader, 4096)

	// if err != nil {
	// 	panic(err)
	// }

	// // Future use for securing.
	// c.controlPrivKey = privatekey
	// c.controlPubKey = &privatekey.PublicKey

	return c
}

// Begin handling load, unload, and inventory
func (p *pluginControl) Start() {
	// begin controlling

	// Start load handler. We only start one to keep load requests handled in
	// a linear fashion for now as this is a low priority.
	go p.HandleLoadRequests()

	p.Started = true
}

func (p *pluginControl) Stop() {
	close(p.loadRequestsChan)
}

// Handles loading of plugins. One at a time.
func (p *pluginControl) HandleLoadRequests() {
	for {
		lPlugin := <-p.loadRequestsChan

		// Create a new Executable plugin
		//
		// In this case we only support Linux right now
		ePlugin, err := newExecutablePlugin(p, lPlugin.Path, false)

		// If error then log and return
		if err != nil {
			log.Println(err)
			return
		}

		// Start the plugin using the start method
		err = ePlugin.Start()
		if err != nil {
			log.Println(err)
			return
		}

		var resp *plugin.Response
		// This blocks until a response or an error
		resp, err = waitForResponse(ePlugin, time.Second*3)
		// resp, err = WaitForPluginResponse(ePlugin, time.Second*3)

		// If error then we log and return

		// On response we create a LoadedPlugin
		// and add to LoadedPlugins index

		fmt.Println(resp, err)

	}
}

func (p *pluginControl) Load(path string) {
	if !p.Started {
		panic("Must start plugin control before calling Load()")
	}

	/*
		Loading plugin status

		Before start (todo)
		* executable (caught on start)
		* signed? (todo)
		* Grab checksum (file watching? todo)
		=> Plugin state = detected

		After start before Ping
		* starts? (catch crash)
		* response? (catch stdout)
		=> Plugin state = loaded
	*/

	log.Printf("Attempting to load: %s\v", path)

	lPlugin := LoadedPlugin{Path: path}
	p.loadRequestsChan <- lPlugin

	/*
		// Start plugin passing control details and receiving response

		x, e := json.Marshal(p.GenerateArgs())
		if e != nil {
			panic(e)
		}
		log.Println(string(x))

		cmd := new(exec.Cmd)
		cmd.Path = path
		cmd.Args = []string{path, string(x)}
		stdout, err := cmd.StdoutPipe()
		// cmd.Stdout = os.Stdout
		// cmd.Stderr = os.Stdout

		scanner := bufio.NewScanner(stdout)
		go func() {
			for scanner.Scan() {
				b := scanner.Bytes()
				r := plugin.Response{}
				err := json.Unmarshal(b, &r)
				if err != nil {
					panic(err)
				}
				fmt.Printf("%v\n", r)
			}
		}()

		err = cmd.Start()
		if err != nil {
			panic(err)
		}

		// var b []byte

		// b, err = ioutil.ReadAll(stdout)
		// if err != nil {
		// 	panic(err)
		// }
		// log.Println("Response:" + string(b))

		go func() {
			// How long to wait for testing for killing
			time.Sleep(time.Second * 10)
			cmd.Process.Kill()
		}()

		cmd.Wait()

		// Ping

		// Plugin
	*/
}

// Wait for response from started ExecutablePlugin. Returns plugin.Response or error.
func waitForResponse(p PluginExecutor, timeout time.Duration) (*plugin.Response, error) {
	// The response we want to return

	var resp *plugin.Response = new(plugin.Response)
	var timeoutErr error
	var jsonErr error

	// Kill on timeout
	go func() {
		time.Sleep(timeout)
		timeoutErr = errors.New("Timeout waiting for response")
		p.Kill()
		return
	}()

	// Wait for response
	scanner := bufio.NewScanner(p.ResponseReader())
	go func() {
		for scanner.Scan() {
			// Get bytes
			b := scanner.Bytes()
			// attempt to unmarshall into struct
			err := json.Unmarshal(b, resp)
			if err != nil {
				jsonErr = errors.New("JSONError - " + err.Error())
				return
			}
		}
	}()

	// Wait for PluginExecutor to respond
	err := p.Wait()
	// Return top level error
	if jsonErr != nil {
		return nil, jsonErr
	}
	// Return top level error
	if timeoutErr != nil {
		return nil, timeoutErr
	}
	// Return pExecutor.Wait() error
	if err != nil {
		// log.Printf("[CONTROL] Plugin stopped with error [%v]\n", err)
		return nil, err
	}
	// Return response
	return resp, nil
}

// Initialize a new ExecutablePlugin from path to executable and daemon mode (true or false)
func newExecutablePlugin(p *pluginControl, path string, daemon bool) (*ExecutablePlugin, error) {
	jsonArgs, err := json.Marshal(p.GenerateArgs(daemon))
	if err != nil {
		return nil, err
	}
	// Init the cmd
	cmd := new(exec.Cmd)
	cmd.Path = path
	cmd.Args = []string{path, string(jsonArgs)}
	// Link the stdout for response reading
	stdout, err2 := cmd.StdoutPipe()
	if err2 != nil {
		return nil, err2
	}
	// Init the ExecutablePlugin and return
	ePlugin := new(ExecutablePlugin)
	ePlugin.cmd = cmd
	ePlugin.stdout = stdout

	return ePlugin, nil
}
<commit_msg>Remove old comments<commit_after>package control

import (
	"bufio"
	"crypto/rsa"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/intelsdilabs/pulse/control/plugin"
	"io"
	"log"
	"os/exec"
	"time"
)

// control private key (RSA private key)
// control public key (RSA public key)
// Plugin token = token generated by plugin and passed to control
// Session token = plugin seed encrypted by control private key, verified by plugin using control public key
//

const (
	// LoadedPlugin Types enum
	CollectorPlugin pluginType = iota
	PublisherPlugin

	// LoadedPlugin States
	DetectedState pluginState = "detected"
	LoadingState  pluginState = "loading"
	LoadedState   pluginState = "loaded"
	UnloadedState pluginState = "unloaded"
)

//
type pluginState string

type pluginType int

type loadedPlugins []LoadedPlugin

type executablePlugins []ExecutablePlugin

// A interface representing an executable plugin.
type PluginExecutor interface {
	Kill() error
	Wait() error
	ResponseReader() io.Reader
}

// Represents a plugin loaded or loading into control
type LoadedPlugin struct {
	Path  string
	Type  pluginType
	State pluginState
}

type pluginControl struct {
	// TODO, going to need coordination on changing of these
	LoadedPlugins  loadedPlugins
	RunningPlugins executablePlugins
	Started        bool

	loadRequestsChan chan LoadedPlugin

	controlPrivKey *rsa.PrivateKey
	controlPubKey  *rsa.PublicKey
}

func (p *pluginControl) GenerateArgs(daemon bool) plugin.Arg {
	a := plugin.Arg{
		ControlPubKey: p.controlPubKey,
		PluginLogPath: "/tmp",
		RunAsDaemon:   daemon,
	}
	return a
}

func Control() *pluginControl {
	c := new(pluginControl)
	c.loadRequestsChan = make(chan LoadedPlugin)
	// privatekey, err := rsa.GenerateKey(rand.Reader, 4096)

	// if err != nil {
	// 	panic(err)
	// }

	// // Future use for securing.
	// c.controlPrivKey = privatekey
	// c.controlPubKey = &privatekey.PublicKey

	return c
}

// Begin handling load, unload, and inventory
func (p *pluginControl) Start() {
	// begin controlling

	// Start load handler. We only start one to keep load requests handled in
	// a linear fashion for now as this is a low priority.
	go p.HandleLoadRequests()

	p.Started = true
}

func (p *pluginControl) Stop() {
	close(p.loadRequestsChan)
}

// Handles loading of plugins. One at a time.
func (p *pluginControl) HandleLoadRequests() {
	for {
		lPlugin := <-p.loadRequestsChan

		// Create a new Executable plugin
		//
		// In this case we only support Linux right now
		ePlugin, err := newExecutablePlugin(p, lPlugin.Path, false)

		// If error then log and return
		if err != nil {
			log.Println(err)
			return
		}

		// Start the plugin using the start method
		err = ePlugin.Start()
		if err != nil {
			log.Println(err)
			return
		}

		var resp *plugin.Response
		// This blocks until a response or an error
		resp, err = waitForResponse(ePlugin, time.Second*3)
		// resp, err = WaitForPluginResponse(ePlugin, time.Second*3)

		// If error then we log and return

		// On response we create a LoadedPlugin
		// and add to LoadedPlugins index

		fmt.Println(resp, err)

	}
}

func (p *pluginControl) Load(path string) {
	if !p.Started {
		panic("Must start plugin control before calling Load()")
	}

	/*
		Loading plugin status

		Before start (todo)
		* executable (caught on start)
		* signed? (todo)
		* Grab checksum (file watching? todo)
		=> Plugin state = detected

		After start before Ping
		* starts? (catch crash)
		* response? (catch stdout)
		=> Plugin state = loaded
	*/

	log.Printf("Attempting to load: %s\v", path)

	lPlugin := LoadedPlugin{Path: path}
	p.loadRequestsChan <- lPlugin
}

// Wait for response from started ExecutablePlugin. Returns plugin.Response or error.
func waitForResponse(p PluginExecutor, timeout time.Duration) (*plugin.Response, error) {
	// The response we want to return

	var resp *plugin.Response = new(plugin.Response)
	var timeoutErr error
	var jsonErr error

	// Kill on timeout
	go func() {
		time.Sleep(timeout)
		timeoutErr = errors.New("Timeout waiting for response")
		p.Kill()
		return
	}()

	// Wait for response
	scanner := bufio.NewScanner(p.ResponseReader())
	go func() {
		for scanner.Scan() {
			// Get bytes
			b := scanner.Bytes()
			// attempt to unmarshall into struct
			err := json.Unmarshal(b, resp)
			if err != nil {
				jsonErr = errors.New("JSONError - " + err.Error())
				return
			}
		}
	}()

	// Wait for PluginExecutor to respond
	err := p.Wait()
	// Return top level error
	if jsonErr != nil {
		return nil, jsonErr
	}
	// Return top level error
	if timeoutErr != nil {
		return nil, timeoutErr
	}
	// Return pExecutor.Wait() error
	if err != nil {
		// log.Printf("[CONTROL] Plugin stopped with error [%v]\n", err)
		return nil, err
	}
	// Return response
	return resp, nil
}

// Initialize a new ExecutablePlugin from path to executable and daemon mode (true or false)
func newExecutablePlugin(p *pluginControl, path string, daemon bool) (*ExecutablePlugin, error) {
	jsonArgs, err := json.Marshal(p.GenerateArgs(daemon))
	if err != nil {
		return nil, err
	}
	// Init the cmd
	cmd := new(exec.Cmd)
	cmd.Path = path
	cmd.Args = []string{path, string(jsonArgs)}
	// Link the stdout for response reading
	stdout, err2 := cmd.StdoutPipe()
	if err2 != nil {
		return nil, err2
	}
	// Init the ExecutablePlugin and return
	ePlugin := new(ExecutablePlugin)
	ePlugin.cmd = cmd
	ePlugin.stdout = stdout

	return ePlugin, nil
}
<|endoftext|>


<commit_before>// Package memory provides a memory broker
package memory

import (
	"errors"
	"math/rand"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/micro/go-micro/v2/broker"
	maddr "github.com/micro/go-micro/v2/util/addr"
	mnet "github.com/micro/go-micro/v2/util/net"
)

type memoryBroker struct {
	opts broker.Options

	addr string
	sync.RWMutex
	connected   bool
	Subscribers map[string][]*memorySubscriber
}

type memoryEvent struct {
	topic   string
	message *broker.Message
}

type memorySubscriber struct {
	id      string
	topic   string
	exit    chan bool
	handler broker.Handler
	opts    broker.SubscribeOptions
}

func (m *memoryBroker) Options() broker.Options {
	return m.opts
}

func (m *memoryBroker) Address() string {
	return m.addr
}

func (m *memoryBroker) Connect() error {
	m.Lock()
	defer m.Unlock()

	if m.connected {
		return nil
	}

	addr, err := maddr.Extract("::")
	if err != nil {
		return err
	}
	i := rand.Intn(20000)
	// set addr with port
	addr = mnet.HostPort(addr, 10000+i)

	m.addr = addr
	m.connected = true

	return nil
}

func (m *memoryBroker) Disconnect() error {
	m.Lock()
	defer m.Unlock()

	if !m.connected {
		return nil
	}

	m.connected = false

	return nil
}

func (m *memoryBroker) Init(opts ...broker.Option) error {
	for _, o := range opts {
		o(&m.opts)
	}
	return nil
}

func (m *memoryBroker) Publish(topic string, message *broker.Message, opts ...broker.PublishOption) error {
	m.RLock()
	if !m.connected {
		m.RUnlock()
		return errors.New("not connected")
	}

	subs, ok := m.Subscribers[topic]
	m.RUnlock()
	if !ok {
		return nil
	}

	p := &memoryEvent{
		topic:   topic,
		message: message,
	}

	for _, sub := range subs {
		if err := sub.handler(p); err != nil {
			return err
		}
	}

	return nil
}

func (m *memoryBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
	m.RLock()
	if !m.connected {
		m.RUnlock()
		return nil, errors.New("not connected")
	}
	m.RUnlock()

	var options broker.SubscribeOptions
	for _, o := range opts {
		o(&options)
	}

	sub := &memorySubscriber{
		exit:    make(chan bool, 1),
		id:      uuid.New().String(),
		topic:   topic,
		handler: handler,
		opts:    options,
	}

	m.Lock()
	m.Subscribers[topic] = append(m.Subscribers[topic], sub)
	m.Unlock()

	go func() {
		<-sub.exit
		m.Lock()
		var newSubscribers []*memorySubscriber
		for _, sb := range m.Subscribers[topic] {
			if sb.id == sub.id {
				continue
			}
			newSubscribers = append(newSubscribers, sb)
		}
		m.Subscribers[topic] = newSubscribers
		m.Unlock()
	}()

	return sub, nil
}

func (m *memoryBroker) String() string {
	return "memory"
}

func (m *memoryEvent) Topic() string {
	return m.topic
}

func (m *memoryEvent) Message() *broker.Message {
	return m.message
}

func (m *memoryEvent) Ack() error {
	return nil
}

func (m *memorySubscriber) Options() broker.SubscribeOptions {
	return m.opts
}

func (m *memorySubscriber) Topic() string {
	return m.topic
}

func (m *memorySubscriber) Unsubscribe() error {
	m.exit <- true
	return nil
}

func NewBroker(opts ...broker.Option) broker.Broker {
	var options broker.Options
	rand.Seed(time.Now().UnixNano())
	for _, o := range opts {
		o(&options)
	}

	return &memoryBroker{
		opts:        options,
		Subscribers: make(map[string][]*memorySubscriber),
	}
}
<commit_msg>broker/memory: add codec support (#1276)<commit_after>// Package memory provides a memory broker
package memory

import (
	"context"
	"errors"
	"math/rand"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/micro/go-micro/v2/broker"
	log "github.com/micro/go-micro/v2/logger"
	maddr "github.com/micro/go-micro/v2/util/addr"
	mnet "github.com/micro/go-micro/v2/util/net"
)

type memoryBroker struct {
	opts broker.Options

	addr string
	sync.RWMutex
	connected   bool
	Subscribers map[string][]*memorySubscriber
}

type memoryEvent struct {
	opts    broker.Options
	topic   string
	message interface{}
}

type memorySubscriber struct {
	id      string
	topic   string
	exit    chan bool
	handler broker.Handler
	opts    broker.SubscribeOptions
}

func (m *memoryBroker) Options() broker.Options {
	return m.opts
}

func (m *memoryBroker) Address() string {
	return m.addr
}

func (m *memoryBroker) Connect() error {
	m.Lock()
	defer m.Unlock()

	if m.connected {
		return nil
	}

	addr, err := maddr.Extract("::")
	if err != nil {
		return err
	}
	i := rand.Intn(20000)
	// set addr with port
	addr = mnet.HostPort(addr, 10000+i)

	m.addr = addr
	m.connected = true

	return nil
}

func (m *memoryBroker) Disconnect() error {
	m.Lock()
	defer m.Unlock()

	if !m.connected {
		return nil
	}

	m.connected = false

	return nil
}

func (m *memoryBroker) Init(opts ...broker.Option) error {
	for _, o := range opts {
		o(&m.opts)
	}
	return nil
}

func (m *memoryBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
	m.RLock()
	if !m.connected {
		m.RUnlock()
		return errors.New("not connected")
	}

	subs, ok := m.Subscribers[topic]
	m.RUnlock()
	if !ok {
		return nil
	}

	var v interface{}
	if m.opts.Codec != nil {
		buf, err := m.opts.Codec.Marshal(msg)
		if err != nil {
			return err
		}
		v = buf
	} else {
		v = msg
	}

	p := &memoryEvent{
		topic:   topic,
		message: v,
		opts:    m.opts,
	}

	for _, sub := range subs {
		if err := sub.handler(p); err != nil {
			return err
		}
	}

	return nil
}

func (m *memoryBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
	m.RLock()
	if !m.connected {
		m.RUnlock()
		return nil, errors.New("not connected")
	}
	m.RUnlock()

	var options broker.SubscribeOptions
	for _, o := range opts {
		o(&options)
	}

	sub := &memorySubscriber{
		exit:    make(chan bool, 1),
		id:      uuid.New().String(),
		topic:   topic,
		handler: handler,
		opts:    options,
	}

	m.Lock()
	m.Subscribers[topic] = append(m.Subscribers[topic], sub)
	m.Unlock()

	go func() {
		<-sub.exit
		m.Lock()
		var newSubscribers []*memorySubscriber
		for _, sb := range m.Subscribers[topic] {
			if sb.id == sub.id {
				continue
			}
			newSubscribers = append(newSubscribers, sb)
		}
		m.Subscribers[topic] = newSubscribers
		m.Unlock()
	}()

	return sub, nil
}

func (m *memoryBroker) String() string {
	return "memory"
}

func (m *memoryEvent) Topic() string {
	return m.topic
}

func (m *memoryEvent) Message() *broker.Message {
	switch v := m.message.(type) {
	case *broker.Message:
		return v
	case []byte:
		msg := &broker.Message{}
		if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
			log.Errorf("[memory]: failed to unmarshal: %v\n", err)
			return nil
		}
		return msg
	}

	return nil
}

func (m *memoryEvent) Ack() error {
	return nil
}

func (m *memorySubscriber) Options() broker.SubscribeOptions {
	return m.opts
}

func (m *memorySubscriber) Topic() string {
	return m.topic
}

func (m *memorySubscriber) Unsubscribe() error {
	m.exit <- true
	return nil
}

func NewBroker(opts ...broker.Option) broker.Broker {
	options := broker.Options{
		Context: context.Background(),
	}

	rand.Seed(time.Now().UnixNano())
	for _, o := range opts {
		o(&options)
	}

	return &memoryBroker{
		opts:        options,
		Subscribers: make(map[string][]*memorySubscriber),
	}
}
<|endoftext|>


<commit_before>package bsdiff

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"runtime"
	"testing"

	"bytes"

	"index/suffixarray"

	"github.com/alecthomas/assert"
	"github.com/itchio/wharf/state"
)

func Test_QsufsortSeq(t *testing.T) {
	testQsufsort(t, 0)
}

func Test_QsufsortPar2(t *testing.T) {
	testQsufsort(t, 2)
}

func Test_QsufsortPar4(t *testing.T) {
	testQsufsort(t, 4)
}

func Test_QsufsortPar8(t *testing.T) {
	testQsufsort(t, 8)
}

func Test_Qsufsort64Seq(t *testing.T) {
	testQsufsort64(t, 0)
}

func Test_Qsufsort64Par2(t *testing.T) {
	testQsufsort64(t, 2)
}

func Test_Qsufsort64Par4(t *testing.T) {
	testQsufsort64(t, 4)
}

func Test_Qsufsort64Par8(t *testing.T) {
	testQsufsort64(t, 8)
}

var dictwords []byte
var dictcalls []byte

var result32 []int32
var result64 []int64

func testQsufsort(t *testing.T, concurrency int) {
	input := paper

	ctx := &DiffContext{
		SuffixSortConcurrency: concurrency,
	}
	consumer := &state.Consumer{}

	I := qsufsort(input, ctx, consumer)

	for i := range I {
		if i == 0 {
			continue
		}

		prev := input[I[i-1]:]
		next := input[I[i]:]
		assert.EqualValues(t, -1, bytes.Compare(prev, next))
	}
}

func testQsufsort64(t *testing.T, concurrency int) {
	input := paper

	ctx := &DiffContext{
		SuffixSortConcurrency: concurrency,
	}
	consumer := &state.Consumer{}

	I := qsufsort64(input, ctx, consumer)

	for i := range I {
		if i == 0 {
			continue
		}

		prev := input[I[i-1]:]
		next := input[I[i]:]
		assert.EqualValues(t, -1, bytes.Compare(prev, next))
	}
}

func benchQsuf(input []byte, concurrency int, b *testing.B) {
	ctx := &DiffContext{SuffixSortConcurrency: concurrency}
	consumer := &state.Consumer{}

	var r []int32
	for n := 0; n < b.N; n++ {
		r = qsufsort(input, ctx, consumer)
	}
	result32 = r
}

func benchQsuf64(input []byte, concurrency int, b *testing.B) {
	ctx := &DiffContext{SuffixSortConcurrency: concurrency}
	consumer := &state.Consumer{}

	var r []int64
	for n := 0; n < b.N; n++ {
		r = qsufsort64(input, ctx, consumer)
	}
	result64 = r
}

var sa *suffixarray.Index

func benchSuffixarray(input []byte, b *testing.B) {
	for n := 0; n < b.N; n++ {
		sa = suffixarray.New(input)
	}
}

var saz *SuffixArrayZ

func benchSuffixarrayx(input []byte, b *testing.B) {
	for n := 0; n < b.N; n++ {
		NewSuffixArrayZ(input)
	}
}

func Benchmark_Qsufsort(b *testing.B) {
	// note: 'paper' is not worth benchmarking because it's too short
	var datasets = []struct {
		name string
		data []byte
	}{
		{"dictwords", dictwords},
		{"dictcalls", dictcalls},
	}

	for _, dataset := range datasets {
		testName := fmt.Sprintf("suffixarrayx-%s", dataset.name)
		b.Run(testName, func(b *testing.B) {
			benchSuffixarrayx(dataset.data, b)
		})
	}

	for _, dataset := range datasets {
		testName := fmt.Sprintf("suffixarray-%s", dataset.name)
		b.Run(testName, func(b *testing.B) {
			benchSuffixarray(dataset.data, b)
		})
	}

	for _, dataset := range datasets {
		for _, concurrency := range []int{0, 2, 3, 4, 5, 6, 7, 8} {
			testName := fmt.Sprintf("qsufsort32-%s-j%d", dataset.name, concurrency)
			b.Run(testName, func(b *testing.B) {
				benchQsuf(dataset.data, concurrency, b)
			})
		}
	}

	for _, dataset := range datasets {
		for _, concurrency := range []int{0, 2, 3, 4, 5, 6, 7, 8} {
			testName := fmt.Sprintf("qsufsort64-%s-j%d", dataset.name, concurrency)
			b.Run(testName, func(b *testing.B) {
				benchQsuf64(dataset.data, concurrency, b)
			})
		}
	}
}

func init() {
	_, filename, _, _ := runtime.Caller(0)

	var err error
	dictwords, err = ioutil.ReadFile(filepath.Join(filepath.Dir(filename), "dictwords"))
	if err != nil {
		fmt.Printf("Could not load dictwords, benchmarks won't be functional (see README.md)\n")
	}

	dictcalls, err = ioutil.ReadFile(filepath.Join(filepath.Dir(filename), "dictcalls"))
	if err != nil {
		fmt.Printf("Could not load dictcalls, benchmarks won't be functional (see README.md)\n")
	}
}

var paper []byte = []byte(`
    Quicksort is a textbook divide-and-conquer algorithm.
    To sort an array, choose a partitioning element, permute
    the elements such that lesser elements are on one side and
    greater elements are on the other, and then recursively sort
    the two subarrays. But what happens to elements equal to
    the partitioning value? Hoare’s partitioning method is
    binary: it places lesser elements on the left and greater elements
    on the right, but equal elements may appear on
    either side.

    Algorithm designers have long recognized the desirability
    and difficulty of a ternary partitioning method.
    Sedgewick [22] observes on page 244: ‘‘Ideally, we would
    like to get all [equal keys] into position in the file, with all
    the keys with a smaller value to their left, and all the keys
    with a larger value to their right. Unfortunately, no
    efficient method for doing so has yet been devised....’’
    Dijkstra [6] popularized this as ‘‘The Problem of the Dutch
    National Flag’’: we are to order a sequence of red, white
    and blue pebbles to appear in their order on Holland’s
    ensign. This corresponds to Quicksort partitioning when
    lesser elements are colored red, equal elements are white,
    and greater elements are blue. Dijkstra’s ternary algorithm
    requires linear time (it looks at each element exactly once),
    but code to implement it has a significantly larger constant
    factor than Hoare’s binary partitioning code.

    Wegner [27] describes more efficient ternary partitioning
    schemes. Bentley and McIlroy [2] present a ternary
    partition based on this counterintuitive loop invariant:

    The main partitioning loop has two inner loops. The first
    inner loop moves up the index b: it scans over lesser elements,
    swaps equal elements to a, and halts on a greater
    element. The second inner loop moves down the index c
    correspondingly: it scans over greater elements, swaps
    equal elements to d, and halts on a lesser element. The
    main loop then swaps the elements pointed to by b and c,
    increments b and decrements c, and continues until b and
    c cross. (Wegner proposed the same invariant, but maintained
    it with more complex code.) Afterwards, the equal
    elements on the edges are swapped to the middle of the
    array, without any extraneous comparisons. This code partitions
    an n-element array using n − 1 comparisons

    Quicksort has been extensively analyzed by authors
    including Hoare [9], van Emden [26], Knuth [11], and
    Sedgewick [23]. Most detailed analyses involve the harmonic
    numbers Hn = Σ 1≤i≤n 1/ i.

    Theorem 1. [Hoare] A Quicksort that partitions
    around a single randomly selected element sorts n distinct
    items in 2nHn + O(n) ∼∼ 1. 386n lg n expected
    comparisons.

    A common variant of Quicksort partitions around the
    median of a random sample.

    Theorem 2. [van Emden] A Quicksort that partitions
    around the median of 2t + 1 randomly selected elements
    sorts n distinct items in 2nHn / (H2t + 2 − Ht + 1 )
    + O(n) expected comparisons.
    By increasing t, we can push the expected number of comparisons
    close to n lg n + O(n).
    `)
<commit_msg>Benchmark gosaca<commit_after>package bsdiff

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"runtime"
	"testing"

	"bytes"

	"index/suffixarray"

	"github.com/alecthomas/assert"
	"github.com/itchio/wharf/state"
	"github.com/jgallagher/gosaca"
)

func Test_QsufsortSeq(t *testing.T) {
	testQsufsort(t, 0)
}

func Test_QsufsortPar2(t *testing.T) {
	testQsufsort(t, 2)
}

func Test_QsufsortPar4(t *testing.T) {
	testQsufsort(t, 4)
}

func Test_QsufsortPar8(t *testing.T) {
	testQsufsort(t, 8)
}

func Test_Qsufsort64Seq(t *testing.T) {
	testQsufsort64(t, 0)
}

func Test_Qsufsort64Par2(t *testing.T) {
	testQsufsort64(t, 2)
}

func Test_Qsufsort64Par4(t *testing.T) {
	testQsufsort64(t, 4)
}

func Test_Qsufsort64Par8(t *testing.T) {
	testQsufsort64(t, 8)
}

var dictwords []byte
var dictcalls []byte

var result32 []int32
var result64 []int64

func testQsufsort(t *testing.T, concurrency int) {
	input := paper

	ctx := &DiffContext{
		SuffixSortConcurrency: concurrency,
	}
	consumer := &state.Consumer{}

	I := qsufsort(input, ctx, consumer)

	for i := range I {
		if i == 0 {
			continue
		}

		prev := input[I[i-1]:]
		next := input[I[i]:]
		assert.EqualValues(t, -1, bytes.Compare(prev, next))
	}
}

func testQsufsort64(t *testing.T, concurrency int) {
	input := paper

	ctx := &DiffContext{
		SuffixSortConcurrency: concurrency,
	}
	consumer := &state.Consumer{}

	I := qsufsort64(input, ctx, consumer)

	for i := range I {
		if i == 0 {
			continue
		}

		prev := input[I[i-1]:]
		next := input[I[i]:]
		assert.EqualValues(t, -1, bytes.Compare(prev, next))
	}
}

func benchQsuf(input []byte, concurrency int, b *testing.B) {
	ctx := &DiffContext{SuffixSortConcurrency: concurrency}
	consumer := &state.Consumer{}

	var r []int32
	for n := 0; n < b.N; n++ {
		r = qsufsort(input, ctx, consumer)
	}
	result32 = r
}

func benchQsuf64(input []byte, concurrency int, b *testing.B) {
	ctx := &DiffContext{SuffixSortConcurrency: concurrency}
	consumer := &state.Consumer{}

	var r []int64
	for n := 0; n < b.N; n++ {
		r = qsufsort64(input, ctx, consumer)
	}
	result64 = r
}

var sa *suffixarray.Index

func benchSuffixarray(input []byte, b *testing.B) {
	for n := 0; n < b.N; n++ {
		sa = suffixarray.New(input)
	}
}

var saz *SuffixArrayZ

func benchSuffixarrayz(input []byte, b *testing.B) {
	for n := 0; n < b.N; n++ {
		NewSuffixArrayZ(input)
	}
}

func benchGosaca(input []byte, b *testing.B) {
	for n := 0; n < b.N; n++ {
		ws := &gosaca.WorkSpace{}
		SA := make([]int, len(input))
		ws.ComputeSuffixArray(input, SA)
	}
}

func Benchmark_Qsufsort(b *testing.B) {
	// note: 'paper' is not worth benchmarking because it's too short
	var datasets = []struct {
		name string
		data []byte
	}{
		{"dictwords", dictwords},
		{"dictcalls", dictcalls},
	}

	for _, dataset := range datasets {
		testName := fmt.Sprintf("suffixarray-%s", dataset.name)
		b.Run(testName, func(b *testing.B) {
			benchSuffixarray(dataset.data, b)
		})
	}

	for _, dataset := range datasets {
		testName := fmt.Sprintf("suffixarrayz-%s", dataset.name)
		b.Run(testName, func(b *testing.B) {
			benchSuffixarrayz(dataset.data, b)
		})
	}

	for _, dataset := range datasets {
		testName := fmt.Sprintf("gosaca-%s", dataset.name)
		b.Run(testName, func(b *testing.B) {
			benchGosaca(dataset.data, b)
		})
	}

	for _, dataset := range datasets {
		for _, concurrency := range []int{0, 2, 3, 4, 5, 6, 7, 8} {
			testName := fmt.Sprintf("qsufsort32-%s-j%d", dataset.name, concurrency)
			b.Run(testName, func(b *testing.B) {
				benchQsuf(dataset.data, concurrency, b)
			})
		}
	}

	for _, dataset := range datasets {
		for _, concurrency := range []int{0, 2, 3, 4, 5, 6, 7, 8} {
			testName := fmt.Sprintf("qsufsort64-%s-j%d", dataset.name, concurrency)
			b.Run(testName, func(b *testing.B) {
				benchQsuf64(dataset.data, concurrency, b)
			})
		}
	}
}

func init() {
	_, filename, _, _ := runtime.Caller(0)

	var err error
	dictwords, err = ioutil.ReadFile(filepath.Join(filepath.Dir(filename), "dictwords"))
	if err != nil {
		fmt.Printf("Could not load dictwords, benchmarks won't be functional (see README.md)\n")
	}

	dictcalls, err = ioutil.ReadFile(filepath.Join(filepath.Dir(filename), "dictcalls"))
	if err != nil {
		fmt.Printf("Could not load dictcalls, benchmarks won't be functional (see README.md)\n")
	}
}

var paper []byte = []byte(`
    Quicksort is a textbook divide-and-conquer algorithm.
    To sort an array, choose a partitioning element, permute
    the elements such that lesser elements are on one side and
    greater elements are on the other, and then recursively sort
    the two subarrays. But what happens to elements equal to
    the partitioning value? Hoare’s partitioning method is
    binary: it places lesser elements on the left and greater elements
    on the right, but equal elements may appear on
    either side.

    Algorithm designers have long recognized the desirability
    and difficulty of a ternary partitioning method.
    Sedgewick [22] observes on page 244: ‘‘Ideally, we would
    like to get all [equal keys] into position in the file, with all
    the keys with a smaller value to their left, and all the keys
    with a larger value to their right. Unfortunately, no
    efficient method for doing so has yet been devised....’’
    Dijkstra [6] popularized this as ‘‘The Problem of the Dutch
    National Flag’’: we are to order a sequence of red, white
    and blue pebbles to appear in their order on Holland’s
    ensign. This corresponds to Quicksort partitioning when
    lesser elements are colored red, equal elements are white,
    and greater elements are blue. Dijkstra’s ternary algorithm
    requires linear time (it looks at each element exactly once),
    but code to implement it has a significantly larger constant
    factor than Hoare’s binary partitioning code.

    Wegner [27] describes more efficient ternary partitioning
    schemes. Bentley and McIlroy [2] present a ternary
    partition based on this counterintuitive loop invariant:

    The main partitioning loop has two inner loops. The first
    inner loop moves up the index b: it scans over lesser elements,
    swaps equal elements to a, and halts on a greater
    element. The second inner loop moves down the index c
    correspondingly: it scans over greater elements, swaps
    equal elements to d, and halts on a lesser element. The
    main loop then swaps the elements pointed to by b and c,
    increments b and decrements c, and continues until b and
    c cross. (Wegner proposed the same invariant, but maintained
    it with more complex code.) Afterwards, the equal
    elements on the edges are swapped to the middle of the
    array, without any extraneous comparisons. This code partitions
    an n-element array using n − 1 comparisons

    Quicksort has been extensively analyzed by authors
    including Hoare [9], van Emden [26], Knuth [11], and
    Sedgewick [23]. Most detailed analyses involve the harmonic
    numbers Hn = Σ 1≤i≤n 1/ i.

    Theorem 1. [Hoare] A Quicksort that partitions
    around a single randomly selected element sorts n distinct
    items in 2nHn + O(n) ∼∼ 1. 386n lg n expected
    comparisons.

    A common variant of Quicksort partitions around the
    median of a random sample.

    Theorem 2. [van Emden] A Quicksort that partitions
    around the median of 2t + 1 randomly selected elements
    sorts n distinct items in 2nHn / (H2t + 2 − Ht + 1 )
    + O(n) expected comparisons.
    By increasing t, we can push the expected number of comparisons
    close to n lg n + O(n).
    `)
<|endoftext|>