<commit_before>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package usb

// #include <libusb.h>
import "C"
import "strconv"

// Class represents a USB-IF class or subclass code.
type Class uint8

// Standard classes defined by USB spec.
const (
	ClassPerInterface Class = C.LIBUSB_CLASS_PER_INTERFACE
	ClassAudio        Class = C.LIBUSB_CLASS_AUDIO
	ClassComm         Class = C.LIBUSB_CLASS_COMM
	ClassHID          Class = C.LIBUSB_CLASS_HID
	ClassPrinter      Class = C.LIBUSB_CLASS_PRINTER
	ClassPTP          Class = C.LIBUSB_CLASS_PTP
	ClassMassStorage  Class = C.LIBUSB_CLASS_MASS_STORAGE
	ClassHub          Class = C.LIBUSB_CLASS_HUB
	ClassData         Class = C.LIBUSB_CLASS_DATA
	ClassWireless     Class = C.LIBUSB_CLASS_WIRELESS
	ClassApplication  Class = C.LIBUSB_CLASS_APPLICATION
	ClassVendorSpec   Class = C.LIBUSB_CLASS_VENDOR_SPEC
)

var classDescription = map[Class]string{
	ClassPerInterface: "per-interface",
	ClassAudio:        "audio",
	ClassComm:         "communications",
	ClassHID:          "human interface device",
	ClassPrinter:      "printer dclass",
	ClassPTP:          "picture transfer protocol",
	ClassMassStorage:  "mass storage",
	ClassHub:          "hub",
	ClassData:         "data",
	ClassWireless:     "wireless",
	ClassApplication:  "application",
	ClassVendorSpec:   "vendor-specific",
}

func (c Class) String() string {
	if d, ok := classDescription[c]; ok {
		return d
	}
	return strconv.Itoa(int(c))
}

// Protocol is the interface class protocol, qualified by the values
// of interface class and subclass.
type Protocol uint8

func (p Protocol) String() string {
	return strconv.Itoa(int(p))
}

// DescriptorType identifies the type of a USB descriptor.
type DescriptorType uint8

// Descriptor types defined by the USB spec.
const (
	DescriptorTypeDevice    DescriptorType = C.LIBUSB_DT_DEVICE
	DescriptorTypeConfig    DescriptorType = C.LIBUSB_DT_CONFIG
	DescriptorTypeString    DescriptorType = C.LIBUSB_DT_STRING
	DescriptorTypeInterface DescriptorType = C.LIBUSB_DT_INTERFACE
	DescriptorTypeEndpoint  DescriptorType = C.LIBUSB_DT_ENDPOINT
	DescriptorTypeHID       DescriptorType = C.LIBUSB_DT_HID
	DescriptorTypeReport    DescriptorType = C.LIBUSB_DT_REPORT
	DescriptorTypePhysical  DescriptorType = C.LIBUSB_DT_PHYSICAL
	DescriptorTypeHub       DescriptorType = C.LIBUSB_DT_HUB
)

var descriptorTypeDescription = map[DescriptorType]string{
	DescriptorTypeDevice:    "device",
	DescriptorTypeConfig:    "configuration",
	DescriptorTypeString:    "string",
	DescriptorTypeInterface: "interface",
	DescriptorTypeEndpoint:  "endpoint",
	DescriptorTypeHID:       "HID",
	DescriptorTypeReport:    "HID report",
	DescriptorTypePhysical:  "physical",
	DescriptorTypeHub:       "hub",
}

func (dt DescriptorType) String() string {
	return descriptorTypeDescription[dt]
}

// EndpointDirection defines the direction of data flow - IN (device to host)
// or OUT (host to device).
type EndpointDirection bool

const (
	endpointNumMask       = 0x0f
	endpointDirectionMask = 0x80
	// EndpointDirectionIn marks data flowing from device to host.
	EndpointDirectionIn EndpointDirection = true
	// EndpointDirectionOut marks data flowing from host to device.
	EndpointDirectionOut EndpointDirection = false
)

var endpointDirectionDescription = map[EndpointDirection]string{
	EndpointDirectionIn:  "IN",
	EndpointDirectionOut: "OUT",
}

func (ed EndpointDirection) String() string {
	return endpointDirectionDescription[ed]
}

// TransferType defines the endpoint transfer type.
type TransferType uint8

// Transfer types defined by the USB spec.
const (
	TransferTypeControl     TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL
	TransferTypeIsochronous TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
	TransferTypeBulk        TransferType = C.LIBUSB_TRANSFER_TYPE_BULK
	TransferTypeInterrupt   TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT
	transferTypeMask                     = 0x03
)

var transferTypeDescription = map[TransferType]string{
	TransferTypeControl:     "control",
	TransferTypeIsochronous: "isochronous",
	TransferTypeBulk:        "bulk",
	TransferTypeInterrupt:   "interrupt",
}

// String returns a human-readable name of the endpoint transfer type.
func (tt TransferType) String() string {
	return transferTypeDescription[tt]
}

// IsoSyncType defines the isochronous transfer synchronization type.
type IsoSyncType uint8

// Synchronization types defined by the USB spec.
const (
	IsoSyncTypeNone     IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2
	IsoSyncTypeAsync    IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2
	IsoSyncTypeAdaptive IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2
	IsoSyncTypeSync     IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2
	isoSyncTypeMask                 = 0x0C
)

var isoSyncTypeDescription = map[IsoSyncType]string{
	IsoSyncTypeNone:     "unsynchronized",
	IsoSyncTypeAsync:    "asynchronous",
	IsoSyncTypeAdaptive: "adaptive",
	IsoSyncTypeSync:     "synchronous",
}

// String returns a human-readable description of the synchronization type.
func (ist IsoSyncType) String() string {
	return isoSyncTypeDescription[ist]
}

// UsageType defines the transfer usage type for isochronous and interrupt
// transfers.
type UsageType uint8

// Usage types for iso and interrupt transfers, defined by the USB spec.
const (
	// Note: USB3.0 defines usage type for both isochronous and interrupt
	// endpoints, with the same constants representing different usage types.
	// UsageType constants do not correspond to bmAttribute values.
	usageTypeMask                = 0x30
	UsageTypeUndefined UsageType = iota
	IsoUsageTypeData
	IsoUsageTypeFeedback
	IsoUsageTypeImplicit
	InterruptUsageTypePeriodic
	InterruptUsageTypeNotification
)

var usageTypeDescription = map[UsageType]string{
	UsageTypeUndefined:             "undefined usage",
	IsoUsageTypeData:               "data",
	IsoUsageTypeFeedback:           "feedback",
	IsoUsageTypeImplicit:           "implicit data",
	InterruptUsageTypePeriodic:     "periodic",
	InterruptUsageTypeNotification: "notification",
}

func (ut UsageType) String() string {
	return usageTypeDescription[ut]
}

// RequestType identifies a control transfer request type.
type RequestType uint8

// Control request types defined in the USB spec.
const (
	RequestTypeStandard = C.LIBUSB_REQUEST_TYPE_STANDARD
	RequestTypeClass    = C.LIBUSB_REQUEST_TYPE_CLASS
	RequestTypeVendor   = C.LIBUSB_REQUEST_TYPE_VENDOR
	RequestTypeReserved = C.LIBUSB_REQUEST_TYPE_RESERVED
)

var requestTypeDescription = map[RequestType]string{
	RequestTypeStandard: "standard",
	RequestTypeClass:    "class",
	RequestTypeVendor:   "vendor",
	RequestTypeReserved: "reserved",
}

// String returns a human-readable name of the control transfer request type.
func (rt RequestType) String() string {
	return requestTypeDescription[rt]
}

// DeviceSpeed identifies the speed of the device.
type DeviceSpeed int

// Device speeds as defined in the USB spec.
const (
	DeviceSpeedUnknown DeviceSpeed = C.LIBUSB_SPEED_UNKNOWN
	DeviceSpeedLow     DeviceSpeed = C.LIBUSB_SPEED_LOW
	DeviceSpeedFull    DeviceSpeed = C.LIBUSB_SPEED_FULL
	DeviceSpeedHigh    DeviceSpeed = C.LIBUSB_SPEED_HIGH
	DeviceSpeedSuper   DeviceSpeed = C.LIBUSB_SPEED_SUPER
)

var deviceSpeedDescription = map[DeviceSpeed]string{
	DeviceSpeedUnknown: "unknown",
	DeviceSpeedLow:     "low",
	DeviceSpeedFull:    "full",
	DeviceSpeedHigh:    "high",
	DeviceSpeedSuper:   "super",
}

// String returns a human-readable name of the device speed.
func (s DeviceSpeed) String() string {
	return deviceSpeedDescription[s]
}

const (
	selfPoweredMask  = 0x40
	remoteWakeupMask = 0x20
)

// Milliamperes is a unit of electric current consumption.
type Milliamperes uint
<commit_msg>iota used on the second line of const block starts from 1, not from 0...<commit_after>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package usb

// #include <libusb.h>
import "C"
import "strconv"

// Class represents a USB-IF class or subclass code.
type Class uint8

// Standard classes defined by USB spec.
const (
	ClassPerInterface Class = C.LIBUSB_CLASS_PER_INTERFACE
	ClassAudio        Class = C.LIBUSB_CLASS_AUDIO
	ClassComm         Class = C.LIBUSB_CLASS_COMM
	ClassHID          Class = C.LIBUSB_CLASS_HID
	ClassPrinter      Class = C.LIBUSB_CLASS_PRINTER
	ClassPTP          Class = C.LIBUSB_CLASS_PTP
	ClassMassStorage  Class = C.LIBUSB_CLASS_MASS_STORAGE
	ClassHub          Class = C.LIBUSB_CLASS_HUB
	ClassData         Class = C.LIBUSB_CLASS_DATA
	ClassWireless     Class = C.LIBUSB_CLASS_WIRELESS
	ClassApplication  Class = C.LIBUSB_CLASS_APPLICATION
	ClassVendorSpec   Class = C.LIBUSB_CLASS_VENDOR_SPEC
)

var classDescription = map[Class]string{
	ClassPerInterface: "per-interface",
	ClassAudio:        "audio",
	ClassComm:         "communications",
	ClassHID:          "human interface device",
	ClassPrinter:      "printer dclass",
	ClassPTP:          "picture transfer protocol",
	ClassMassStorage:  "mass storage",
	ClassHub:          "hub",
	ClassData:         "data",
	ClassWireless:     "wireless",
	ClassApplication:  "application",
	ClassVendorSpec:   "vendor-specific",
}

func (c Class) String() string {
	if d, ok := classDescription[c]; ok {
		return d
	}
	return strconv.Itoa(int(c))
}

// Protocol is the interface class protocol, qualified by the values
// of interface class and subclass.
type Protocol uint8

func (p Protocol) String() string {
	return strconv.Itoa(int(p))
}

// DescriptorType identifies the type of a USB descriptor.
type DescriptorType uint8

// Descriptor types defined by the USB spec.
const (
	DescriptorTypeDevice    DescriptorType = C.LIBUSB_DT_DEVICE
	DescriptorTypeConfig    DescriptorType = C.LIBUSB_DT_CONFIG
	DescriptorTypeString    DescriptorType = C.LIBUSB_DT_STRING
	DescriptorTypeInterface DescriptorType = C.LIBUSB_DT_INTERFACE
	DescriptorTypeEndpoint  DescriptorType = C.LIBUSB_DT_ENDPOINT
	DescriptorTypeHID       DescriptorType = C.LIBUSB_DT_HID
	DescriptorTypeReport    DescriptorType = C.LIBUSB_DT_REPORT
	DescriptorTypePhysical  DescriptorType = C.LIBUSB_DT_PHYSICAL
	DescriptorTypeHub       DescriptorType = C.LIBUSB_DT_HUB
)

var descriptorTypeDescription = map[DescriptorType]string{
	DescriptorTypeDevice:    "device",
	DescriptorTypeConfig:    "configuration",
	DescriptorTypeString:    "string",
	DescriptorTypeInterface: "interface",
	DescriptorTypeEndpoint:  "endpoint",
	DescriptorTypeHID:       "HID",
	DescriptorTypeReport:    "HID report",
	DescriptorTypePhysical:  "physical",
	DescriptorTypeHub:       "hub",
}

func (dt DescriptorType) String() string {
	return descriptorTypeDescription[dt]
}

// EndpointDirection defines the direction of data flow - IN (device to host)
// or OUT (host to device).
type EndpointDirection bool

const (
	endpointNumMask       = 0x0f
	endpointDirectionMask = 0x80
	// EndpointDirectionIn marks data flowing from device to host.
	EndpointDirectionIn EndpointDirection = true
	// EndpointDirectionOut marks data flowing from host to device.
	EndpointDirectionOut EndpointDirection = false
)

var endpointDirectionDescription = map[EndpointDirection]string{
	EndpointDirectionIn:  "IN",
	EndpointDirectionOut: "OUT",
}

func (ed EndpointDirection) String() string {
	return endpointDirectionDescription[ed]
}

// TransferType defines the endpoint transfer type.
type TransferType uint8

// Transfer types defined by the USB spec.
const (
	TransferTypeControl     TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL
	TransferTypeIsochronous TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
	TransferTypeBulk        TransferType = C.LIBUSB_TRANSFER_TYPE_BULK
	TransferTypeInterrupt   TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT
	transferTypeMask                     = 0x03
)

var transferTypeDescription = map[TransferType]string{
	TransferTypeControl:     "control",
	TransferTypeIsochronous: "isochronous",
	TransferTypeBulk:        "bulk",
	TransferTypeInterrupt:   "interrupt",
}

// String returns a human-readable name of the endpoint transfer type.
func (tt TransferType) String() string {
	return transferTypeDescription[tt]
}

// IsoSyncType defines the isochronous transfer synchronization type.
type IsoSyncType uint8

// Synchronization types defined by the USB spec.
const (
	IsoSyncTypeNone     IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2
	IsoSyncTypeAsync    IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2
	IsoSyncTypeAdaptive IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2
	IsoSyncTypeSync     IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2
	isoSyncTypeMask                 = 0x0C
)

var isoSyncTypeDescription = map[IsoSyncType]string{
	IsoSyncTypeNone:     "unsynchronized",
	IsoSyncTypeAsync:    "asynchronous",
	IsoSyncTypeAdaptive: "adaptive",
	IsoSyncTypeSync:     "synchronous",
}

// String returns a human-readable description of the synchronization type.
func (ist IsoSyncType) String() string {
	return isoSyncTypeDescription[ist]
}

// UsageType defines the transfer usage type for isochronous and interrupt
// transfers.
type UsageType uint8

// Usage types for iso and interrupt transfers, defined by the USB spec.
const (
	// Note: USB3.0 defines usage type for both isochronous and interrupt
	// endpoints, with the same constants representing different usage types.
	// UsageType constants do not correspond to bmAttribute values.
	UsageTypeUndefined UsageType = iota
	IsoUsageTypeData
	IsoUsageTypeFeedback
	IsoUsageTypeImplicit
	InterruptUsageTypePeriodic
	InterruptUsageTypeNotification
	usageTypeMask = 0x30
)

var usageTypeDescription = map[UsageType]string{
	UsageTypeUndefined:             "undefined usage",
	IsoUsageTypeData:               "data",
	IsoUsageTypeFeedback:           "feedback",
	IsoUsageTypeImplicit:           "implicit data",
	InterruptUsageTypePeriodic:     "periodic",
	InterruptUsageTypeNotification: "notification",
}

func (ut UsageType) String() string {
	return usageTypeDescription[ut]
}

// RequestType identifies a control transfer request type.
type RequestType uint8

// Control request types defined in the USB spec.
const (
	RequestTypeStandard = C.LIBUSB_REQUEST_TYPE_STANDARD
	RequestTypeClass    = C.LIBUSB_REQUEST_TYPE_CLASS
	RequestTypeVendor   = C.LIBUSB_REQUEST_TYPE_VENDOR
	RequestTypeReserved = C.LIBUSB_REQUEST_TYPE_RESERVED
)

var requestTypeDescription = map[RequestType]string{
	RequestTypeStandard: "standard",
	RequestTypeClass:    "class",
	RequestTypeVendor:   "vendor",
	RequestTypeReserved: "reserved",
}

// String returns a human-readable name of the control transfer request type.
func (rt RequestType) String() string {
	return requestTypeDescription[rt]
}

// DeviceSpeed identifies the speed of the device.
type DeviceSpeed int

// Device speeds as defined in the USB spec.
const (
	DeviceSpeedUnknown DeviceSpeed = C.LIBUSB_SPEED_UNKNOWN
	DeviceSpeedLow     DeviceSpeed = C.LIBUSB_SPEED_LOW
	DeviceSpeedFull    DeviceSpeed = C.LIBUSB_SPEED_FULL
	DeviceSpeedHigh    DeviceSpeed = C.LIBUSB_SPEED_HIGH
	DeviceSpeedSuper   DeviceSpeed = C.LIBUSB_SPEED_SUPER
)

var deviceSpeedDescription = map[DeviceSpeed]string{
	DeviceSpeedUnknown: "unknown",
	DeviceSpeedLow:     "low",
	DeviceSpeedFull:    "full",
	DeviceSpeedHigh:    "high",
	DeviceSpeedSuper:   "super",
}

// String returns a human-readable name of the device speed.
func (s DeviceSpeed) String() string {
	return deviceSpeedDescription[s]
}

const (
	selfPoweredMask  = 0x40
	remoteWakeupMask = 0x20
)

// Milliamperes is a unit of electric current consumption.
type Milliamperes uint
<|endoftext|>


<commit_before>package gexec_test

import (
	"io"
	"io/ioutil"
	"os/exec"
	"syscall"
	"time"

	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Session", func() {
	var command *exec.Cmd
	var session *Session

	var outWriter, errWriter io.Writer

	BeforeEach(func() {
		outWriter = nil
		errWriter = nil
	})

	JustBeforeEach(func() {
		command = exec.Command(fireflyPath)
		var err error
		session, err = Start(command, outWriter, errWriter)
		Ω(err).ShouldNot(HaveOccurred())
	})

	Context("running a command", func() {
		It("should start the process", func() {
			Ω(command.Process).ShouldNot(BeNil())
		})

		It("should wrap the process's stdout and stderr with gbytes buffers", func(done Done) {
			Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
			Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!"))
			defer session.Out.CancelDetects()

			select {
			case <-session.Out.Detect("Can we maybe vote on the whole murdering people issue"):
				Eventually(session).Should(Exit(0))
			case <-session.Out.Detect("I swear by my pretty floral bonnet, I will end you."):
				Eventually(session).Should(Exit(1))
			case <-session.Out.Detect("My work's illegal, but at least it's honest."):
				Eventually(session).Should(Exit(2))
			}

			close(done)
		})

		It("should satisfy the gbytes.BufferProvider interface, passing Stdout", func() {
			Eventually(session).Should(Say("We've done the impossible, and that makes us mighty"))
			Eventually(session).Should(Exit())
		})
	})

	Describe("providing the exit code", func() {
		It("should provide the app's exit code", func() {
			Ω(session.ExitCode()).Should(Equal(-1))

			Eventually(session).Should(Exit())
			Ω(session.ExitCode()).Should(BeNumerically(">=", 0))
			Ω(session.ExitCode()).Should(BeNumerically("<", 3))
		})
	})

	Describe("wait", func() {
		It("should wait till the command exits", func() {
			Ω(session.ExitCode()).Should(Equal(-1))
			Ω(session.Wait().ExitCode()).Should(BeNumerically(">=", 0))
			Ω(session.Wait().ExitCode()).Should(BeNumerically("<", 3))
		})
	})

	Describe("exited", func() {
		It("should close when the command exits", func() {
			Eventually(session.Exited).Should(BeClosed())
			Ω(session.ExitCode()).ShouldNot(Equal(-1))
		})
	})

	Describe("kill", func() {
		It("should kill the command and don't wait for it to exit", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Kill()
			Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
			Eventually(session).Should(Exit(128 + 9))
		})
	})

	Describe("interrupt", func() {
		It("should interrupt the command", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Interrupt()
			Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
			Eventually(session).Should(Exit(128 + 2))
		})
	})

	Describe("terminate", func() {
		It("should terminate the command", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Terminate()
			Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
			Eventually(session).Should(Exit(128 + 15))
		})
	})

	Describe("signal", func() {
		It("should send the signal to the command", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Signal(syscall.SIGABRT)
			Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
			Eventually(session).Should(Exit(128 + 6))
		})

		It("should ignore sending a signal if the command did not start", func() {
			session, err := Start(exec.Command("notexisting"), GinkgoWriter, GinkgoWriter)
			Expect(err).To(HaveOccurred())

			Expect(func() { session.Signal(syscall.SIGUSR1) }).NotTo(Panic())
		})
	})

	Context("tracking sessions", func() {
		BeforeEach(func() {
			KillAndWait()
		})

		Describe("kill", func() {
			It("should kill all the started sessions", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Kill()

				Eventually(session1).Should(Exit(128 + 9))
				Eventually(session2).Should(Exit(128 + 9))
				Eventually(session3).Should(Exit(128 + 9))
			})

			It("should not wait for exit", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Kill()
				Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")

				Eventually(session1).Should(Exit(128 + 9))
			})

			It("should not track unstarted sessions", func() {
				_, err := Start(exec.Command("does not exist", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).Should(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Kill()

				Eventually(session2).Should(Exit(128 + 9))
				Eventually(session3).Should(Exit(128 + 9))
			})

		})

		Describe("killAndWait", func() {
			It("should kill all the started sessions and wait for them to finish", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				KillAndWait()
				Ω(session1).Should(Exit(128+9), "Should have exited")
				Ω(session2).Should(Exit(128+9), "Should have exited")
				Ω(session3).Should(Exit(128+9), "Should have exited")
			})
		})

		Describe("terminate", func() {
			It("should terminate all the started sessions", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Terminate()

				Eventually(session1).Should(Exit(128 + 15))
				Eventually(session2).Should(Exit(128 + 15))
				Eventually(session3).Should(Exit(128 + 15))
			})

			It("should not wait for exit", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Terminate()

				Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
			})
		})

		Describe("terminateAndWait", func() {
			It("should terminate all the started sessions, and wait for them to exit", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				TerminateAndWait()

				Ω(session1).Should(Exit(128+15), "Should have exited")
				Ω(session2).Should(Exit(128+15), "Should have exited")
				Ω(session3).Should(Exit(128+15), "Should have exited")
			})
		})

		Describe("signal", func() {
			It("should signal all the started sessions", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Signal(syscall.SIGABRT)

				Eventually(session1).Should(Exit(128 + 6))
				Eventually(session2).Should(Exit(128 + 6))
				Eventually(session3).Should(Exit(128 + 6))
			})

			It("should not wait", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Signal(syscall.SIGABRT)

				Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
			})
		})

		Describe("interrupt", func() {
			It("should interrupt all the started sessions, and not wait", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Interrupt()

				Eventually(session1).Should(Exit(128 + 2))
				Eventually(session2).Should(Exit(128 + 2))
				Eventually(session3).Should(Exit(128 + 2))
			})

			It("should not wait", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Interrupt()

				Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
			})
		})
	})

	Context("when the command exits", func() {
		It("should close the buffers", func() {
			Eventually(session).Should(Exit())

			Ω(session.Out.Closed()).Should(BeTrue())
			Ω(session.Err.Closed()).Should(BeTrue())

			Ω(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
		})

		var So = It

		So("this means that eventually should short circuit", func() {
			t := time.Now()
			failures := InterceptGomegaFailures(func() {
				Eventually(session).Should(Say("blah blah blah blah blah"))
			})
			Ω(time.Since(t)).Should(BeNumerically("<=", 500*time.Millisecond))
			Ω(failures).Should(HaveLen(1))
		})
	})

	Context("when wrapping out and err", func() {
		var (
			outWriterBuffer, errWriterBuffer *Buffer
		)

		BeforeEach(func() {
			outWriterBuffer = NewBuffer()
			outWriter = outWriterBuffer
			errWriterBuffer = NewBuffer()
			errWriter = errWriterBuffer
		})

		It("should route to both the provided writers and the gbytes buffers", func() {
			Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
			Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!"))

			Ω(outWriterBuffer.Contents()).Should(ContainSubstring("We've done the impossible, and that makes us mighty"))
			Ω(errWriterBuffer.Contents()).Should(ContainSubstring("Ah, curse your sudden but inevitable betrayal!"))

			Eventually(session).Should(Exit())

			Ω(outWriterBuffer.Contents()).Should(Equal(session.Out.Contents()))
			Ω(errWriterBuffer.Contents()).Should(Equal(session.Err.Contents()))
		})

		Context("when discarding the output of the command", func() {
			BeforeEach(func() {
				outWriter = ioutil.Discard
				errWriter = ioutil.Discard
			})

			It("executes succesfuly", func() {
				Eventually(session).Should(Exit())
			})
		})
	})

	Describe("when the command fails to start", func() {
		It("should return an error", func() {
			_, err := Start(exec.Command("agklsjdfas"), nil, nil)
			Ω(err).Should(HaveOccurred())
		})
	})
})
<commit_msg>Remove tests that tried to verify signals as async<commit_after>package gexec_test

import (
	"io"
	"io/ioutil"
	"os/exec"
	"syscall"
	"time"

	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Session", func() {
	var command *exec.Cmd
	var session *Session

	var outWriter, errWriter io.Writer

	BeforeEach(func() {
		outWriter = nil
		errWriter = nil
	})

	JustBeforeEach(func() {
		command = exec.Command(fireflyPath)
		var err error
		session, err = Start(command, outWriter, errWriter)
		Ω(err).ShouldNot(HaveOccurred())
	})

	Context("running a command", func() {
		It("should start the process", func() {
			Ω(command.Process).ShouldNot(BeNil())
		})

		It("should wrap the process's stdout and stderr with gbytes buffers", func(done Done) {
			Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
			Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!"))
			defer session.Out.CancelDetects()

			select {
			case <-session.Out.Detect("Can we maybe vote on the whole murdering people issue"):
				Eventually(session).Should(Exit(0))
			case <-session.Out.Detect("I swear by my pretty floral bonnet, I will end you."):
				Eventually(session).Should(Exit(1))
			case <-session.Out.Detect("My work's illegal, but at least it's honest."):
				Eventually(session).Should(Exit(2))
			}

			close(done)
		})

		It("should satisfy the gbytes.BufferProvider interface, passing Stdout", func() {
			Eventually(session).Should(Say("We've done the impossible, and that makes us mighty"))
			Eventually(session).Should(Exit())
		})
	})

	Describe("providing the exit code", func() {
		It("should provide the app's exit code", func() {
			Ω(session.ExitCode()).Should(Equal(-1))

			Eventually(session).Should(Exit())
			Ω(session.ExitCode()).Should(BeNumerically(">=", 0))
			Ω(session.ExitCode()).Should(BeNumerically("<", 3))
		})
	})

	Describe("wait", func() {
		It("should wait till the command exits", func() {
			Ω(session.ExitCode()).Should(Equal(-1))
			Ω(session.Wait().ExitCode()).Should(BeNumerically(">=", 0))
			Ω(session.Wait().ExitCode()).Should(BeNumerically("<", 3))
		})
	})

	Describe("exited", func() {
		It("should close when the command exits", func() {
			Eventually(session.Exited).Should(BeClosed())
			Ω(session.ExitCode()).ShouldNot(Equal(-1))
		})
	})

	Describe("kill", func() {
		It("should kill the command", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Kill()
			Eventually(session).Should(Exit(128 + 9))
		})
	})

	Describe("interrupt", func() {
		It("should interrupt the command", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Interrupt()
			Eventually(session).Should(Exit(128 + 2))
		})
	})

	Describe("terminate", func() {
		It("should terminate the command", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Terminate()
			Eventually(session).Should(Exit(128 + 15))
		})
	})

	Describe("signal", func() {
		It("should send the signal to the command", func() {
			session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
			Ω(err).ShouldNot(HaveOccurred())

			session.Signal(syscall.SIGABRT)
			Eventually(session).Should(Exit(128 + 6))
		})

		It("should ignore sending a signal if the command did not start", func() {
			session, err := Start(exec.Command("notexisting"), GinkgoWriter, GinkgoWriter)
			Expect(err).To(HaveOccurred())

			Expect(func() { session.Signal(syscall.SIGUSR1) }).NotTo(Panic())
		})
	})

	Context("tracking sessions", func() {
		BeforeEach(func() {
			KillAndWait()
		})

		Describe("kill", func() {
			It("should kill all the started sessions", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Kill()

				Eventually(session1).Should(Exit(128 + 9))
				Eventually(session2).Should(Exit(128 + 9))
				Eventually(session3).Should(Exit(128 + 9))
			})

			It("should not track unstarted sessions", func() {
				_, err := Start(exec.Command("does not exist", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).Should(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Kill()

				Eventually(session2).Should(Exit(128 + 9))
				Eventually(session3).Should(Exit(128 + 9))
			})

		})

		Describe("killAndWait", func() {
			It("should kill all the started sessions and wait for them to finish", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				KillAndWait()
				Ω(session1).Should(Exit(128+9), "Should have exited")
				Ω(session2).Should(Exit(128+9), "Should have exited")
				Ω(session3).Should(Exit(128+9), "Should have exited")
			})
		})

		Describe("terminate", func() {
			It("should terminate all the started sessions", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Terminate()

				Eventually(session1).Should(Exit(128 + 15))
				Eventually(session2).Should(Exit(128 + 15))
				Eventually(session3).Should(Exit(128 + 15))
			})
		})

		Describe("terminateAndWait", func() {
			It("should terminate all the started sessions, and wait for them to exit", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				TerminateAndWait()

				Ω(session1).Should(Exit(128+15), "Should have exited")
				Ω(session2).Should(Exit(128+15), "Should have exited")
				Ω(session3).Should(Exit(128+15), "Should have exited")
			})
		})

		Describe("signal", func() {
			It("should signal all the started sessions", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Signal(syscall.SIGABRT)

				Eventually(session1).Should(Exit(128 + 6))
				Eventually(session2).Should(Exit(128 + 6))
				Eventually(session3).Should(Exit(128 + 6))
			})
		})

		Describe("interrupt", func() {
			It("should interrupt all the started sessions, and not wait", func() {
				session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
				Ω(err).ShouldNot(HaveOccurred())

				Interrupt()

				Eventually(session1).Should(Exit(128 + 2))
				Eventually(session2).Should(Exit(128 + 2))
				Eventually(session3).Should(Exit(128 + 2))
			})
		})
	})

	Context("when the command exits", func() {
		It("should close the buffers", func() {
			Eventually(session).Should(Exit())

			Ω(session.Out.Closed()).Should(BeTrue())
			Ω(session.Err.Closed()).Should(BeTrue())

			Ω(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
		})

		var So = It

		So("this means that eventually should short circuit", func() {
			t := time.Now()
			failures := InterceptGomegaFailures(func() {
				Eventually(session).Should(Say("blah blah blah blah blah"))
			})
			Ω(time.Since(t)).Should(BeNumerically("<=", 500*time.Millisecond))
			Ω(failures).Should(HaveLen(1))
		})
	})

	Context("when wrapping out and err", func() {
		var (
			outWriterBuffer, errWriterBuffer *Buffer
		)

		BeforeEach(func() {
			outWriterBuffer = NewBuffer()
			outWriter = outWriterBuffer
			errWriterBuffer = NewBuffer()
			errWriter = errWriterBuffer
		})

		It("should route to both the provided writers and the gbytes buffers", func() {
			Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
			Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!"))

			Ω(outWriterBuffer.Contents()).Should(ContainSubstring("We've done the impossible, and that makes us mighty"))
			Ω(errWriterBuffer.Contents()).Should(ContainSubstring("Ah, curse your sudden but inevitable betrayal!"))

			Eventually(session).Should(Exit())

			Ω(outWriterBuffer.Contents()).Should(Equal(session.Out.Contents()))
			Ω(errWriterBuffer.Contents()).Should(Equal(session.Err.Contents()))
		})

		Context("when discarding the output of the command", func() {
			BeforeEach(func() {
				outWriter = ioutil.Discard
				errWriter = ioutil.Discard
			})

			It("executes succesfuly", func() {
				Eventually(session).Should(Exit())
			})
		})
	})

	Describe("when the command fails to start", func() {
		It("should return an error", func() {
			_, err := Start(exec.Command("agklsjdfas"), nil, nil)
			Ω(err).Should(HaveOccurred())
		})
	})
})
<|endoftext|>


<commit_before>/*
    User : https://api.github.com/users/Omie
        returns a dict
        has repos_url : https://api.github.com/users/Omie/repos
    Repos : https://api.github.com/users/Omie/repos
        returns a list of dict
        has collaborators_url : https://api.github.com/repos/Omie/configfiles/collaborators
    Collaborators : https://api.github.com/repos/Omie/configfiles/collaborators
        returns a list of dict
        has repos_url for each user
*/

package main

import (
        "os"
        "fmt"
        "encoding/json"
        "io/ioutil"
        "net/http"
        "strings"
        "log"
        "errors"
        "github.com/omie/ghlib"
)

var visited = make(map[string]string)

var requestsLeft int = 60

var username, password string

const maxDepth int = 3

//because Math.Min is for float64
func min(a, b int) int {
    if a <= b {
        return a
    }
    return b
}

func getData(url string) ([]byte, error) {
        log.Println("--- reached getData for ", url)

        requestsLeft--
        if requestsLeft < 0 {
            log.Println("--- LIMIT REACHED ")
            return nil, errors.New("limit reached")
        }

        client := &http.Client{}

        /* Authenticate */
        req, err := http.NewRequest("GET", url, nil)
        req.SetBasicAuth(username, password)
        resp, err := client.Do(req)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            return nil, err
        }

        return body, nil
}

func getApiLimit() (int, error) {
    jsonData, err := getData("https://api.github.com/rate_limit")
    if err != nil {
        return 0, err
    }

    var limitData ghlib.GhLimit
    if err := json.Unmarshal(jsonData, &limitData); err != nil {
        return 0, err
    }
    return limitData.Rate.Remaining, nil
}

func getReposURL(username string) (string, error) {
        log.Println("--- reached getReposURL for ", username)

        userJsonData, err := getData("https://api.github.com/users/" + username)
        if err != nil {
            return "", err
        }

        var user ghlib.GhUser
        if err := json.Unmarshal(userJsonData, &user); err != nil {
            return "", err
        }
        return user.ReposUrl, nil
}

func processCollaborators(collabURL string, currentDepth int) {
        log.Println("--- reached processCollaborators for ", collabURL)
        if _, exists := visited[collabURL]; exists {
            log.Println("--- skipped ", collabURL)
            return
        }
        visited[collabURL] = collabURL

        jsonData, err := getData(collabURL)
        if err != nil {
            return
        }

        var collaborators []*ghlib.GhUser
        err = json.Unmarshal(jsonData, &collaborators)
        if err != nil {
            log.Println("Error while parsing collaborators: ", err)
            return
        }
        //for each collaborator
        for _, collaborator := range collaborators {
            //handle user if not previously listed
            tempUser := collaborator.Login
            if _, exists := visited[tempUser]; exists {
                continue
            }
            //We found new user in network
            for t:=0; t<=currentDepth; t++ {
                fmt.Print("\t|")
            }
            fmt.Print(tempUser, "\n")
            visited[tempUser] = tempUser
            tempRepoURL := collaborator.ReposUrl

            //make a call to processRepo(tempRepoURL)
            processRepos(tempRepoURL, currentDepth+1)
        } //end for
}

func processRepos(repoURL string, currentDepth int) {
        log.Println("--- reached processRepos for ", repoURL)
        if currentDepth > maxDepth {
            log.Println("maxDepth reached")
            return
        }

        if _, exists := visited[repoURL]; exists {
            log.Println("--- skipped ", repoURL)
            return
        }
        visited[repoURL] = repoURL

        repoData, err := getData(repoURL) //get a list of repositories
        if err != nil {
            log.Println("err while getting data", err)
            return
        }

        var repoList []*ghlib.GhRepository
        err = json.Unmarshal(repoData, &repoList)
        if err != nil {
            log.Println("Error while parsing repo list: ", err)
            return
        }

        //m := min(len(repoList), 2)
        //repoList = repoList[:m] //limit to only 2 entries for time being

        for _, repo := range repoList {
            tempCollabsURL := repo.CollaboratorsUrl
            log.Println(tempCollabsURL)
            idx := strings.Index(tempCollabsURL, "{")
            //use bytes package for serious string manipulation. much faster
            collabURL := tempCollabsURL[:idx]
            processCollaborators(collabURL, currentDepth)
        }

} //end processRepos

func main() {
    f, err := os.OpenFile("/tmp/linkedhub.log", os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0666)
    if err != nil {
        fmt.Println("Could not open file for logging")
        return
    }
    defer f.Close()

    log.SetOutput(f)

    fmt.Println("Enter github credentials")
    fmt.Print("username: ")
    fmt.Scanln(&username)
    fmt.Print("password: ")
    fmt.Scanln(&password)

    //find out current API limit
    limit, err := getApiLimit()
    if err != nil {
        fmt.Println("error while getting limit: ", err)
        return
    }
    if limit <= 10 {
        fmt.Println("Too few of API calls left. Not worth it.")
        return
    }
    requestsLeft = limit
    fmt.Println("requestsLeft: ", requestsLeft)

    //get username from command line
    var u string
    fmt.Println("Enter github username: ")
    fmt.Scanln(&u)

    repoURL, err := getReposURL(u)
    if err != nil {
        log.Println("error while getting repo url for: ", u)
        return
    }

    processRepos(repoURL, 0)
}

<commit_msg>collaborators -> contributors<commit_after>/*
    User : https://api.github.com/users/Omie
        returns a dict
        has repos_url : https://api.github.com/users/Omie/repos
    Repos : https://api.github.com/users/Omie/repos
        returns a list of dict
        has contributors_url : https://api.github.com/repos/Omie/configfiles/contributors
    Contributors : https://api.github.com/repos/Omie/configfiles/contributors
        returns a list of dict
        has repos_url for each user
*/

package main

import (
        "os"
        "fmt"
        "encoding/json"
        "io/ioutil"
        "net/http"
        "log"
        "errors"
        "github.com/omie/ghlib"
)

var visited = make(map[string]string)

var requestsLeft int = 60

var username, password string

const maxDepth int = 3

//because Math.Min is for float64
func min(a, b int) int {
    if a <= b {
        return a
    }
    return b
}

func getData(url string) ([]byte, error) {
        log.Println("--- reached getData for ", url)

        requestsLeft--
        if requestsLeft < 0 {
            log.Println("--- LIMIT REACHED ")
            return nil, errors.New("limit reached")
        }

        client := &http.Client{}

        /* Authenticate */
        req, err := http.NewRequest("GET", url, nil)
        req.SetBasicAuth(username, password)
        resp, err := client.Do(req)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            return nil, err
        }

        return body, nil
}

func getApiLimit() (int, error) {
    jsonData, err := getData("https://api.github.com/rate_limit")
    if err != nil {
        return 0, err
    }

    var limitData ghlib.GhLimit
    if err := json.Unmarshal(jsonData, &limitData); err != nil {
        return 0, err
    }
    return limitData.Rate.Remaining, nil
}

func getReposURL(username string) (string, error) {
        log.Println("--- reached getReposURL for ", username)

        userJsonData, err := getData("https://api.github.com/users/" + username)
        if err != nil {
            return "", err
        }

        var user ghlib.GhUser
        if err := json.Unmarshal(userJsonData, &user); err != nil {
            return "", err
        }
        return user.ReposUrl, nil
}

func processContributors(contribURL string, currentDepth int) {
        log.Println("--- reached processContributors for ", contribURL)
        if _, exists := visited[contribURL]; exists {
            log.Println("--- skipped ", contribURL)
            return
        }
        visited[contribURL] = contribURL

        jsonData, err := getData(contribURL)
        if err != nil {
            return
        }

        var contributors []*ghlib.GhUser
        err = json.Unmarshal(jsonData, &contributors)
        if err != nil {
            log.Println("Error while parsing contributors: ", err)
            return
        }
        //for each contributor
        for _, contributor := range contributors {
            //handle user if not previously listed
            tempUser := contributor.Login
            if _, exists := visited[tempUser]; exists {
                continue
            }
            //We found new user in network
            for t:=0; t<=currentDepth; t++ {
                fmt.Print("\t|")
            }
            fmt.Print(tempUser, "\n")
            visited[tempUser] = tempUser
            tempRepoURL := contributor.ReposUrl

            //make a call to processRepo(tempRepoURL)
            processRepos(tempRepoURL, currentDepth+1)
        } //end for
}

func processRepos(repoURL string, currentDepth int) {
        log.Println("--- reached processRepos for ", repoURL)
        if currentDepth > maxDepth {
            log.Println("maxDepth reached")
            return
        }

        if _, exists := visited[repoURL]; exists {
            log.Println("--- skipped ", repoURL)
            return
        }
        visited[repoURL] = repoURL

        repoData, err := getData(repoURL) //get a list of repositories
        if err != nil {
            log.Println("err while getting data", err)
            return
        }

        var repoList []*ghlib.GhRepository
        err = json.Unmarshal(repoData, &repoList)
        if err != nil {
            log.Println("Error while parsing repo list: ", err)
            return
        }

        //m := min(len(repoList), 2)
        //repoList = repoList[:m] //limit to only 2 entries for time being

        for _, repo := range repoList {
            contribURL := repo.ContributorsUrl
            log.Println(contribURL)
            processContributors(contribURL, currentDepth)
        }

} //end processRepos

func main() {
    f, err := os.OpenFile("/tmp/linkedhub.log", os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0666)
    if err != nil {
        fmt.Println("Could not open file for logging")
        return
    }
    defer f.Close()

    log.SetOutput(f)

    fmt.Println("Enter github credentials")
    fmt.Print("username: ")
    fmt.Scanln(&username)
    fmt.Print("password: ")
    fmt.Scanln(&password)

    //find out current API limit
    limit, err := getApiLimit()
    if err != nil {
        fmt.Println("error while getting limit: ", err)
        return
    }
    if limit <= 10 {
        fmt.Println("Too few of API calls left. Not worth it.")
        return
    }
    requestsLeft = limit
    fmt.Println("requestsLeft: ", requestsLeft)

    //get username from command line
    var u string
    fmt.Println("Enter github username: ")
    fmt.Scanln(&u)

    repoURL, err := getReposURL(u)
    if err != nil {
        log.Println("error while getting repo url for: ", u)
        return
    }

    processRepos(repoURL, 0)
}

<|endoftext|>


<commit_before>package fscache

import (
	"io"
	"os"
	"sync"
	"sync/atomic"
	"time"
)

type Cache struct {
	mu    sync.RWMutex
	files map[string]*cachedFile
	grim  Reaper
	fs    FileSystem
}

// New creates a new Cache using NewFs(dir, perms).
// expiry is the # of hours after which an un-accessed key will be
// removed from the cache, an expiry of 0 means never expire.
func New(dir string, perms os.FileMode, expiry int) (*Cache, error) {
	fs, err := NewFs(dir, perms)
	if err != nil {
		return nil, err
	}
	var grim Reaper
	if expiry > 0 {
		grim = &reaper{
			expiry: time.Duration(expiry) * time.Hour,
			period: time.Duration(expiry) * time.Hour,
		}
	}
	return NewCache(fs, grim)
}

// NewCache creates a new Cache based on FileSystem fs.
// fs.Files() are loaded using the name they were created with as a key.
// Reaper is used to determine when files expire, nil means never expire.
func NewCache(fs FileSystem, grim Reaper) (*Cache, error) {
	c := &Cache{
		files: make(map[string]*cachedFile),
		grim:  grim,
		fs:    fs,
	}
	err := c.load()
	if err != nil {
		return nil, err
	}
	if grim != nil {
		c.haunter()
	}
	return c, nil
}

func (c *Cache) haunter() {
	c.haunt()
	time.AfterFunc(c.grim.Next(), c.haunter)
}

func (c *Cache) haunt() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	for key, f := range c.files {
		if atomic.LoadInt64(&f.cnt) > 0 {
			continue
		}

		lastRead, err := c.fs.LastAccess(f.name)
		if err != nil {
			continue
		}

		if c.grim.Reap(key, lastRead) {
			delete(c.files, key)
			return c.fs.Remove(f.name)
		}
	}
	return nil
}

func (c *Cache) load() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.fs.Reload(func(key, name string) {
		c.files[key] = c.oldFile(name)
	})
}

// Exists checks if a key is in the cache.
// It is safe to call Exists concurrently with Get.
func (c *Cache) Exists(key string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	_, ok := c.files[key]
	return ok
}

// Get manages access to the streams in the cache.
// If the key does not exist, w != nil and you can start writing to the stream.
// If the key does exist, w == nil.
// r will always be non-nil as long as err == nil and you must close r when you're done reading.
// Get can be called concurrently, and writing and reading is concurrent safe.
func (c *Cache) Get(key string) (r io.ReadCloser, w io.WriteCloser, err error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	f, ok := c.files[key]
	if !ok {
		f, err = c.newFile(key)
		if err != nil {
			return nil, nil, err
		}
		w = f
		c.files[key] = f
	}
	r, err = f.next()

	return r, w, err
}

// Remove deletes the stream from the cache, blocking until the underlying
// file can be deleted (all active streams finish with it).
// It is safe to call Remove concurrently with Get.
func (c *Cache) Remove(key string) error {
	c.mu.Lock()
	f, ok := c.files[key]
	delete(c.files, key)
	c.mu.Unlock()

	if ok {
		f.grp.Wait()
		return c.fs.Remove(f.name)
	}
	return nil
}

// Clean will empty the cache and delete the cache folder.
// Clean is not safe to call while streams are being read/written.
func (c *Cache) Clean() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.files = make(map[string]*cachedFile)
	return c.fs.RemoveAll()
}

type cachedFile struct {
	fs   FileSystem
	name string
	cnt  int64
	grp  sync.WaitGroup
	w    io.WriteCloser
	b    *broadcaster
}

func (c *Cache) newFile(name string) (*cachedFile, error) {
	f, err := c.fs.Create(name)
	if err != nil {
		return nil, err
	}
	cf := &cachedFile{
		fs:   c.fs,
		name: f.Name(),
		w:    f,
		b:    newBroadcaster(),
	}
	cf.grp.Add(1)
	atomic.AddInt64(&cf.cnt, 1)
	return cf, nil
}

func (c *Cache) oldFile(name string) *cachedFile {
	b := newBroadcaster()
	b.Close()
	return &cachedFile{
		fs:   c.fs,
		name: name,
		b:    b,
	}
}

func (f *cachedFile) next() (r io.ReadCloser, err error) {
	r, err = f.fs.Open(f.name)
	if err != nil {
		return nil, err
	}
	f.grp.Add(1)
	atomic.AddInt64(&f.cnt, 1)
	return &cacheReader{
		grp: &f.grp,
		cnt: &f.cnt,
		r:   r,
		b:   f.b,
	}, nil
}

func (f *cachedFile) Write(p []byte) (int, error) {
	defer f.b.Broadcast()
	f.b.Lock()
	defer f.b.Unlock()
	return f.w.Write(p)
}

func (f *cachedFile) Close() error {
	defer f.grp.Done()
	defer func() { atomic.AddInt64(&f.cnt, -1) }()
	defer f.b.Close()
	return f.w.Close()
}

type cacheReader struct {
	r   io.ReadCloser
	grp *sync.WaitGroup
	cnt *int64
	b   *broadcaster
}

func (r *cacheReader) Read(p []byte) (n int, err error) {
	r.b.RLock()
	defer r.b.RUnlock()

	for {

		n, err = r.r.Read(p)

		if r.b.IsOpen() { // file is still being written to

			if n != 0 && err == nil { // successful read
				return n, nil
			} else if err == io.EOF { // no data read, wait for some
				r.b.RUnlock()
				r.b.Wait()
				r.b.RLock()
			} else if err != nil { // non-nil, non-eof error
				return n, err
			}

		} else { // file is closed, just return
			return n, err
		}

	}

	return n, err
}

func (r *cacheReader) Close() error {
	defer r.grp.Done()
	defer func() { atomic.AddInt64(r.cnt, -1) }()
	return r.r.Close()
}
<commit_msg>fixed a bug where haunt would end early<commit_after>package fscache

import (
	"io"
	"os"
	"sync"
	"sync/atomic"
	"time"
)

type Cache struct {
	mu    sync.RWMutex
	files map[string]*cachedFile
	grim  Reaper
	fs    FileSystem
}

// New creates a new Cache using NewFs(dir, perms).
// expiry is the # of hours after which an un-accessed key will be
// removed from the cache, an expiry of 0 means never expire.
func New(dir string, perms os.FileMode, expiry int) (*Cache, error) {
	fs, err := NewFs(dir, perms)
	if err != nil {
		return nil, err
	}
	var grim Reaper
	if expiry > 0 {
		grim = &reaper{
			expiry: time.Duration(expiry) * time.Hour,
			period: time.Duration(expiry) * time.Hour,
		}
	}
	return NewCache(fs, grim)
}

// NewCache creates a new Cache based on FileSystem fs.
// fs.Files() are loaded using the name they were created with as a key.
// Reaper is used to determine when files expire, nil means never expire.
func NewCache(fs FileSystem, grim Reaper) (*Cache, error) {
	c := &Cache{
		files: make(map[string]*cachedFile),
		grim:  grim,
		fs:    fs,
	}
	err := c.load()
	if err != nil {
		return nil, err
	}
	if grim != nil {
		c.haunter()
	}
	return c, nil
}

func (c *Cache) haunter() {
	c.haunt()
	time.AfterFunc(c.grim.Next(), c.haunter)
}

func (c *Cache) haunt() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	for key, f := range c.files {
		if atomic.LoadInt64(&f.cnt) > 0 {
			continue
		}

		lastRead, err := c.fs.LastAccess(f.name)
		if err != nil {
			continue
		}

		if c.grim.Reap(key, lastRead) {
			delete(c.files, key)
			c.fs.Remove(f.name)
		}
	}
	return nil
}

func (c *Cache) load() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.fs.Reload(func(key, name string) {
		c.files[key] = c.oldFile(name)
	})
}

// Exists checks if a key is in the cache.
// It is safe to call Exists concurrently with Get.
func (c *Cache) Exists(key string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	_, ok := c.files[key]
	return ok
}

// Get manages access to the streams in the cache.
// If the key does not exist, w != nil and you can start writing to the stream.
// If the key does exist, w == nil.
// r will always be non-nil as long as err == nil and you must close r when you're done reading.
// Get can be called concurrently, and writing and reading is concurrent safe.
func (c *Cache) Get(key string) (r io.ReadCloser, w io.WriteCloser, err error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	f, ok := c.files[key]
	if !ok {
		f, err = c.newFile(key)
		if err != nil {
			return nil, nil, err
		}
		w = f
		c.files[key] = f
	}
	r, err = f.next()

	return r, w, err
}

// Remove deletes the stream from the cache, blocking until the underlying
// file can be deleted (all active streams finish with it).
// It is safe to call Remove concurrently with Get.
func (c *Cache) Remove(key string) error {
	c.mu.Lock()
	f, ok := c.files[key]
	delete(c.files, key)
	c.mu.Unlock()

	if ok {
		f.grp.Wait()
		return c.fs.Remove(f.name)
	}
	return nil
}

// Clean will empty the cache and delete the cache folder.
// Clean is not safe to call while streams are being read/written.
func (c *Cache) Clean() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.files = make(map[string]*cachedFile)
	return c.fs.RemoveAll()
}

type cachedFile struct {
	fs   FileSystem
	name string
	cnt  int64
	grp  sync.WaitGroup
	w    io.WriteCloser
	b    *broadcaster
}

func (c *Cache) newFile(name string) (*cachedFile, error) {
	f, err := c.fs.Create(name)
	if err != nil {
		return nil, err
	}
	cf := &cachedFile{
		fs:   c.fs,
		name: f.Name(),
		w:    f,
		b:    newBroadcaster(),
	}
	cf.grp.Add(1)
	atomic.AddInt64(&cf.cnt, 1)
	return cf, nil
}

func (c *Cache) oldFile(name string) *cachedFile {
	b := newBroadcaster()
	b.Close()
	return &cachedFile{
		fs:   c.fs,
		name: name,
		b:    b,
	}
}

func (f *cachedFile) next() (r io.ReadCloser, err error) {
	r, err = f.fs.Open(f.name)
	if err != nil {
		return nil, err
	}
	f.grp.Add(1)
	atomic.AddInt64(&f.cnt, 1)
	return &cacheReader{
		grp: &f.grp,
		cnt: &f.cnt,
		r:   r,
		b:   f.b,
	}, nil
}

func (f *cachedFile) Write(p []byte) (int, error) {
	defer f.b.Broadcast()
	f.b.Lock()
	defer f.b.Unlock()
	return f.w.Write(p)
}

func (f *cachedFile) Close() error {
	defer f.grp.Done()
	defer func() { atomic.AddInt64(&f.cnt, -1) }()
	defer f.b.Close()
	return f.w.Close()
}

type cacheReader struct {
	r   io.ReadCloser
	grp *sync.WaitGroup
	cnt *int64
	b   *broadcaster
}

func (r *cacheReader) Read(p []byte) (n int, err error) {
	r.b.RLock()
	defer r.b.RUnlock()

	for {

		n, err = r.r.Read(p)

		if r.b.IsOpen() { // file is still being written to

			if n != 0 && err == nil { // successful read
				return n, nil
			} else if err == io.EOF { // no data read, wait for some
				r.b.RUnlock()
				r.b.Wait()
				r.b.RLock()
			} else if err != nil { // non-nil, non-eof error
				return n, err
			}

		} else { // file is closed, just return
			return n, err
		}

	}

	return n, err
}

func (r *cacheReader) Close() error {
	defer r.grp.Done()
	defer func() { atomic.AddInt64(r.cnt, -1) }()
	return r.r.Close()
}
<|endoftext|>


<commit_before>package misc

import (
    //"unsafe"
    "encoding/binary"
    "jvmgo/jvm/rtda"
)

func init() {
    _unsafe(allocateMemory, "allocateMemory",   "(J)J")
    _unsafe(freeMemory,     "freeMemory",       "(J)V")
    _unsafe(putLong,        "putLong",          "(JJ)V")
    _unsafe(getByte,        "getByte",          "(J)B")
}

// public native long allocateMemory(long bytes);
// (J)J
func allocateMemory(frame *rtda.Frame) {
    stack := frame.OperandStack()
    bytes := stack.PopLong()
    stack.PopRef() // this

    address := allocate(bytes)
    stack.PushLong(address)
}

// public native void freeMemory(long address);
// (J)V
func freeMemory(frame *rtda.Frame) {
    stack := frame.OperandStack()
    address := stack.PopLong()
    stack.PopRef() // this
    free(address)
}

// public native void putByte(long offset, byte b);

// public native byte getByte(long offset);
// (J)B
func getByte(frame *rtda.Frame) {
    stack := frame.OperandStack()
    address := stack.PopLong()
    stack.PopRef() // this

    mem := memoryAt(address)
    b := mem[0]
    stack.PushInt(int32(b))
}

// public native void putLong(long offset, long x);
// (JJ)V
func putLong(frame *rtda.Frame) {
    stack := frame.OperandStack()
    value := stack.PopLong()
    address := stack.PopLong()
    stack.PopRef() // this

    mem := memoryAt(address)
    binary.BigEndian.PutUint64(mem, uint64(value))
}
<commit_msg>native: Unsafe.putByte()<commit_after>package misc

import (
    //"unsafe"
    "encoding/binary"
    "jvmgo/jvm/rtda"
)

func init() {
    _unsafe(allocateMemory, "allocateMemory",   "(J)J")
    _unsafe(freeMemory,     "freeMemory",       "(J)V")
    _unsafe(putLong,        "putLong",          "(JJ)V")
    _unsafe(putByte,        "putByte",          "(JB)V")
    _unsafe(getByte,        "getByte",          "(J)B")
}

// public native long allocateMemory(long bytes);
// (J)J
func allocateMemory(frame *rtda.Frame) {
    stack := frame.OperandStack()
    bytes := stack.PopLong()
    stack.PopRef() // this

    address := allocate(bytes)
    stack.PushLong(address)
}

// public native void freeMemory(long address);
// (J)V
func freeMemory(frame *rtda.Frame) {
    stack := frame.OperandStack()
    address := stack.PopLong()
    stack.PopRef() // this
    free(address)
}

// public native void putByte(long address, byte b);
// (JB)V
func putByte(frame *rtda.Frame) {
    stack := frame.OperandStack()
    b := stack.PopInt()
    address := stack.PopLong()
    stack.PopRef() // this

    mem := memoryAt(address)
    mem[0] = uint8(b)
}

// public native byte getByte(long address);
// (J)B
func getByte(frame *rtda.Frame) {
    stack := frame.OperandStack()
    address := stack.PopLong()
    stack.PopRef() // this

    mem := memoryAt(address)
    b := mem[0]
    stack.PushInt(int32(b))
}

// public native void putLong(long address, long x);
// (JJ)V
func putLong(frame *rtda.Frame) {
    stack := frame.OperandStack()
    value := stack.PopLong()
    address := stack.PopLong()
    stack.PopRef() // this

    mem := memoryAt(address)
    binary.BigEndian.PutUint64(mem, uint64(value))
}
<|endoftext|>


<commit_before>/**
 * @file counter_test.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU AGPLv3
 * @date September, 2015
 * @brief test counter package
 */

package counter_test

import (
	"database/sql"
	"fmt"
	"log"
	"testing"
	"time"
)

import (
	"github.com/jollheef/tin_foil_hat/counter"
	"github.com/jollheef/tin_foil_hat/steward"
	"github.com/jollheef/tin_foil_hat/vexillary"
)

type testDB struct {
	db *sql.DB
}

const db_path string = "user=postgres dbname=tinfoilhat_test sslmode=disable"

func openDB() (t testDB, err error) {

	t.db, err = steward.OpenDatabase(db_path)

	t.Close()

	t.db, err = steward.OpenDatabase(db_path)

	return
}

func (t testDB) Close() {

	t.db.Exec("DROP TABLE team")
	t.db.Exec("DROP TABLE advisory")
	t.db.Exec("DROP TABLE captured_flag")
	t.db.Exec("DROP TABLE flag")
	t.db.Exec("DROP TABLE service")
	t.db.Exec("DROP TABLE status")
	t.db.Exec("DROP TABLE round")
	t.db.Exec("DROP TABLE round_result")

	t.db.Close()
}

func TestCountStatesResult(*testing.T) {

	db, err := openDB()
	if err != nil {
		log.Fatalln("Open database failed:", err)
	}

	defer db.Close()

	r := 1 // round
	t := 1 // team id
	s := 1 // service id

	svc := steward.Service{ID: s, Name: "foo", Port: 8080,
		CheckerPath: "", UDP: false}

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusMumble})

	res, err := counter.CountStatesResult(db.db, r, t, svc)
	if err != nil {
		log.Fatalln("Count states failed:", err)
	}

	must_be := 0.75

	if res != must_be {
		log.Fatalln("Result invalid:", res, "instead", must_be)
	}

}

func TestCountDefenceResult(*testing.T) {

	db, err := openDB()
	if err != nil {
		log.Fatalln("Open database failed:", err)
	}

	defer db.Close()

	r := 1
	t := 1

	services := make([]steward.Service, 0)
	services = append(services, steward.Service{ID: 1, Name: "foo",
		Port: 8080, CheckerPath: "", UDP: false})
	services = append(services, steward.Service{ID: 2, Name: "bar",
		Port: 8081, CheckerPath: "", UDP: false})
	services = append(services, steward.Service{ID: 3, Name: "baz",
		Port: 8082, CheckerPath: "", UDP: false})
	services = append(services, steward.Service{ID: 4, Name: "qwe",
		Port: 8083, CheckerPath: "", UDP: false})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusDown})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 3, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 3, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 3, State: steward.StatusUP})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusDown})

	res, err := counter.CountDefenceResult(db.db, r, t, services)
	if err != nil {
		log.Fatalln("Count defence result failed:", err)
	}

	must_be := 0.75*0.25 + 0 + 1*0.25 + 0.25*0.25

	if res != must_be {
		log.Fatalln("Result invalid:", res, "instead", must_be)

	}
}

func TestCountRound(*testing.T) {

	db, err := openDB()
	if err != nil {
		log.Fatalln("Open database failed:", err)
	}

	defer db.Close()

	for index, team := range []string{
		"FooTeam", "BarTeam", "BazTeam", "Ololosha"} {

		// just trick for bypass UNIQUE team subnet
		subnet := fmt.Sprintf("127.%d.0.1/24", index)

		vulnbox := fmt.Sprintf("127.0.%d.3/24", index)

		t := steward.Team{ID: -1, Name: team, Subnet: subnet,
			Vulnbox: vulnbox}

		_, err = steward.AddTeam(db.db, t)
		if err != nil {
			log.Fatalln("Add team failed:", err)
		}
	}

	for _, service := range []string{"Foo", "Bar", "Baz", "Boo"} {

		err = steward.AddService(db.db,
			steward.Service{ID: -1, Name: service, Port: 8080,
				CheckerPath: "", UDP: false})
		if err != nil {
			log.Fatalln("Add service failed:", err)
		}
	}

	priv, err := vexillary.GenerateKey()
	if err != nil {
		log.Fatalln("Generate key failed:", err)
	}

	round, err := steward.NewRound(db.db, time.Minute)
	if err != nil {
		log.Fatalln("Create new round failed:", err)
	}

	teams, err := steward.GetTeams(db.db)
	if err != nil {
		log.Fatalln("Get teams failed:", err)
	}

	services, err := steward.GetServices(db.db)
	if err != nil {
		log.Fatalln("Get services failed:", err)
	}

	flags := make([]string, 0)

	for _, team := range teams {
		for _, svc := range services {

			flag, err := vexillary.GenerateFlag(priv)
			if err != nil {
				log.Fatalln("Generate flag failed:", err)
			}

			flags = append(flags, flag)

			flg := steward.Flag{ID: -1, Flag: flag, Round: round,
				TeamID: team.ID, ServiceID: svc.ID, CreD: ""}

			err = steward.AddFlag(db.db, flg)
			if err != nil {
				log.Fatalln("Add flag to database failed:", err)
			}

			err = steward.PutStatus(db.db, steward.Status{
				Round: round, TeamID: team.ID,
				ServiceID: svc.ID, State: steward.StatusUP})
			if err != nil {
				log.Fatalln("Put status to database failed:", err)
			}
		}
	}

	flag1, err := steward.GetFlagInfo(db.db, flags[2])
	if err != nil {
		log.Fatalln("Get flag info failed:", err)
	}

	err = steward.CaptureFlag(db.db, flag1.ID, teams[2].ID)
	if err != nil {
		log.Fatalln("Capture flag failed:", err)
	}

	flag2, err := steward.GetFlagInfo(db.db, flags[7])
	if err != nil {
		log.Fatalln("Get flag info failed:", err)
	}

	err = steward.CaptureFlag(db.db, flag2.ID, teams[3].ID)
	if err != nil {
		log.Fatalln("Capture flag failed:", err)
	}

	err = counter.CountRound(db.db, round, teams, services)
	if err != nil {
		log.Fatalln("Count round failed:", err)
	}

	res, err := steward.GetRoundResult(db.db, teams[0].ID, round)
	if err != nil || res.AttackScore != 0.0 || res.DefenceScore != 1.75 {
		log.Fatalln("Invalid result:", res)
	}

	res, err = steward.GetRoundResult(db.db, teams[1].ID, round)
	if err != nil || res.AttackScore != 0.0 || res.DefenceScore != 1.75 {
		log.Fatalln("Invalid result:", res)
	}

	res, err = steward.GetRoundResult(db.db, teams[2].ID, round)
	if err != nil || res.AttackScore != 0.25 || res.DefenceScore != 2.0 {
		log.Fatalln("Invalid result:", res)
	}

	res, err = steward.GetRoundResult(db.db, teams[3].ID, round)
	if err != nil || res.AttackScore != 0.25 || res.DefenceScore != 2.0 {
		log.Fatalln("Invalid result:", res)
	}

}
<commit_msg>Fix mistake<commit_after>/**
 * @file counter_test.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU AGPLv3
 * @date September, 2015
 * @brief test counter package
 */

package counter_test

import (
	"database/sql"
	"fmt"
	"log"
	"testing"
	"time"
)

import (
	"github.com/jollheef/tin_foil_hat/counter"
	"github.com/jollheef/tin_foil_hat/steward"
	"github.com/jollheef/tin_foil_hat/vexillary"
)

type testDB struct {
	db *sql.DB
}

const db_path string = "user=postgres dbname=tinfoilhat_test sslmode=disable"

func openDB() (t testDB, err error) {

	t.db, err = steward.OpenDatabase(db_path)

	t.Close()

	t.db, err = steward.OpenDatabase(db_path)

	return
}

func (t testDB) Close() {

	t.db.Exec("DROP TABLE team")
	t.db.Exec("DROP TABLE advisory")
	t.db.Exec("DROP TABLE captured_flag")
	t.db.Exec("DROP TABLE flag")
	t.db.Exec("DROP TABLE service")
	t.db.Exec("DROP TABLE status")
	t.db.Exec("DROP TABLE round")
	t.db.Exec("DROP TABLE round_result")

	t.db.Close()
}

func TestCountStatesResult(*testing.T) {

	db, err := openDB()
	if err != nil {
		log.Fatalln("Open database failed:", err)
	}

	defer db.Close()

	r := 1 // round
	t := 1 // team id
	s := 1 // service id

	svc := steward.Service{ID: s, Name: "foo", Port: 8080,
		CheckerPath: "", UDP: false}

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: s, State: steward.StatusMumble})

	res, err := counter.CountStatesResult(db.db, r, t, svc)
	if err != nil {
		log.Fatalln("Count states failed:", err)
	}

	must_be := 0.75

	if res != must_be {
		log.Fatalln("Result invalid:", res, "instead", must_be)
	}

}

func TestCountDefenceResult(*testing.T) {

	db, err := openDB()
	if err != nil {
		log.Fatalln("Open database failed:", err)
	}

	defer db.Close()

	r := 1
	t := 1

	services := make([]steward.Service, 0)
	services = append(services, steward.Service{ID: 1, Name: "foo",
		Port: 8080, CheckerPath: "", UDP: false})
	services = append(services, steward.Service{ID: 2, Name: "bar",
		Port: 8081, CheckerPath: "", UDP: false})
	services = append(services, steward.Service{ID: 3, Name: "baz",
		Port: 8082, CheckerPath: "", UDP: false})
	services = append(services, steward.Service{ID: 4, Name: "qwe",
		Port: 8083, CheckerPath: "", UDP: false})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 1, State: steward.StatusDown})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 2, State: steward.StatusDown})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 3, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 3, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 3, State: steward.StatusUP})

	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusUP})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusDown})
	steward.PutStatus(db.db, steward.Status{Round: r, TeamID: t,
		ServiceID: 4, State: steward.StatusDown})

	res, err := counter.CountDefenceResult(db.db, r, t, services)
	if err != nil {
		log.Fatalln("Count defence result failed:", err)
	}

	must_be := 0.75*0.25 + 0 + 1*0.25 + 0.25*0.25

	if res != must_be {
		log.Fatalln("Result invalid:", res, "instead", must_be)

	}
}

func TestCountRound(*testing.T) {

	db, err := openDB()
	if err != nil {
		log.Fatalln("Open database failed:", err)
	}

	defer db.Close()

	for index, team := range []string{
		"FooTeam", "BarTeam", "BazTeam", "Ololosha"} {

		// just trick for bypass UNIQUE team subnet
		subnet := fmt.Sprintf("127.%d.0.1/24", index)

		vulnbox := fmt.Sprintf("127.0.%d.3/24", index)

		t := steward.Team{ID: -1, Name: team, Subnet: subnet,
			Vulnbox: vulnbox}

		_, err = steward.AddTeam(db.db, t)
		if err != nil {
			log.Fatalln("Add team failed:", err)
		}
	}

	for _, service := range []string{"Foo", "Bar", "Baz", "Boo"} {

		err = steward.AddService(db.db,
			steward.Service{ID: -1, Name: service, Port: 8080,
				CheckerPath: "", UDP: false})
		if err != nil {
			log.Fatalln("Add service failed:", err)
		}
	}

	priv, err := vexillary.GenerateKey()
	if err != nil {
		log.Fatalln("Generate key failed:", err)
	}

	round, err := steward.NewRound(db.db, time.Minute)
	if err != nil {
		log.Fatalln("Create new round failed:", err)
	}

	teams, err := steward.GetTeams(db.db)
	if err != nil {
		log.Fatalln("Get teams failed:", err)
	}

	services, err := steward.GetServices(db.db)
	if err != nil {
		log.Fatalln("Get services failed:", err)
	}

	flags := make([]string, 0)

	for _, team := range teams {
		for _, svc := range services {

			flag, err := vexillary.GenerateFlag(priv)
			if err != nil {
				log.Fatalln("Generate flag failed:", err)
			}

			flags = append(flags, flag)

			flg := steward.Flag{ID: -1, Flag: flag, Round: round,
				TeamID: team.ID, ServiceID: svc.ID, Cred: ""}

			err = steward.AddFlag(db.db, flg)
			if err != nil {
				log.Fatalln("Add flag to database failed:", err)
			}

			err = steward.PutStatus(db.db, steward.Status{
				Round: round, TeamID: team.ID,
				ServiceID: svc.ID, State: steward.StatusUP})
			if err != nil {
				log.Fatalln("Put status to database failed:", err)
			}
		}
	}

	flag1, err := steward.GetFlagInfo(db.db, flags[2])
	if err != nil {
		log.Fatalln("Get flag info failed:", err)
	}

	err = steward.CaptureFlag(db.db, flag1.ID, teams[2].ID)
	if err != nil {
		log.Fatalln("Capture flag failed:", err)
	}

	flag2, err := steward.GetFlagInfo(db.db, flags[7])
	if err != nil {
		log.Fatalln("Get flag info failed:", err)
	}

	err = steward.CaptureFlag(db.db, flag2.ID, teams[3].ID)
	if err != nil {
		log.Fatalln("Capture flag failed:", err)
	}

	err = counter.CountRound(db.db, round, teams, services)
	if err != nil {
		log.Fatalln("Count round failed:", err)
	}

	res, err := steward.GetRoundResult(db.db, teams[0].ID, round)
	if err != nil || res.AttackScore != 0.0 || res.DefenceScore != 1.75 {
		log.Fatalln("Invalid result:", res)
	}

	res, err = steward.GetRoundResult(db.db, teams[1].ID, round)
	if err != nil || res.AttackScore != 0.0 || res.DefenceScore != 1.75 {
		log.Fatalln("Invalid result:", res)
	}

	res, err = steward.GetRoundResult(db.db, teams[2].ID, round)
	if err != nil || res.AttackScore != 0.25 || res.DefenceScore != 2.0 {
		log.Fatalln("Invalid result:", res)
	}

	res, err = steward.GetRoundResult(db.db, teams[3].ID, round)
	if err != nil || res.AttackScore != 0.25 || res.DefenceScore != 2.0 {
		log.Fatalln("Invalid result:", res)
	}

}
<|endoftext|>


<commit_before>package entrypoint_test

import (
	"strings"
	"testing"

	"github.com/datawire/ambassador/cmd/entrypoint"
	envoy "github.com/datawire/ambassador/pkg/api/envoy/api/v2"
	bootstrap "github.com/datawire/ambassador/pkg/api/envoy/config/bootstrap/v2"
	"github.com/datawire/ambassador/pkg/snapshot/v1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestFakeHello(t *testing.T) {
	// Spins up a fake ambassador that is running our real control plane.
	f := entrypoint.RunFake(t, entrypoint.FakeConfig{})

	// Feeds the control plane the kubernetes resources supplied in the referenced file.
	f.UpsertFile("testdata/FakeHello.yaml")
	f.AutoFlush(true)

	// Grab the next snapshot that satisfies the supplied predicate.
	snap := f.GetSnapshot(func(snap *snapshot.Snapshot) bool {
		return len(snap.Kubernetes.Mappings) > 0
	})
	// Check that the snapshot contains the mapping from the file.
	assert.Equal(t, "hello", snap.Kubernetes.Mappings[0].Name)
}

func TestFakeHelloWithEnvoyConfig(t *testing.T) {
	// Spins up a fake ambassador that is running our real control plane.
	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true})

	// Feeds the control plane the kubernetes resources supplied in the referenced file.
	f.UpsertFile("testdata/FakeHello.yaml")
	f.Flush()

	// Grab the next snapshot that satisfies the supplied predicate.
	snap := f.GetSnapshot(func(snap *snapshot.Snapshot) bool {
		return len(snap.Kubernetes.Mappings) > 0
	})
	// Check that the snapshot contains the mapping from the file.
	assert.Equal(t, "hello", snap.Kubernetes.Mappings[0].Name)

	// Create a predicate that will recognize the cluster we care about.
	isHelloCluster := func(c *envoy.Cluster) bool {
		return strings.Contains(c.Name, "hello")
	}

	// Grab the next envoy config that satisfies the supplied predicate.
	envoyConfig := f.GetEnvoyConfig(func(envoy *bootstrap.Bootstrap) bool {
		return FindCluster(envoy, isHelloCluster) != nil
	})

	cluster := FindCluster(envoyConfig, isHelloCluster)
	endpoints := cluster.LoadAssignment.Endpoints
	require.NotEmpty(t, endpoints)
	lbEndpoints := endpoints[0].LbEndpoints
	require.NotEmpty(t, lbEndpoints)
	endpoint := lbEndpoints[0].GetEndpoint()
	address := endpoint.Address.GetSocketAddress().Address
	assert.Equal(t, "hello", address)
}

func FindCluster(envoyConfig *bootstrap.Bootstrap, predicate func(*envoy.Cluster) bool) *envoy.Cluster {
	for _, cluster := range envoyConfig.StaticResources.Clusters {
		if predicate(cluster) {
			return cluster
		}
	}

	return nil
}

func TestFakeHelloConsul(t *testing.T) {
	// Spins up a fake ambassador that is running our real control plane.
	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true})

	// Feeds the control plane the kubernetes resources supplied in the referenced file. In this
	// case that includes a consul resolver.
	f.UpsertFile("testdata/FakeHelloConsul.yaml")
	f.Flush()
	f.ConsulEndpoint("dc1", "hello", "1.2.3.4", 8080)
	//f.ConsulEndpoint("dc1", "hello", "4.3.2.1", 8080)
	f.Flush()

	// Grab the next snapshot that satisfies the supplied predicate.
	snap := f.GetSnapshot(func(snap *snapshot.Snapshot) bool {
		return len(snap.Kubernetes.Mappings) > 0
	})
	// Check that the snapshot contains the mapping from the file.
	assert.Equal(t, "hello", snap.Kubernetes.Mappings[0].Name)

	// Create a predicate that will recognize the cluster we care about.
	isHelloCluster := func(c *envoy.Cluster) bool {
		return strings.Contains(c.Name, "hello")
	}

	// Grab the next envoy config that satisfies the supplied predicate.
	envoyConfig := f.GetEnvoyConfig(func(envoy *bootstrap.Bootstrap) bool {
		return FindCluster(envoy, isHelloCluster) != nil
	})

	cluster := FindCluster(envoyConfig, isHelloCluster)
	endpoints := cluster.LoadAssignment.Endpoints
	require.NotEmpty(t, endpoints)
	lbEndpoints := endpoints[0].LbEndpoints
	require.NotEmpty(t, lbEndpoints)

	endpoint := lbEndpoints[0].GetEndpoint()
	address := endpoint.Address.GetSocketAddress().Address
	assert.Equal(t, "1.2.3.4", address)

	/*	endpoint = lbEndpoints[1].GetEndpoint()
		address = endpoint.Address.GetSocketAddress().Address
		assert.Equal(t, "4.3.2.1", address)
	*/
}
<commit_msg>(from AES) more docs<commit_after>package entrypoint_test

import (
	"strings"
	"testing"

	"github.com/datawire/ambassador/cmd/entrypoint"
	envoy "github.com/datawire/ambassador/pkg/api/envoy/api/v2"
	bootstrap "github.com/datawire/ambassador/pkg/api/envoy/config/bootstrap/v2"
	"github.com/datawire/ambassador/pkg/snapshot/v1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// The Fake struct is a test harness for edgestack. It spins up the key portions of the edgestack
// control plane that contain the bulk of its business logic, but instead of requiring tests to feed
// the business logic inputs via a real kubernetes or real consul deployment, inputs can be fed
// directly into the business logic via the harness APIs. This is not only several orders of
// magnitute faster, this also provides the author of the test perfect control over the ordering of
// events.
func TestFakeHello(t *testing.T) {
	// Use RunFake() to spin up the ambassador control plane with its inputs wired up to the Fake
	// APIs. This will automatically invoke the Setup() method for the Fake and also register the
	// Teardown() method with the Cleanup() hook of the supplied testing.T object.
	f := entrypoint.RunFake(t, entrypoint.FakeConfig{})

	// The Fake harness has a store for both kubernetes resources and consul endpoint data. We can
	// use the UpsertFile() to method to load as many resources as we would like. This is much like
	// doing a `kubectl apply` to a real kubernetes API server, however apply uses fancy merge
	// logic, whereas UpsertFile() does a simple Upsert operation. The `testdata/FakeHello.yaml`
	// file has a single mapping named "hello".
	f.UpsertFile("testdata/FakeHello.yaml")
	// Initially the Fake harness is paused. This means we can make as many method calls as we want
	// to in order to set up our initial conditions, and no inputs will be fed into the control
	// plane. To feed inputs to the control plane, we can choose to either manually invoke the
	// Flush() method whenever we want to send the control plane inputs, or for convenience we can
	// enable AutoFlush so that inputs are set whenever we modify data that the control plane is
	// watching.
	f.AutoFlush(true)

	// Once the control plane has started processing inputs, we need some way to observe its
	// computation. The Fake harness provides two ways to do this. The GetSnapshot() method allows
	// us to observe the snapshots assembled by the watchers for further processing. The
	// GetEnvoyConfig() method allows us to observe the envoy configuration produced from a
	// snapshot. Both these methods take a predicate so the can search for a snapshot that satisifes
	// whatever conditions are being tested. This allows the test to verify that the correct
	// computation is occurring without being overly prescriptive about the exact number of
	// snapshots and/or envoy configs that are produce to achieve a certain result.
	snap := f.GetSnapshot(func(snap *snapshot.Snapshot) bool {
		return len(snap.Kubernetes.Mappings) > 0
	})
	// Check that the snapshot contains the mapping from the file.
	assert.Equal(t, "hello", snap.Kubernetes.Mappings[0].Name)
}

// By default the Fake struct only invokes the first part of the pipeline that forms the control
// plane. If you use the EnvoyConfig option you can run the rest of the control plane. There is also
// a Timeout option that controls how long the harness waits for the desired Snapshot and/or
// EnvoyConfig to come along.
//
// Note that this test depends on diagd being in your path. If diagd is not available, the test will
// be skipped.
func TestFakeHelloWithEnvoyConfig(t *testing.T) {
	// Use the FakeConfig parameter to conigure the Fake harness. In this case we want to inspect
	// the EnvoyConfig that is produced from the inputs we feed the control plane.
	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true})

	// We will use the same inputs we used in TestFakeHello. A single mapping named "hello".
	f.UpsertFile("testdata/FakeHello.yaml")
	// Instead of using AutoFlush(true) we will manually Flush() when we want to feed inputs to the
	// control plane.
	f.Flush()

	// Grab the next snapshot that has mappings. The bootstrap logic should actually gaurantee this
	// is also the first mapping, but we aren't trying to test that here.
	snap := f.GetSnapshot(func(snap *snapshot.Snapshot) bool {
		return len(snap.Kubernetes.Mappings) > 0
	})
	// The first snapshot should contain the one and only mapping we have supplied the control
	// plane.x
	assert.Equal(t, "hello", snap.Kubernetes.Mappings[0].Name)

	// Create a predicate that will recognize the cluster we care about. The surjection from
	// Mappings to clusters is a bit opaque, so we just look for a cluster that contains the name
	// hello.
	isHelloCluster := func(c *envoy.Cluster) bool {
		return strings.Contains(c.Name, "hello")
	}

	// Grab the next envoy config that satisfies our predicate.
	envoyConfig := f.GetEnvoyConfig(func(envoy *bootstrap.Bootstrap) bool {
		return FindCluster(envoy, isHelloCluster) != nil
	})

	// Now let's dig into the envoy configuration and check that the correct target endpoint is
	// present.
	//
	// Note: This is admittedly quite verbose as envoy configuration is very dense. I expect we will
	// introduce an API that will provide a more abstract and convenient way of navigating envoy
	// configuration, however that will be covered in a future PR. The core of that logic is already
	// developing inside ambex since ambex slices and dices the envoy config in order to implement
	// RDS and enpdoint routing.
	cluster := FindCluster(envoyConfig, isHelloCluster)
	endpoints := cluster.LoadAssignment.Endpoints
	require.NotEmpty(t, endpoints)
	lbEndpoints := endpoints[0].LbEndpoints
	require.NotEmpty(t, lbEndpoints)
	endpoint := lbEndpoints[0].GetEndpoint()
	address := endpoint.Address.GetSocketAddress().Address
	assert.Equal(t, "hello", address)
}

func FindCluster(envoyConfig *bootstrap.Bootstrap, predicate func(*envoy.Cluster) bool) *envoy.Cluster {
	for _, cluster := range envoyConfig.StaticResources.Clusters {
		if predicate(cluster) {
			return cluster
		}
	}

	return nil
}

// This test will cover how to exercise the consul portion of the control plane. In principal it is
// the same as supplying kubernetes resources, however it uses the ConsulEndpoint() method to
// provide consul data.
func TestFakeHelloConsul(t *testing.T) {
	// Create our Fake harness and tell it to produce envoy configuration.
	f := entrypoint.RunFake(t, entrypoint.FakeConfig{EnvoyConfig: true})

	// Feed the control plane the kubernetes resources supplied in the referenced file. In this case
	// that includes a consul resolver and a mapping that uses that consul resolver.
	f.UpsertFile("testdata/FakeHelloConsul.yaml")
	// This test is a bit more interesting for the control plane from a bootstrapping perspective,
	// so we invoke Flush() manually rather than using AutoFlush(true). The control plane needs to
	// figure out that there is a mapping that depends on consul endpoint data, and it needs to wait
	// until that data is available before producing the first snapshot.
	f.Flush()

	// XXX: It would be nice at this point to affirmitively test that no snapshot is produced until
	// we supply the endpoint data below, however that would require extending the inject APIs a
	// bit. This is something we can do in the future.

	// Now let's supply the endpoint data for the hello service referenced by our hello mapping.
	f.ConsulEndpoint("dc1", "hello", "1.2.3.4", 8080)
	f.Flush()

	// Grab the next snapshot that has mappings.
	snap := f.GetSnapshot(func(snap *snapshot.Snapshot) bool {
		return len(snap.Kubernetes.Mappings) > 0
	})
	// Check that the snapshot contains the mapping from the file.
	assert.Equal(t, "hello", snap.Kubernetes.Mappings[0].Name)

	// Create a predicate that will recognize the cluster we care about. The surjection from
	// Mappings to clusters is a bit opaque, so we just look for a cluster that contains the name
	// hello.
	isHelloCluster := func(c *envoy.Cluster) bool {
		return strings.Contains(c.Name, "hello")
	}

	// Grab the next envoy config that satisfies our predicate.
	envoyConfig := f.GetEnvoyConfig(func(envoy *bootstrap.Bootstrap) bool {
		return FindCluster(envoy, isHelloCluster) != nil
	})

	// Now let's check that it has the IP address we supplied in the consul data.
	//
	// Note: This is admittedly quite verbose as envoy configuration is very dense. I expect we will
	// introduce an API that will provide a more abstract and convenient way of navigating envoy
	// configuration, however that will be covered in a future PR. The core of that logic is already
	// developing inside ambex since ambex slices and dices the envoy config in order to implement
	// RDS and enpdoint routing.
	cluster := FindCluster(envoyConfig, isHelloCluster)
	endpoints := cluster.LoadAssignment.Endpoints
	require.NotEmpty(t, endpoints)
	lbEndpoints := endpoints[0].LbEndpoints
	require.NotEmpty(t, lbEndpoints)

	endpoint := lbEndpoints[0].GetEndpoint()
	address := endpoint.Address.GetSocketAddress().Address
	assert.Equal(t, "1.2.3.4", address)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"strings"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/sarama"
)

type UnderReplicated struct {
	Ui  cli.Ui
	Cmd string
}

func (this *UnderReplicated) Run(args []string) (exitCode int) {
	if len(args) == 0 {
		this.Ui.Error("zone required")
		this.Ui.Output(this.Help())
		return 2
	}

	for _, zone := range args {
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZonePath(zone)))
		zkzone.WithinClusters(func(cluster string, path string) {
			zkcluster := zkzone.NewCluster(cluster)
			this.displayUnderReplicatedPartitionsOfCluster(zkcluster)
		})
	}

	return
}

func (this *UnderReplicated) swallow(err error) {
	if err != nil {
		panic(err)
	}
}

func (this *UnderReplicated) displayUnderReplicatedPartitionsOfCluster(zkcluster *zk.ZkCluster) {
	this.Ui.Output(zkcluster.Name())

	brokerList := zkcluster.BrokerList()
	if len(brokerList) == 0 {
		this.Ui.Output(fmt.Sprintf("%4s%s", " ", color.Red("empty brokers")))
		return
	}

	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		this.Ui.Output(color.Yellow("%+v%s %s", " ", brokerList, err.Error()))

		return
	}
	defer kfk.Close()

	topics, err := kfk.Topics()
	this.swallow(err)
	if len(topics) == 0 {
		return
	}

	for _, topic := range topics {
		// get partitions and check if some dead
		alivePartitions, err := kfk.WritablePartitions(topic)
		this.swallow(err)
		partions, err := kfk.Partitions(topic)
		this.swallow(err)
		if len(alivePartitions) != len(partions) {
			this.Ui.Output(fmt.Sprintf("topic[%s] has %s partitions: %+v/%+v",
				alivePartitions, color.Red("dead"), partions))
		}

		for _, partitionID := range alivePartitions {
			replicas, err := kfk.Replicas(topic, partitionID)
			this.swallow(err)

			isr, err := kfk.Isr(topic, partitionID)
			this.swallow(err)

			underReplicated := false
			if len(isr) != len(replicas) {
				underReplicated = true
			}

			if underReplicated {
				leader, err := kfk.Leader(topic, partitionID)
				this.swallow(err)

				latestOffset, err := kfk.GetOffset(topic, partitionID,
					sarama.OffsetNewest)
				this.swallow(err)

				oldestOffset, err := kfk.GetOffset(topic, partitionID,
					sarama.OffsetOldest)
				this.swallow(err)

				this.Ui.Output(color.Red("\t%s Partition:%d Leader:%d Replicas:%+v Isr:%+v Offset:%d Num:%d",
					topic,
					partitionID, leader.ID(), replicas, isr,
					latestOffset, latestOffset-oldestOffset))
			}
		}
	}
}

func (*UnderReplicated) Synopsis() string {
	return "Display under-replicated partitions"
}

func (this *UnderReplicated) Help() string {
	help := fmt.Sprintf(`
Usage: %s underreplicated [zone ...]

	Display under-replicated partitions
`, this.Cmd)
	return strings.TrimSpace(help)
}
<commit_msg>bug fix: false alarm of underreplicated<commit_after>package command

import (
	"fmt"
	"strings"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/sarama"
)

type UnderReplicated struct {
	Ui  cli.Ui
	Cmd string
}

func (this *UnderReplicated) Run(args []string) (exitCode int) {
	if len(args) == 0 {
		this.Ui.Error("zone required")
		this.Ui.Output(this.Help())
		return 2
	}

	for _, zone := range args {
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZonePath(zone)))
		zkzone.WithinClusters(func(cluster string, path string) {
			zkcluster := zkzone.NewCluster(cluster)
			this.displayUnderReplicatedPartitionsOfCluster(zkcluster)
		})
	}

	return
}

func (this *UnderReplicated) swallow(err error) {
	if err != nil {
		panic(err)
	}
}

func (this *UnderReplicated) displayUnderReplicatedPartitionsOfCluster(zkcluster *zk.ZkCluster) {
	this.Ui.Output(zkcluster.Name())

	brokerList := zkcluster.BrokerList()
	if len(brokerList) == 0 {
		this.Ui.Output(fmt.Sprintf("%4s%s", " ", color.Red("empty brokers")))
		return
	}

	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		this.Ui.Output(color.Yellow("%+v%s %s", " ", brokerList, err.Error()))

		return
	}
	defer kfk.Close()

	topics, err := kfk.Topics()
	this.swallow(err)
	if len(topics) == 0 {
		return
	}

	for _, topic := range topics {
		// get partitions and check if some dead
		alivePartitions, err := kfk.WritablePartitions(topic)
		this.swallow(err)
		partions, err := kfk.Partitions(topic)
		this.swallow(err)
		if len(alivePartitions) != len(partions) {
			this.Ui.Output(fmt.Sprintf("topic[%s] has %s partitions: %+v/%+v",
				alivePartitions, color.Red("dead"), partions))
		}

		for _, partitionID := range alivePartitions {
			replicas, err := kfk.Replicas(topic, partitionID)
			this.swallow(err)

			isr := zkcluster.Isr(topic, partitionID)

			underReplicated := false
			if len(isr) != len(replicas) {
				underReplicated = true
			}

			if underReplicated {
				leader, err := kfk.Leader(topic, partitionID)
				this.swallow(err)

				latestOffset, err := kfk.GetOffset(topic, partitionID,
					sarama.OffsetNewest)
				this.swallow(err)

				oldestOffset, err := kfk.GetOffset(topic, partitionID,
					sarama.OffsetOldest)
				this.swallow(err)

				this.Ui.Output(color.Red("\t%s Partition:%d Leader:%d Replicas:%+v Isr:%+v Offset:%d Num:%d",
					topic,
					partitionID, leader.ID(), replicas, isr,
					latestOffset, latestOffset-oldestOffset))
			}
		}
	}
}

func (*UnderReplicated) Synopsis() string {
	return "Display under-replicated partitions"
}

func (this *UnderReplicated) Help() string {
	help := fmt.Sprintf(`
Usage: %s underreplicated [zone ...]

	Display under-replicated partitions
`, this.Cmd)
	return strings.TrimSpace(help)
}
<|endoftext|>