<commit_before>package udp

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"net/url"
	"strconv"

	"github.com/mdlayher/goat/goat/common"
)

// compactPeer represents a compact peer response with IP and port
type compactPeer struct {
	IP   string
	Port uint16
}

// AnnounceRequest represents a tracker announce in the UDP format
type AnnounceRequest struct {
	ConnID     uint64
	Action     uint32
	TransID    uint32
	InfoHash   []byte
	PeerID     []byte
	Downloaded uint64
	Left       uint64
	Uploaded   uint64
	Event      uint32
	IP         uint32
	Key        uint32
	Numwant    uint32
	Port       uint16
}

// UnmarshalBinary creates a AnnounceRequest from a packed byte array
func (u *AnnounceRequest) UnmarshalBinary(buf []byte) (err error) {
	// Set up recovery function to catch a panic as an error
	// This will run if we attempt to access an out of bounds index
	defer func() {
		if r := recover(); r != nil {
			err = errors.New("failed to create AnnounceRequest from bytes")
		}
	}()

	// ConnID (uint64)
	u.ConnID = binary.BigEndian.Uint64(buf[0:8])

	// Action (uint32) (Announce = 1)
	u.Action = binary.BigEndian.Uint32(buf[8:12])
	if u.Action != uint32(1) {
		return fmt.Errorf("invalid action '%d' for AnnounceRequest", u.Action)
	}

	// TransID (uint32)
	u.TransID = binary.BigEndian.Uint32(buf[12:16])

	// InfoHash (20 bytes)
	u.InfoHash = buf[16:36]
	if len(u.InfoHash) != 20 {
		return errors.New("info_hash must be exactly 20 bytes")
	}

	// PeerID (20 bytes)
	u.PeerID = buf[36:56]
	if len(u.PeerID) != 20 {
		return errors.New("peer_id must be exactly 20 bytes")
	}

	// Downloaded (uint64)
	u.Downloaded = binary.BigEndian.Uint64(buf[56:64])

	// Left (uint64)
	u.Left = binary.BigEndian.Uint64(buf[64:72])

	// Uploaded (uint64)
	u.Uploaded = binary.BigEndian.Uint64(buf[72:80])

	// Event (uint32)
	u.Event = binary.BigEndian.Uint32(buf[80:84])

	// IP (uint32)
	u.IP = binary.BigEndian.Uint32(buf[84:88])

	// Key (uint32)
	u.Key = binary.BigEndian.Uint32(buf[88:92])

	// Numwant (uint32)
	numwant := binary.BigEndian.Uint32(buf[92:96])
	// If numwant is uint32 max, use protocol default of 50
	if numwant == uint32(4294967295) {
		numwant = 50
	}
	u.Numwant = numwant

	// Port (uint16)
	u.Port = binary.BigEndian.Uint16(buf[96:98])

	return nil
}

// MarshalBinary creates a packed byte array from a AnnounceRequest
func (u AnnounceRequest) MarshalBinary() ([]byte, error) {
	res := bytes.NewBuffer(make([]byte, 0))

	// ConnID (uint64)
	if err := binary.Write(res, binary.BigEndian, u.ConnID); err != nil {
		return nil, err
	}

	// Action (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Action); err != nil {
		return nil, err
	}

	// TransID (uint32)
	if err := binary.Write(res, binary.BigEndian, u.TransID); err != nil {
		return nil, err
	}

	// InfoHash (20 bytes)
	if len(u.InfoHash) != 20 {
		return nil, errors.New("info_hash must be exactly 20 bytes")
	}

	if err := binary.Write(res, binary.BigEndian, u.InfoHash); err != nil {
		return nil, err
	}

	// PeerID (20 bytes)
	if len(u.PeerID) != 20 {
		return nil, errors.New("peer_id must be exactly 20 bytes")
	}

	if err := binary.Write(res, binary.BigEndian, u.PeerID); err != nil {
		return nil, err
	}

	// Downloaded (uint64)
	if err := binary.Write(res, binary.BigEndian, u.Downloaded); err != nil {
		return nil, err
	}

	// Left (uint64)
	if err := binary.Write(res, binary.BigEndian, u.Left); err != nil {
		return nil, err
	}

	// Uploaded (uint64)
	if err := binary.Write(res, binary.BigEndian, u.Uploaded); err != nil {
		return nil, err
	}

	// Event (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Event); err != nil {
		return nil, err
	}

	// IP (uint32)
	if err := binary.Write(res, binary.BigEndian, u.IP); err != nil {
		return nil, err
	}

	// Key (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Key); err != nil {
		return nil, err
	}

	// Numwant (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Numwant); err != nil {
		return nil, err
	}

	// Port (uint16)
	if err := binary.Write(res, binary.BigEndian, u.Port); err != nil {
		return nil, err
	}

	return res.Bytes(), nil
}

// ToValues creates a url.Values struct from a AnnounceRequest
func (u AnnounceRequest) ToValues() url.Values {
	// Initialize query map
	query := url.Values{}
	query.Set("udp", "1")

	// Copy all fields into query map
	query.Set("info_hash", string(u.InfoHash))

	// Integer fields
	query.Set("downloaded", strconv.FormatUint(u.Downloaded, 10))
	query.Set("left", strconv.FormatUint(u.Left, 10))
	query.Set("uploaded", strconv.FormatUint(u.Uploaded, 10))

	// Event, converted to actual string
	switch u.Event {
	case 0:
		query.Set("event", "")
	case 1:
		query.Set("event", "completed")
	case 2:
		query.Set("event", "started")
	case 3:
		query.Set("event", "stopped")
	}

	// IP
	query.Set("ip", strconv.FormatUint(uint64(u.IP), 10))

	// Key
	query.Set("key", strconv.FormatUint(uint64(u.Key), 10))

	// Numwant
	query.Set("numwant", strconv.FormatUint(uint64(u.Numwant), 10))

	// Port
	query.Set("port", strconv.FormatUint(uint64(u.Port), 10))

	// Return final query map
	return query
}

// AnnounceResponse represents a tracker announce response in the UDP format
type AnnounceResponse struct {
	Action   uint32
	TransID  uint32
	Interval uint32
	Leechers uint32
	Seeders  uint32
	PeerList []compactPeer
}

// UnmarshalBinary creates a AnnounceResponse from a packed byte array
func (u *AnnounceResponse) UnmarshalBinary(buf []byte) (err error) {
	// Set up recovery function to catch a panic as an error
	// This will run if we attempt to access an out of bounds index
	defer func() {
		if r := recover(); r != nil {
			err = errors.New("failed to create AnnounceResponse from bytes")
		}
	}()

	// Action (uint32) (Announce = 1)
	u.Action = binary.BigEndian.Uint32(buf[0:4])
	if u.Action != uint32(1) {
		return fmt.Errorf("invalid action '%d' for AnnounceResponse", u.Action)
	}

	// Transaction ID
	u.TransID = binary.BigEndian.Uint32(buf[4:8])

	// Interval
	u.Interval = binary.BigEndian.Uint32(buf[8:12])

	// Leechers
	u.Leechers = binary.BigEndian.Uint32(buf[12:16])

	// Seeders
	u.Seeders = binary.BigEndian.Uint32(buf[16:20])

	// Peer List
	u.PeerList = make([]compactPeer, 0)

	// Iterate peers buffer
	i := 20
	for {
		// Validate that we are not seeking beyond buffer
		if i >= len(buf) {
			break
		}

		// Get peer IP and port
		ip, port := common.B2IP(buf[i : i+6])

		// Create compact peer
		peer := compactPeer{
			IP:   ip,
			Port: port,
		}

		// Append peer
		u.PeerList = append(u.PeerList[:], peer)
		i += 6
	}

	return nil
}

// MarshalBinary creates a packed byte array from a AnnounceResponse
func (u AnnounceResponse) MarshalBinary() ([]byte, error) {
	res := bytes.NewBuffer(make([]byte, 0))

	// Action (uint32, must be 1 for announce)
	if u.Action != uint32(1) {
		return nil, fmt.Errorf("invalid action '%d' for AnnounceResponse", u.Action)

	}

	if err := binary.Write(res, binary.BigEndian, u.Action); err != nil {
		return nil, err
	}

	// TransID (uint32)
	if err := binary.Write(res, binary.BigEndian, u.TransID); err != nil {
		return nil, err
	}

	// Interval (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Interval); err != nil {
		return nil, err
	}

	// Leechers (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Leechers); err != nil {
		return nil, err
	}

	// Seeders (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Seeders); err != nil {
		return nil, err
	}

	// PeerList, []compactPeer, iterated and compressed to compact format
	for _, peer := range u.PeerList {
		// Compact and write
		if err := binary.Write(res, binary.BigEndian, common.IP2B(peer.IP, peer.Port)); err != nil {
			return nil, err
		}
	}

	return res.Bytes(), nil
}
<commit_msg>Check for bad action on AnnounceRequest.MarshalBinary()<commit_after>package udp

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"net/url"
	"strconv"

	"github.com/mdlayher/goat/goat/common"
)

// compactPeer represents a compact peer response with IP and port
type compactPeer struct {
	IP   string
	Port uint16
}

// AnnounceRequest represents a tracker announce in the UDP format
type AnnounceRequest struct {
	ConnID     uint64
	Action     uint32
	TransID    uint32
	InfoHash   []byte
	PeerID     []byte
	Downloaded uint64
	Left       uint64
	Uploaded   uint64
	Event      uint32
	IP         uint32
	Key        uint32
	Numwant    uint32
	Port       uint16
}

// UnmarshalBinary creates a AnnounceRequest from a packed byte array
func (u *AnnounceRequest) UnmarshalBinary(buf []byte) (err error) {
	// Set up recovery function to catch a panic as an error
	// This will run if we attempt to access an out of bounds index
	defer func() {
		if r := recover(); r != nil {
			err = errors.New("failed to create AnnounceRequest from bytes")
		}
	}()

	// ConnID (uint64)
	u.ConnID = binary.BigEndian.Uint64(buf[0:8])

	// Action (uint32) (Announce = 1)
	u.Action = binary.BigEndian.Uint32(buf[8:12])
	if u.Action != uint32(1) {
		return fmt.Errorf("invalid action '%d' for AnnounceRequest", u.Action)
	}

	// TransID (uint32)
	u.TransID = binary.BigEndian.Uint32(buf[12:16])

	// InfoHash (20 bytes)
	u.InfoHash = buf[16:36]
	if len(u.InfoHash) != 20 {
		return errors.New("info_hash must be exactly 20 bytes")
	}

	// PeerID (20 bytes)
	u.PeerID = buf[36:56]
	if len(u.PeerID) != 20 {
		return errors.New("peer_id must be exactly 20 bytes")
	}

	// Downloaded (uint64)
	u.Downloaded = binary.BigEndian.Uint64(buf[56:64])

	// Left (uint64)
	u.Left = binary.BigEndian.Uint64(buf[64:72])

	// Uploaded (uint64)
	u.Uploaded = binary.BigEndian.Uint64(buf[72:80])

	// Event (uint32)
	u.Event = binary.BigEndian.Uint32(buf[80:84])

	// IP (uint32)
	u.IP = binary.BigEndian.Uint32(buf[84:88])

	// Key (uint32)
	u.Key = binary.BigEndian.Uint32(buf[88:92])

	// Numwant (uint32)
	numwant := binary.BigEndian.Uint32(buf[92:96])
	// If numwant is uint32 max, use protocol default of 50
	if numwant == uint32(4294967295) {
		numwant = 50
	}
	u.Numwant = numwant

	// Port (uint16)
	u.Port = binary.BigEndian.Uint16(buf[96:98])

	return nil
}

// MarshalBinary creates a packed byte array from a AnnounceRequest
func (u AnnounceRequest) MarshalBinary() ([]byte, error) {
	res := bytes.NewBuffer(make([]byte, 0))

	// ConnID (uint64)
	if err := binary.Write(res, binary.BigEndian, u.ConnID); err != nil {
		return nil, err
	}

	// Action (uint32)
	if u.Action != uint32(1) {
		return nil, fmt.Errorf("invalid action '%d' for AnnounceRequest", u.Action)
	}

	if err := binary.Write(res, binary.BigEndian, u.Action); err != nil {
		return nil, err
	}

	// TransID (uint32)
	if err := binary.Write(res, binary.BigEndian, u.TransID); err != nil {
		return nil, err
	}

	// InfoHash (20 bytes)
	if len(u.InfoHash) != 20 {
		return nil, errors.New("info_hash must be exactly 20 bytes")
	}

	if err := binary.Write(res, binary.BigEndian, u.InfoHash); err != nil {
		return nil, err
	}

	// PeerID (20 bytes)
	if len(u.PeerID) != 20 {
		return nil, errors.New("peer_id must be exactly 20 bytes")
	}

	if err := binary.Write(res, binary.BigEndian, u.PeerID); err != nil {
		return nil, err
	}

	// Downloaded (uint64)
	if err := binary.Write(res, binary.BigEndian, u.Downloaded); err != nil {
		return nil, err
	}

	// Left (uint64)
	if err := binary.Write(res, binary.BigEndian, u.Left); err != nil {
		return nil, err
	}

	// Uploaded (uint64)
	if err := binary.Write(res, binary.BigEndian, u.Uploaded); err != nil {
		return nil, err
	}

	// Event (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Event); err != nil {
		return nil, err
	}

	// IP (uint32)
	if err := binary.Write(res, binary.BigEndian, u.IP); err != nil {
		return nil, err
	}

	// Key (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Key); err != nil {
		return nil, err
	}

	// Numwant (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Numwant); err != nil {
		return nil, err
	}

	// Port (uint16)
	if err := binary.Write(res, binary.BigEndian, u.Port); err != nil {
		return nil, err
	}

	return res.Bytes(), nil
}

// ToValues creates a url.Values struct from a AnnounceRequest
func (u AnnounceRequest) ToValues() url.Values {
	// Initialize query map
	query := url.Values{}
	query.Set("udp", "1")

	// Copy all fields into query map
	query.Set("info_hash", string(u.InfoHash))

	// Integer fields
	query.Set("downloaded", strconv.FormatUint(u.Downloaded, 10))
	query.Set("left", strconv.FormatUint(u.Left, 10))
	query.Set("uploaded", strconv.FormatUint(u.Uploaded, 10))

	// Event, converted to actual string
	switch u.Event {
	case 0:
		query.Set("event", "")
	case 1:
		query.Set("event", "completed")
	case 2:
		query.Set("event", "started")
	case 3:
		query.Set("event", "stopped")
	}

	// IP
	query.Set("ip", strconv.FormatUint(uint64(u.IP), 10))

	// Key
	query.Set("key", strconv.FormatUint(uint64(u.Key), 10))

	// Numwant
	query.Set("numwant", strconv.FormatUint(uint64(u.Numwant), 10))

	// Port
	query.Set("port", strconv.FormatUint(uint64(u.Port), 10))

	// Return final query map
	return query
}

// AnnounceResponse represents a tracker announce response in the UDP format
type AnnounceResponse struct {
	Action   uint32
	TransID  uint32
	Interval uint32
	Leechers uint32
	Seeders  uint32
	PeerList []compactPeer
}

// UnmarshalBinary creates a AnnounceResponse from a packed byte array
func (u *AnnounceResponse) UnmarshalBinary(buf []byte) (err error) {
	// Set up recovery function to catch a panic as an error
	// This will run if we attempt to access an out of bounds index
	defer func() {
		if r := recover(); r != nil {
			err = errors.New("failed to create AnnounceResponse from bytes")
		}
	}()

	// Action (uint32) (Announce = 1)
	u.Action = binary.BigEndian.Uint32(buf[0:4])
	if u.Action != uint32(1) {
		return fmt.Errorf("invalid action '%d' for AnnounceResponse", u.Action)
	}

	// Transaction ID
	u.TransID = binary.BigEndian.Uint32(buf[4:8])

	// Interval
	u.Interval = binary.BigEndian.Uint32(buf[8:12])

	// Leechers
	u.Leechers = binary.BigEndian.Uint32(buf[12:16])

	// Seeders
	u.Seeders = binary.BigEndian.Uint32(buf[16:20])

	// Peer List
	u.PeerList = make([]compactPeer, 0)

	// Iterate peers buffer
	i := 20
	for {
		// Validate that we are not seeking beyond buffer
		if i >= len(buf) {
			break
		}

		// Get peer IP and port
		ip, port := common.B2IP(buf[i : i+6])

		// Create compact peer
		peer := compactPeer{
			IP:   ip,
			Port: port,
		}

		// Append peer
		u.PeerList = append(u.PeerList[:], peer)
		i += 6
	}

	return nil
}

// MarshalBinary creates a packed byte array from a AnnounceResponse
func (u AnnounceResponse) MarshalBinary() ([]byte, error) {
	res := bytes.NewBuffer(make([]byte, 0))

	// Action (uint32, must be 1 for announce)
	if u.Action != uint32(1) {
		return nil, fmt.Errorf("invalid action '%d' for AnnounceResponse", u.Action)

	}

	if err := binary.Write(res, binary.BigEndian, u.Action); err != nil {
		return nil, err
	}

	// TransID (uint32)
	if err := binary.Write(res, binary.BigEndian, u.TransID); err != nil {
		return nil, err
	}

	// Interval (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Interval); err != nil {
		return nil, err
	}

	// Leechers (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Leechers); err != nil {
		return nil, err
	}

	// Seeders (uint32)
	if err := binary.Write(res, binary.BigEndian, u.Seeders); err != nil {
		return nil, err
	}

	// PeerList, []compactPeer, iterated and compressed to compact format
	for _, peer := range u.PeerList {
		// Compact and write
		if err := binary.Write(res, binary.BigEndian, common.IP2B(peer.IP, peer.Port)); err != nil {
			return nil, err
		}
	}

	return res.Bytes(), nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package grpclogger

import (
	"bytes"
	"reflect"
	"testing"
	"time"

	"github.com/Thomasdezeeuw/logger"
	"github.com/kylelemons/godebug/pretty"
	"google.golang.org/grpc/grpclog"
)

// EventWriter that collects the events and errors.
type eventWriter struct {
	events []logger.Event
	errors []error
	closed bool
}

func (ew *eventWriter) Write(event logger.Event) error {
	ew.events = append(ew.events, event)
	return nil
}

func (ew *eventWriter) HandleError(err error) {
	ew.errors = append(ew.errors, err)
}

func (ew *eventWriter) Close() error {
	ew.closed = true
	return nil
}

func TestGrpcLogger(t *testing.T) {
	closedCalled := setupExitCounter()
	defer resetExitFns()
	closeFn := func() {
		*closedCalled++
	}

	var ew eventWriter
	logger.Start(&ew)

	tags := logger.Tags{"TestGrpcLogger"}
	logTime := time.Now()

	grpclog.SetLogger(CreateLogger(tags, closeFn))
	expectedEvents := callGrpcLogger(tags)

	if err := logger.Close(); err != nil {
		t.Fatal("Unexpected error closing logger: " + err.Error())
	}

	if expectedN, got := len(expectedEvents), len(ew.events); expectedN != got {
		t.Fatalf("Expected %d events, but got got %d", expectedN, got)
	}

	const margin = 100 * time.Millisecond
	for i, event := range ew.events {
		expected, got := expectedEvents[i], event

		// Can't mock time in the logger package, so we have a truncate it.
		if !got.Timestamp.Truncate(margin).Equal(logTime.Truncate(margin)) {
			diff := pretty.Compare(got.Timestamp.Format(time.RFC3339Nano),
				logTime.Format(time.RFC3339Nano))
			t.Errorf("Expected and actual event #%d timestamps don't match\n%s",
				i, diff)
			continue
		}

		// Now the timestamp is tested, make sure we don't fall over it later on.
		got.Timestamp = expected.Timestamp

		if expected.Type == logger.FatalEvent {
			// Sortof test the stack trace, best we can do.
			stackTrace := got.Data.([]byte)
			if !bytes.HasPrefix(stackTrace, []byte("goroutine")) {
				t.Errorf("Expected a stack trace as data for a Fatal event, but got %s ",
					string(stackTrace))
			}
			got.Data = nil
		}

		if !reflect.DeepEqual(expected, got) {
			diff := pretty.Compare(got, expected)
			t.Errorf("Expected and actual #%d event don't match\n%s", i, diff)
		}
	}

	if *closedCalled != 6 {
		t.Fatalf("Expected the exit and close function to be called three times, but got %d",
			*closedCalled/2)
	}
}

// Make calls to the grpclog package and returns the expected events.
func callGrpcLogger(tags logger.Tags) (expected []logger.Event) {
	grpclog.Print("Error message")
	grpclog.Printf("Error %s message", "formatted")
	grpclog.Println("Error message")
	grpclog.Fatal("Fatal message")
	grpclog.Fatalf("Fatal %s message", "formatted")
	grpclog.Fatalln("Fatal message")

	return []logger.Event{
		{Type: logger.ErrorEvent, Tags: tags, Message: "Error message"},
		{Type: logger.ErrorEvent, Tags: tags, Message: "Error formatted message"},
		{Type: logger.ErrorEvent, Tags: tags, Message: "Error message"},
		{Type: logger.FatalEvent, Tags: tags, Message: "Fatal message"},
		{Type: logger.FatalEvent, Tags: tags, Message: "Fatal formatted message"},
		{Type: logger.FatalEvent, Tags: tags, Message: "Fatal message"},
	}
}

func TestExit(t *testing.T) {
	defer resetExitFns()

	var exitCode int
	var closedCalled bool
	osExit = func(n int) {
		exitCode = n
	}
	closeFn := func() {
		closedCalled = true
	}

	exit(closeFn)

	if !closedCalled {
		t.Fatal("Close function not called")
	} else if exitCode != 1 {
		t.Fatalf("Expceted exit to be called with 1, but got %d", exitCode)
	}
}

var (
	oldExit   = exit
	oldOSExit = osExit
)

func setupExitCounter() (counter *int) {
	var cnt int
	exit = func(closeFn func()) {
		closeFn()
		cnt++
	}
	return &cnt
}

func resetExitFns() {
	exit = oldExit
	osExit = oldOSExit
}
<commit_msg>grpclogger: move comparing events into it's own function<commit_after>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package grpclogger

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"
	"time"

	"github.com/Thomasdezeeuw/logger"
	"github.com/kylelemons/godebug/pretty"
	"google.golang.org/grpc/grpclog"
)

const timeMargin = 100 * time.Millisecond

// EventWriter that collects the events and errors.
type eventWriter struct {
	events []logger.Event
	errors []error
	closed bool
}

func (ew *eventWriter) Write(event logger.Event) error {
	ew.events = append(ew.events, event)
	return nil
}

func (ew *eventWriter) HandleError(err error) {
	ew.errors = append(ew.errors, err)
}

func (ew *eventWriter) Close() error {
	ew.closed = true
	return nil
}

func TestGrpcLogger(t *testing.T) {
	closedCalled := setupExitCounter()
	defer resetExitFns()
	closeFn := func() {
		*closedCalled++
	}

	var ew eventWriter
	logger.Start(&ew)

	tags := logger.Tags{"TestGrpcLogger"}
	logTime := time.Now()

	grpclog.SetLogger(CreateLogger(tags, closeFn))
	expectedEvents := callGrpcLogger(tags)

	if err := logger.Close(); err != nil {
		t.Fatal("Unexpected error closing logger: " + err.Error())
	}

	if expectedN, got := len(expectedEvents), len(ew.events); expectedN != got {
		t.Fatalf("Expected %d events, but got got %d", expectedN, got)
	}

	for i, event := range ew.events {
		expected, got := expectedEvents[i], event
		expected.Timestamp = logTime

		if err := compareEvents(i, expected, got); err != nil {
			t.Error(err)
		}
	}

	if *closedCalled != 6 {
		t.Fatalf("Expected the exit and close function to be called three times, but got %d",
			*closedCalled/2)
	}
}

// Make calls to the grpclog package and returns the expected events.
func callGrpcLogger(tags logger.Tags) (expected []logger.Event) {
	grpclog.Print("Error message")
	grpclog.Printf("Error %s message", "formatted")
	grpclog.Println("Error message")
	grpclog.Fatal("Fatal message")
	grpclog.Fatalf("Fatal %s message", "formatted")
	grpclog.Fatalln("Fatal message")

	return []logger.Event{
		{Type: logger.ErrorEvent, Tags: tags, Message: "Error message"},
		{Type: logger.ErrorEvent, Tags: tags, Message: "Error formatted message"},
		{Type: logger.ErrorEvent, Tags: tags, Message: "Error message"},
		{Type: logger.FatalEvent, Tags: tags, Message: "Fatal message"},
		{Type: logger.FatalEvent, Tags: tags, Message: "Fatal formatted message"},
		{Type: logger.FatalEvent, Tags: tags, Message: "Fatal message"},
	}
}

func compareEvents(i int, expected, got logger.Event) error {
	// Can't mock time in the logger package, so we have a truncate it.
	if !got.Timestamp.Truncate(timeMargin).Equal(expected.Timestamp.Truncate(timeMargin)) {
		diff := pretty.Compare(got.Timestamp.Format(time.RFC3339Nano),
			expected.Timestamp.Format(time.RFC3339Nano))
		return fmt.Errorf("Expected and actual event #%d timestamps don't match\n%s",
			i, diff)
	}

	// Now the timestamp is tested, make sure we don't fall over it later on.
	got.Timestamp = expected.Timestamp

	if expected.Type == logger.FatalEvent {
		// Sortof test the stack trace, best we can do.
		stackTrace := got.Data.([]byte)
		if !bytes.HasPrefix(stackTrace, []byte("goroutine")) {
			return fmt.Errorf("Expected a stack trace as data for a Fatal event, but got %s ",
				string(stackTrace))
		}
		got.Data = nil
	}

	if !reflect.DeepEqual(expected, got) {
		diff := pretty.Compare(got, expected)
		return fmt.Errorf("Expected and actual #%d event don't match\n%s", i, diff)
	}

	return nil
}

func TestExit(t *testing.T) {
	defer resetExitFns()

	var exitCode int
	var closedCalled bool
	osExit = func(n int) {
		exitCode = n
	}
	closeFn := func() {
		closedCalled = true
	}

	exit(closeFn)

	if !closedCalled {
		t.Fatal("Close function not called")
	} else if exitCode != 1 {
		t.Fatalf("Expceted exit to be called with 1, but got %d", exitCode)
	}
}

var (
	oldExit   = exit
	oldOSExit = osExit
)

func setupExitCounter() (counter *int) {
	var cnt int
	exit = func(closeFn func()) {
		closeFn()
		cnt++
	}
	return &cnt
}

func resetExitFns() {
	exit = oldExit
	osExit = oldOSExit
}
<|endoftext|>


<commit_before>// Go library for the Push Bullet REST API
// More info: https://www.pushbullet.com/api
package pbullet

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

var pushUrl string
var getUrl string

// Extra device info returned by GetDevices API call.
type DeviceInfo struct {
	Manufacturer   string `json:"manufacturer"`
	Model          string `json:"model"`
	AndroidVersion string `json:"android_version"`
	SDKVersion     string `json:"sdk_version"`
	AppVersion     string `json:"app_version"`
	Nickname       string `json:"nickname"`
}

// Device is the structure needed to push Notes/Addresses/Links/etc.
// Only need to populate Id field. Other fields are informational only.
type Device struct {
	Id      int        `json:"id"`
	DevInfo DeviceInfo `json:"extras"`
	Owner   string     `json:"owner_name"`
}

// GetDevices returns two lists, owned devices and devices that are shared.
type DeviceList struct {
	Devices       []Device `json:"devices"`
	SharedDevices []Device `json:"shared_devices"`
}

// Set the API key used for all Get and Push API calls.
func SetAPIKey(apiKey string) {
	pUrl := url.URL{}
	pUrl.Scheme = "https"
	pUrl.User = url.UserPassword(apiKey, "")
	pUrl.Host = "www.pushbullet.com"
	pUrl.Path = "/api/pushes"
	pushUrl = pUrl.String()

	gUrl := url.URL{}
	gUrl.Scheme = "https"
	gUrl.User = url.UserPassword(apiKey, "")
	gUrl.Host = "www.pushbullet.com"
	gUrl.Path = "/api/devices"
	getUrl = gUrl.String()
}

// Get devices configured on PushBullet
func GetDevices() (DeviceList, error) {
	var devList DeviceList
	resp, err := http.Get(getUrl)
	if err != nil {
		return devList, err
	}
	fmt.Println(resp)
	respBytes, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(respBytes, &devList)
	return devList, err
}

// Push a note to a device.
func (pd *Device) PushNote(title, body string) (resp *http.Response, err error) {
	pushVals := url.Values{}
	pushVals.Set("device_id", strconv.Itoa(pd.Id))
	pushVals.Set("type", "note")
	pushVals.Set("title", title)
	pushVals.Set("body", body)

	return http.PostForm(pushUrl, pushVals)
}

// Push an address to a device.
func (pd *Device) PushAddress(name, address string) (resp *http.Response, err error) {
	pushVals := url.Values{}
	pushVals.Set("device_id", strconv.Itoa(pd.Id))
	pushVals.Set("type", "note")
	pushVals.Set("name", name)
	pushVals.Set("address", address)

	return http.PostForm(pushUrl, pushVals)
}

// Push a link to a device.
func (pd *Device) PushLink(title, urlAddress string) (resp *http.Response, err error) {
	pushVals := url.Values{}
	pushVals.Set("device_id", strconv.Itoa(pd.Id))
	pushVals.Set("type", "note")
	pushVals.Set("title", title)
	pushVals.Set("url", urlAddress)

	return http.PostForm(pushUrl, pushVals)
}
<commit_msg>Update pbullet.go<commit_after>// Go library for the Push Bullet REST API
// More info: https://www.pushbullet.com/api
package pbullet

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

var pushUrl string
var getUrl string

// Extra device info returned by GetDevices API call.
type DeviceInfo struct {
	Manufacturer   string `json:"manufacturer"`
	Model          string `json:"model"`
	AndroidVersion string `json:"android_version"`
	SDKVersion     string `json:"sdk_version"`
	AppVersion     string `json:"app_version"`
	Nickname       string `json:"nickname"`
}

// Device is the structure needed to push Notes/Addresses/Links/etc.
// Only need to populate Id field. Other fields are informational only.
type Device struct {
	Id      int        `json:"id"`
	DevInfo DeviceInfo `json:"extras"`
	Owner   string     `json:"owner_name"`
}

// GetDevices returns two lists, owned devices and devices that are shared.
type DeviceList struct {
	Devices       []Device `json:"devices"`
	SharedDevices []Device `json:"shared_devices"`
}

// Set the API key used for all Get and Push API calls.
func SetAPIKey(apiKey string) {
	pUrl := url.URL{}
	pUrl.Scheme = "https"
	pUrl.User = url.UserPassword(apiKey, "")
	pUrl.Host = "api.pushbullet.com"
	pUrl.Path = "/api/pushes"
	pushUrl = pUrl.String()

	gUrl := url.URL{}
	gUrl.Scheme = "https"
	gUrl.User = url.UserPassword(apiKey, "")
	gUrl.Host = "api.pushbullet.com"
	gUrl.Path = "/api/devices"
	getUrl = gUrl.String()
}

// Get devices configured on PushBullet
func GetDevices() (DeviceList, error) {
	var devList DeviceList
	resp, err := http.Get(getUrl)
	if err != nil {
		return devList, err
	}
	fmt.Println(resp)
	respBytes, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(respBytes, &devList)
	return devList, err
}

// Push a note to a device.
func (pd *Device) PushNote(title, body string) (resp *http.Response, err error) {
	pushVals := url.Values{}
	pushVals.Set("device_id", strconv.Itoa(pd.Id))
	pushVals.Set("type", "note")
	pushVals.Set("title", title)
	pushVals.Set("body", body)

	return http.PostForm(pushUrl, pushVals)
}

// Push an address to a device.
func (pd *Device) PushAddress(name, address string) (resp *http.Response, err error) {
	pushVals := url.Values{}
	pushVals.Set("device_id", strconv.Itoa(pd.Id))
	pushVals.Set("type", "note")
	pushVals.Set("name", name)
	pushVals.Set("address", address)

	return http.PostForm(pushUrl, pushVals)
}

// Push a link to a device.
func (pd *Device) PushLink(title, urlAddress string) (resp *http.Response, err error) {
	pushVals := url.Values{}
	pushVals.Set("device_id", strconv.Itoa(pd.Id))
	pushVals.Set("type", "note")
	pushVals.Set("title", title)
	pushVals.Set("url", urlAddress)

	return http.PostForm(pushUrl, pushVals)
}
<|endoftext|>


<commit_before>// Package permute provides a function, NextPermutation, that generates permutations of any collection that satisfies sort.Interface.
package permute

import "sort"

// NextPermutation generates the next permutation of the
// sortable collection x in lexical order.  It returns false
// if the permutations are exhausted.
//
// Knuth, Donald (2011), "Section 7.2.1.2: Generating All Permutations",
// The Art of Computer Programming, volume 4A.
//
// The test is on http://play.golang.org/p/ljft9xhOEn
func NextPermutation(x sort.Interface) bool {
	n := x.Len() - 1
	if n < 1 {
		return false
	}
	j := n - 1
	for ; !x.Less(j, j+1); j-- {
		if j == 0 {
			return false
		}
	}
	l := n
	for !x.Less(j, l) {
		l--
	}
	x.Swap(j, l)
	for k, l := j+1, n; k < l; {
		x.Swap(k, l)
		k++
		l--
	}
	return true
}
<commit_msg>Removed stutter.<commit_after>// Package permute provides a function that generates permutations of any collection that satisfies sort.Interface.
package permute

import "sort"

// NextPermutation generates the next permutation of the
// sortable collection x in lexical order.  It returns false
// if the permutations are exhausted.
//
// Knuth, Donald (2011), "Section 7.2.1.2: Generating All Permutations",
// The Art of Computer Programming, volume 4A.
//
// The test is on http://play.golang.org/p/ljft9xhOEn
func Next(x sort.Interface) bool {
	n := x.Len() - 1
	if n < 1 {
		return false
	}
	j := n - 1
	for ; !x.Less(j, j+1); j-- {
		if j == 0 {
			return false
		}
	}
	l := n
	for !x.Less(j, l) {
		l--
	}
	x.Swap(j, l)
	for k, l := j+1, n; k < l; {
		x.Swap(k, l)
		k++
		l--
	}
	return true
}
<|endoftext|>


<commit_before>package fuzzy

import (
	"fmt"
	"sort"
	"testing"
)

var fuzzyTests = []struct {
	source   string
	target string
	wanted   bool
	rank     int
}{
	{"twl", "cartwheel", true, 6},
	{"cart", "cartwheel", true, 5},
	{"cw", "cartwheel", true, 7},
	{"ee", "cartwheel", true, 7},
	{"art", "cartwheel", true, 6},
	{"eeel", "cartwheel", false, -1},
	{"dog", "cartwheel", false, -1},
	{"ёлка", "ёлочка", true, 2},
	{"ветер", "ёлочка", false, -1},
	{"中国", "中华人民共和国", true, 5},
	{"日本", "中华人民共和国", false, -1},
}

func TestFuzzyMatch(t *testing.T) {
	for _, val := range fuzzyTests {
		match := Match(val.source, val.target)
		if match != val.wanted {
			t.Errorf("%s in %s expected match to be %t, got %t",
				val.source, val.target, val.wanted, match)
		}
	}
}

func TestFuzzyFind(t *testing.T) {
	target := []string{"cartwheel", "foobar", "wheel", "baz"}
	wanted := []string{"cartwheel", "wheel"}

	matches := Find("whl", target)

	if len(matches) != len(wanted) {
		t.Errorf("expected %s, got %s", wanted, matches)
	}

	for i := range wanted {
		if wanted[i] != matches[i] {
			t.Errorf("expected %s, got %s", wanted, matches)
		}
	}
}

func TestRankMatch(t *testing.T) {
	for _, val := range fuzzyTests {
		rank := RankMatch(val.source, val.target)
		if rank != val.rank {
			t.Errorf("expected ranking %d, got %d for %s in %s", val.rank, rank, val.source, val.target)
		}
	}
}

func TestRankFind(t *testing.T) {
	target := []string{"cartwheel", "foobar", "wheel", "baz"}
	wanted := []Rank{
		{"whl", "cartwheel", 6},
		{"whl", "wheel", 2},
	}

	ranks := RankFind("whl", target)

	if len(ranks) != len(wanted) {
		t.Errorf("expected %+v, got %+v", wanted, ranks)
	}

	for i := range wanted {
		if wanted[i] != ranks[i] {
			t.Errorf("expected %+v, got %+v", wanted, ranks)
		}
	}
}

func TestSortingRanks(t *testing.T) {
	rs := ranks{{"a", "b", 1}, {"a", "cc", 2}, {"a", "a", 0}}
	wanted := ranks{rs[2], rs[0], rs[1]}

	sort.Sort(rs)

	for i := range wanted {
		if wanted[i] != rs[i] {
			t.Errorf("expected %+v, got %+v", wanted, rs)
		}
	}
}

func BenchmarkMatch(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Match("kitten", "sitting")
	}
}

func BenchmarkRankMatch(b *testing.B) {
	for i := 0; i < b.N; i++ {
		RankMatch("kitten", "sitting")
	}
}

func ExampleMatch() {
	fmt.Print(Match("twl", "cartwheel"))
	// Output: true
}

func ExampleFind() {
	fmt.Print(Find("whl", []string{"cartwheel", "foobar", "wheel", "baz"}))
	// Output: [cartwheel wheel]
}

func ExampleRankMatch() {
	fmt.Print(RankMatch("twl", "cartwheel"))
	// Output: 6
}

func ExampleRankFind() {
	fmt.Print(RankFind("whl", []string{"cartwheel", "foobar", "wheel", "baz"}))
	// Output: [{whl cartwheel 6} {whl wheel 2}]
}
<commit_msg>doc: Include field names in ExampleRankFind output<commit_after>package fuzzy

import (
	"fmt"
	"sort"
	"testing"
)

var fuzzyTests = []struct {
	source string
	target string
	wanted bool
	rank   int
}{
	{"twl", "cartwheel", true, 6},
	{"cart", "cartwheel", true, 5},
	{"cw", "cartwheel", true, 7},
	{"ee", "cartwheel", true, 7},
	{"art", "cartwheel", true, 6},
	{"eeel", "cartwheel", false, -1},
	{"dog", "cartwheel", false, -1},
	{"ёлка", "ёлочка", true, 2},
	{"ветер", "ёлочка", false, -1},
	{"中国", "中华人民共和国", true, 5},
	{"日本", "中华人民共和国", false, -1},
}

func TestFuzzyMatch(t *testing.T) {
	for _, val := range fuzzyTests {
		match := Match(val.source, val.target)
		if match != val.wanted {
			t.Errorf("%s in %s expected match to be %t, got %t",
				val.source, val.target, val.wanted, match)
		}
	}
}

func TestFuzzyFind(t *testing.T) {
	target := []string{"cartwheel", "foobar", "wheel", "baz"}
	wanted := []string{"cartwheel", "wheel"}

	matches := Find("whl", target)

	if len(matches) != len(wanted) {
		t.Errorf("expected %s, got %s", wanted, matches)
	}

	for i := range wanted {
		if wanted[i] != matches[i] {
			t.Errorf("expected %s, got %s", wanted, matches)
		}
	}
}

func TestRankMatch(t *testing.T) {
	for _, val := range fuzzyTests {
		rank := RankMatch(val.source, val.target)
		if rank != val.rank {
			t.Errorf("expected ranking %d, got %d for %s in %s", val.rank, rank, val.source, val.target)
		}
	}
}

func TestRankFind(t *testing.T) {
	target := []string{"cartwheel", "foobar", "wheel", "baz"}
	wanted := []Rank{
		{"whl", "cartwheel", 6},
		{"whl", "wheel", 2},
	}

	ranks := RankFind("whl", target)

	if len(ranks) != len(wanted) {
		t.Errorf("expected %+v, got %+v", wanted, ranks)
	}

	for i := range wanted {
		if wanted[i] != ranks[i] {
			t.Errorf("expected %+v, got %+v", wanted, ranks)
		}
	}
}

func TestSortingRanks(t *testing.T) {
	rs := ranks{{"a", "b", 1}, {"a", "cc", 2}, {"a", "a", 0}}
	wanted := ranks{rs[2], rs[0], rs[1]}

	sort.Sort(rs)

	for i := range wanted {
		if wanted[i] != rs[i] {
			t.Errorf("expected %+v, got %+v", wanted, rs)
		}
	}
}

func BenchmarkMatch(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Match("kitten", "sitting")
	}
}

func BenchmarkRankMatch(b *testing.B) {
	for i := 0; i < b.N; i++ {
		RankMatch("kitten", "sitting")
	}
}

func ExampleMatch() {
	fmt.Print(Match("twl", "cartwheel"))
	// Output: true
}

func ExampleFind() {
	fmt.Print(Find("whl", []string{"cartwheel", "foobar", "wheel", "baz"}))
	// Output: [cartwheel wheel]
}

func ExampleRankMatch() {
	fmt.Print(RankMatch("twl", "cartwheel"))
	// Output: 6
}

func ExampleRankFind() {
	fmt.Printf("%+v", RankFind("whl", []string{"cartwheel", "foobar", "wheel", "baz"}))
	// Output: [{Source:whl Target:cartwheel Distance:6} {Source:whl Target:wheel Distance:2}]
}
<|endoftext|>


<commit_before>package github

import (
	"fmt"

	"github.com/github/hub/git"
)

func LocalRepo() (repo *GitHubRepo, err error) {
	repo = &GitHubRepo{}

	_, err = git.Dir()
	if err != nil {
		err = fmt.Errorf("fatal: Not a git repository")
		return
	}

	return
}

type GitHubRepo struct {
	remotes []Remote
}

func (r *GitHubRepo) loadRemotes() error {
	if r.remotes != nil {
		return nil
	}

	remotes, err := Remotes()
	if err != nil {
		return err
	}
	r.remotes = remotes

	return nil
}

func (r *GitHubRepo) RemoteByName(name string) (*Remote, error) {
	r.loadRemotes()

	for _, remote := range r.remotes {
		if remote.Name == name {
			return &remote, nil
		}
	}

	return nil, fmt.Errorf("No git remote with name %s", name)
}

func (r *GitHubRepo) remotesForPublish(owner string) (remotes []Remote) {
	r.loadRemotes()

	if owner != "" {
		for _, remote := range r.remotes {
			p, e := remote.Project()
			if e == nil && p.Owner == owner {
				remotes = append(remotes, remote)
			}
		}
	}

	remote, err := r.RemoteByName("origin")
	if err == nil {
		remotes = append(remotes, *remote)
	}

	remote, err = r.RemoteByName("github")
	if err == nil {
		remotes = append(remotes, *remote)
	}

	remote, err = r.RemoteByName("upstream")
	if err == nil {
		remotes = append(remotes, *remote)
	}

	return
}

func (r *GitHubRepo) CurrentBranch() (branch *Branch, err error) {
	head, err := git.Head()
	if err != nil {
		err = fmt.Errorf("Aborted: not currently on any branch.")
		return
	}

	branch = &Branch{r, head}
	return
}

func (r *GitHubRepo) MasterBranch() (branch *Branch) {
	origin, e := r.RemoteByName("origin")
	var name string
	if e == nil {
		name, _ = git.BranchAtRef("refs", "remotes", origin.Name, "HEAD")
	}

	if name == "" {
		name = "refs/heads/master"
	}

	branch = &Branch{r, name}

	return
}

func (r *GitHubRepo) RemoteBranchAndProject(owner string) (branch *Branch, project *Project, err error) {
	project, err = r.MainProject()
	if err != nil {
		return
	}

	branch, err = r.CurrentBranch()
	if err != nil {
		return
	}

	branch = branch.PushTarget(owner)

	if branch != nil && branch.IsRemote() {
		remote, e := r.RemoteByName(branch.RemoteName())
		if e == nil {
			project, err = remote.Project()
			if err != nil {
				return
			}
		}
	}

	return
}

func (r *GitHubRepo) OriginRemote() (*Remote, error) {
	return r.RemoteByName("origin")
}

func (r *GitHubRepo) MainProject() (project *Project, err error) {
	origin, err := r.OriginRemote()
	if err != nil {
		err = fmt.Errorf("Aborted: the origin remote doesn't point to a GitHub repository.")

		return
	}

	project, err = origin.Project()
	if err != nil {
		err = fmt.Errorf("Aborted: the origin remote doesn't point to a GitHub repository.")
	}

	return
}

func (r *GitHubRepo) CurrentProject() (project *Project, err error) {
	project, err = r.UpstreamProject()
	if err != nil {
		project, err = r.MainProject()
	}

	return
}

func (r *GitHubRepo) UpstreamProject() (project *Project, err error) {
	currentBranch, err := r.CurrentBranch()
	if err != nil {
		return
	}

	upstream, err := currentBranch.Upstream()
	if err != nil {
		return
	}

	remote, err := r.RemoteByName(upstream.RemoteName())
	if err != nil {
		return
	}

	project, err = remote.Project()

	return
}
<commit_msg>Refactor code that orders remotes by priority<commit_after>package github

import (
	"fmt"

	"github.com/github/hub/git"
)

func LocalRepo() (repo *GitHubRepo, err error) {
	repo = &GitHubRepo{}

	_, err = git.Dir()
	if err != nil {
		err = fmt.Errorf("fatal: Not a git repository")
		return
	}

	return
}

type GitHubRepo struct {
	remotes []Remote
}

func (r *GitHubRepo) loadRemotes() error {
	if r.remotes != nil {
		return nil
	}

	remotes, err := Remotes()
	if err != nil {
		return err
	}
	r.remotes = remotes

	return nil
}

func (r *GitHubRepo) RemoteByName(name string) (*Remote, error) {
	r.loadRemotes()

	for _, remote := range r.remotes {
		if remote.Name == name {
			return &remote, nil
		}
	}

	return nil, fmt.Errorf("No git remote with name %s", name)
}

func (r *GitHubRepo) remotesForPublish(owner string) (remotes []Remote) {
	r.loadRemotes()
	remotesMap := make(map[string]Remote)

	if owner != "" {
		for _, remote := range r.remotes {
			p, e := remote.Project()
			if e == nil && p.Owner == owner {
				remotesMap[remote.Name] = remote
			}
		}
	}

	names := []string{"origin", "github", "upstream"}
	for _, name := range names {
		if _, ok := remotesMap[name]; ok {
			continue
		}

		remote, err := r.RemoteByName(name)
		if err == nil {
			remotesMap[remote.Name] = *remote
		}
	}

	for _, name := range names {
		if remote, ok := remotesMap[name]; ok {
			remotes = append(remotes, remote)
			delete(remotesMap, name)
		}
	}

	// anything other than names has higher priority
	for _, remote := range remotesMap {
		remotes = append([]Remote{remote}, remotes...)
	}

	return
}

func (r *GitHubRepo) CurrentBranch() (branch *Branch, err error) {
	head, err := git.Head()
	if err != nil {
		err = fmt.Errorf("Aborted: not currently on any branch.")
		return
	}

	branch = &Branch{r, head}
	return
}

func (r *GitHubRepo) MasterBranch() (branch *Branch) {
	origin, e := r.RemoteByName("origin")
	var name string
	if e == nil {
		name, _ = git.BranchAtRef("refs", "remotes", origin.Name, "HEAD")
	}

	if name == "" {
		name = "refs/heads/master"
	}

	branch = &Branch{r, name}

	return
}

func (r *GitHubRepo) RemoteBranchAndProject(owner string) (branch *Branch, project *Project, err error) {
	project, err = r.MainProject()
	if err != nil {
		return
	}

	branch, err = r.CurrentBranch()
	if err != nil {
		return
	}

	branch = branch.PushTarget(owner)

	if branch != nil && branch.IsRemote() {
		remote, e := r.RemoteByName(branch.RemoteName())
		if e == nil {
			project, err = remote.Project()
			if err != nil {
				return
			}
		}
	}

	return
}

func (r *GitHubRepo) OriginRemote() (*Remote, error) {
	return r.RemoteByName("origin")
}

func (r *GitHubRepo) MainProject() (project *Project, err error) {
	origin, err := r.OriginRemote()
	if err != nil {
		err = fmt.Errorf("Aborted: the origin remote doesn't point to a GitHub repository.")

		return
	}

	project, err = origin.Project()
	if err != nil {
		err = fmt.Errorf("Aborted: the origin remote doesn't point to a GitHub repository.")
	}

	return
}

func (r *GitHubRepo) CurrentProject() (project *Project, err error) {
	project, err = r.UpstreamProject()
	if err != nil {
		project, err = r.MainProject()
	}

	return
}

func (r *GitHubRepo) UpstreamProject() (project *Project, err error) {
	currentBranch, err := r.CurrentBranch()
	if err != nil {
		return
	}

	upstream, err := currentBranch.Upstream()
	if err != nil {
		return
	}

	remote, err := r.RemoteByName(upstream.RemoteName())
	if err != nil {
		return
	}

	project, err = remote.Project()

	return
}
<|endoftext|>


<commit_before>package descriptor

import (
	"fmt"
	"io/ioutil"

	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
	"google.golang.org/protobuf/encoding/protojson"
	"sigs.k8s.io/yaml"
)

func loadOpenAPIConfigFromYAML(yamlFileContents []byte, yamlSourceLogName string) (*openapiconfig.OpenAPIConfig, error) {
	jsonContents, err := yaml.YAMLToJSON(yamlFileContents)
	if err != nil {
		return nil, fmt.Errorf("failed to convert OpenAPI Configuration from YAML in '%v' to JSON: %v", yamlSourceLogName, err)
	}

	// Reject unknown fields because OpenAPIConfig is only used here
	unmarshaler := protojson.UnmarshalOptions{
		DiscardUnknown: false,
	}

	openapiConfiguration := openapiconfig.OpenAPIConfig{}
	if err := unmarshaler.Unmarshal(jsonContents, &openapiConfiguration); err != nil {
		return nil, fmt.Errorf("failed to parse gRPC API Configuration from YAML in '%v': %v", yamlSourceLogName, err)
	}

	return &openapiConfiguration, nil
}

func registerOpenAPIOptions(registry *Registry, openAPIConfig *openapiconfig.OpenAPIConfig, yamlSourceLogName string) error {
	if openAPIConfig.OpenapiOptions == nil {
		// Nothing to do
		return nil
	}

	if err := registry.RegisterOpenAPIOptions(openAPIConfig.OpenapiOptions); err != nil {
		return fmt.Errorf("failed to register option in %s: %s", yamlSourceLogName, err)
	}
	return nil
}

// LoadOpenAPIConfigFromYAML loads an  OpenAPI Configuration from the given YAML file
// and registers the OpenAPI options the given registry.
// This must be done after loading the proto file.
func (r *Registry) LoadOpenAPIConfigFromYAML(yamlFile string) error {
	yamlFileContents, err := ioutil.ReadFile(yamlFile)
	if err != nil {
		return fmt.Errorf("failed to read gRPC API Configuration description from '%v': %v", yamlFile, err)
	}

	config, err := loadOpenAPIConfigFromYAML(yamlFileContents, yamlFile)
	if err != nil {
		return err
	}

	return registerOpenAPIOptions(r, config, yamlFile)
}
<commit_msg>Correct typos in error messages from loading OpenAPI Configuration (#2636)<commit_after>package descriptor

import (
	"fmt"
	"io/ioutil"

	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
	"google.golang.org/protobuf/encoding/protojson"
	"sigs.k8s.io/yaml"
)

func loadOpenAPIConfigFromYAML(yamlFileContents []byte, yamlSourceLogName string) (*openapiconfig.OpenAPIConfig, error) {
	jsonContents, err := yaml.YAMLToJSON(yamlFileContents)
	if err != nil {
		return nil, fmt.Errorf("failed to convert OpenAPI Configuration from YAML in '%v' to JSON: %v", yamlSourceLogName, err)
	}

	// Reject unknown fields because OpenAPIConfig is only used here
	unmarshaler := protojson.UnmarshalOptions{
		DiscardUnknown: false,
	}

	openapiConfiguration := openapiconfig.OpenAPIConfig{}
	if err := unmarshaler.Unmarshal(jsonContents, &openapiConfiguration); err != nil {
		return nil, fmt.Errorf("failed to parse OpenAPI Configuration from YAML in '%v': %v", yamlSourceLogName, err)
	}

	return &openapiConfiguration, nil
}

func registerOpenAPIOptions(registry *Registry, openAPIConfig *openapiconfig.OpenAPIConfig, yamlSourceLogName string) error {
	if openAPIConfig.OpenapiOptions == nil {
		// Nothing to do
		return nil
	}

	if err := registry.RegisterOpenAPIOptions(openAPIConfig.OpenapiOptions); err != nil {
		return fmt.Errorf("failed to register option in %s: %s", yamlSourceLogName, err)
	}
	return nil
}

// LoadOpenAPIConfigFromYAML loads an  OpenAPI Configuration from the given YAML file
// and registers the OpenAPI options the given registry.
// This must be done after loading the proto file.
func (r *Registry) LoadOpenAPIConfigFromYAML(yamlFile string) error {
	yamlFileContents, err := ioutil.ReadFile(yamlFile)
	if err != nil {
		return fmt.Errorf("failed to read OpenAPI Configuration description from '%v': %v", yamlFile, err)
	}

	config, err := loadOpenAPIConfigFromYAML(yamlFileContents, yamlFile)
	if err != nil {
		return err
	}

	return registerOpenAPIOptions(r, config, yamlFile)
}
<|endoftext|>


<commit_before>package debug

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"github.com/pkg/errors"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/lunixbochs/usercorn/go/models"
	"github.com/lunixbochs/usercorn/go/models/cpu"
)

func escape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	for _, c := range p {
		if c == '#' || c == '$' || c == '}' {
			out = append(out, '}')
			out = append(out, c^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func unescape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	escaped := false
	for i, c := range p {
		if escaped {
			continue
		}
		if c == '{' && i < len(p)-1 {
			escaped = true
			out = append(out, p[i+1]^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func checksum(p []byte) []byte {
	chk := 0
	for _, c := range p {
		chk = (chk + int(c)) % 256
	}
	return []byte(fmt.Sprintf("%02x", chk))
}

func parseRange(s string) (uint64, uint64) {
	tmp := strings.Split(s, ":")
	if len(tmp) == 0 {
		tmp = []string{s}
	}
	tmp = strings.Split(tmp[len(tmp)-1], ",")
	if len(tmp) != 2 {
		return 0, 0
	}
	a, _ := strconv.ParseUint(tmp[0], 16, 0)
	b, _ := strconv.ParseUint(tmp[1], 16, 0)
	return a, b
}

type Gdbstub struct {
	instances []models.Usercorn
}

func NewGdbstub(first models.Usercorn, extra ...models.Usercorn) *Gdbstub {
	instances := append([]models.Usercorn{first}, extra...)
	for _, u := range instances {
		u.Gate().Lock()
	}
	return &Gdbstub{instances}
}

func (d *Gdbstub) Run(c net.Conn) {
	fmt.Fprintf(os.Stderr, "GDB stub connected from %s\n", c.RemoteAddr())
	(&gdbClient{
		Conn: c,
		stub: d,
		u:    d.instances[0],

		breakpoints: make(map[uint64]cpu.Hook),

		verbose: false,
	}).Run()
}

type gdbClient struct {
	net.Conn
	noAck     bool
	noAckTest bool
	stub      *Gdbstub
	u         models.Usercorn

	regData  map[int]gdbReg
	regEnums map[int]int
	regList  []int

	breakpoints map[uint64]cpu.Hook

	verbose bool
}

func (c *gdbClient) fmtaddr(addr uint64) string {
	var tmp [8]byte
	packed, _ := c.u.PackAddr(tmp[:], addr)
	return hex.EncodeToString(packed)
}

func (c *gdbClient) Send(s string) error {
	if c.verbose {
		fmt.Printf("sending %v\n", s)
	}
	data := escape([]byte(s))
	data = []byte("$" + string(data) + "#" + string(checksum(data)))
	_, err := c.Write(data)
	return errors.Wrap(err, "gdbstub socket write failed")
}

func (c *gdbClient) Wait() {
	u := c.u
	pc, _ := u.RegRead(u.Arch().PC)
	c.Send(fmt.Sprintf("T%02xpc:%s;thread:1;", 0, c.fmtaddr(pc)))
}

func (c *gdbClient) Handle(cmdb []byte) error {
	if c.verbose {
		fmt.Printf("handling %v\n", string(cmdb))
	}
	u := c.u
	if len(cmdb) == 0 {
		return nil
	}
	b, rest := cmdb[0], string(cmdb[1:])
	var cmd, args string
	if strings.Contains(rest, ":") {
		tmp := strings.SplitN(rest, ":", 2)
		cmd, args = tmp[0], tmp[1]
	} else {
		cmd = rest
	}
	switch b {
	case 'q': // query
		switch cmd {
		case "Supported":
			c.Send("PacketSize=4000;qXfer:features:read+") // ;qXfer:memory-map:read+
		case "Attached":
			c.Send("1")
		case "Symbol":
			c.Send("OK")
		case "C":
			c.Send("OK")
		case "Xfer":
			if strings.HasPrefix(args, "features:read:target.xml:") {
				a, b := parseRange(args)
				tdesc := u.Arch().GdbXml
				if a >= 0 && a < uint64(len(tdesc)) {
					if a+b > uint64(len(tdesc)) {
						b = uint64(len(tdesc)) - a
					}
					c.Send("m" + tdesc[a:a+b])
				} else {
					c.Send("l")
				}
			} else {
				if c.verbose {
					fmt.Println("unknown q Xfer:", args)
				}
			}
		case "TStatus":
			c.Send("T0")
		case "Rcmd":
			tmp := strings.SplitN(cmd, ",", 2)
			if c.verbose {
				fmt.Println("would send input:", tmp[1])
			}
			// c.Send("O" + (output + "\n").encode("hex"))
			c.Send("OK")
		default:
			if c.verbose {
				fmt.Println("unknown cmd q", cmd, args)
			}
			c.Send("")
		}
	case 'Q': // set query
		switch cmd {
		case "StartNoAckMode":
			c.noAckTest = true
		default:
			if c.verbose {
				fmt.Println("unknown cmd Q", cmd, args)
			}
			c.Send("")
		}
	case 'v': // resume
		if cmd == "Cont?" {
			c.Send("")
		}
	case 'g': // read regs
		/*
			var vals []string
			for _, v := range c.regList {
				if v > 0 {
					enum := v - 1
					r, _ := u.RegRead(enum)
					vals = append(vals, c.fmtaddr(r))
				}
			}
			c.Send(strings.Repeat("0", 8))
			// c.Send(strings.Join(vals, ""))
		*/
		// FIXME
		c.Send("00000000")
	case 'G': // write regs
		if c.verbose {
			fmt.Println("should write regs")
		}
	case 'p': // read one reg
		i, _ := strconv.ParseUint(cmd, 16, 0)
		if int(i) < len(c.regList) {
			v := c.regList[i]
			if v > 0 {
				val, _ := u.RegRead(v - 1)
				c.Send(c.fmtaddr(val))
			} else {
				c.Send("00000000")
			}
		} else {
			c.Send("")
		}
	case 'm': // read memory
		a, b := parseRange(rest)
		mem, err := u.MemRead(a, b)
		if err != nil {
			if c.verbose {
				fmt.Println("error reading mem", err)
			}
			c.Send("")
		} else {
			c.Send(hex.EncodeToString(mem))
		}
	case 'M': // write memory
		a, _ := parseRange(rest)
		data, err := hex.DecodeString(rest)
		if err != nil {
			if c.verbose {
				fmt.Println("error parsing hex", rest, err)
			}
			c.Send("")
		} else {
			err := u.MemWrite(a, data)
			if err != nil {
				if c.verbose {
					fmt.Println("error writing mem", err)
				}
			}
		}
	case 'Z': // add breakpoint
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if _, ok := c.breakpoints[addr]; ok {
			c.Send("OK")
			break
		}
		h, _ := u.HookAdd(cpu.HOOK_CODE, func(_ cpu.Cpu, addr uint64, size uint32) {
			u.Trampoline(func() error { return nil })
		}, addr, addr+1)
		c.breakpoints[addr] = h
		c.Send("OK")
	case 'z': // remove breakpoint
		// TODO: this seems to freeze gdb
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if h, ok := c.breakpoints[addr]; ok {
			u.HookDel(h)
		}
		c.Send("OK")
	case 'c': // continue
		u.Gate().UnlockStopRelock()
		c.Wait()
	case 's': // step
		first := true
		h, _ := u.HookAdd(cpu.HOOK_CODE, func(_ cpu.Cpu, addr uint64, size uint32) {
			if first {
				first = false
			} else {
				u.Trampoline(func() error { return nil })
				return
			}
		}, 1, 0)
		u.Gate().UnlockStopRelock()
		u.HookDel(h)
		c.Wait()
	case '?': // last signal
		c.Wait()
	case 'H': // do thread op
		if c.verbose {
			fmt.Println("thread op", b, cmd, args)
		}
		c.Send("OK")
	case 'D': // detach
		return errors.New("detached")
	case 'T': // thread
		c.Send("OK")
	default:
		if c.verbose {
			fmt.Printf("unknown command %c %s %s\n", b, cmd, args)
		}
	}
	return nil
}

type gdbReg struct {
	XMLName xml.Name `xml:"reg"`
	Name    string   `xml:"name,attr"`
	Bitsize int      `xml:"bitsize,attr"`
	Type    string   `xml:"type,attr"`
	Regnum  int      `xml:"regnum,attr"`
}

type gdbTarget struct {
	XMLName xml.Name `xml:"target"`
	Regs    []gdbReg `xml:"feature>reg"`
}

func (c *gdbClient) parseXml(x string) {
	regLookup := make(map[string]int)
	c.regData = make(map[int]gdbReg)
	c.regEnums = make(map[int]int)

	var target gdbTarget
	xml.Unmarshal([]byte(x), &target)
	base := 0
	for i, v := range target.Regs {
		if v.Regnum > 0 {
			base = v.Regnum - i
		}
		c.regData[base+i] = v
		regLookup[v.Name] = base + i
	}
	a := c.u.Arch()
	regNames := a.RegNames()
	max := 0
	for enum, name := range regNames {
		if i, ok := regLookup[name]; ok {
			c.regEnums[i] = enum
			if i > max {
				max = i
			}
		}
	}
	c.regList = make([]int, max+1)
	for i, v := range c.regEnums {
		c.regList[i] = v + 1
	}
}

func (c *gdbClient) Run() {
	c.parseXml(c.u.Arch().GdbXml)

	input := bufio.NewReader(c)
	var err error

	var loop sync.Mutex
	go func() {
		for {
			loop.Lock()
			b, err := input.Peek(1)
			if err != nil {
				break
			}
			// TODO: this won't interrupt pending syscalls
			if b[0] == '\x03' {
				input.Discard(1)
				c.u.Trampoline(func() error { return nil })
			}
			loop.Unlock()
			<-time.After(100 * time.Millisecond)
		}
	}()

	loop.Lock()
	for {
		// Locking in this order simplifies loop flow, and guarantees lock is unlocked each iteration.
		loop.Unlock()
		loop.Lock()
		var b, chk []byte

		b, err = input.Peek(1)
		if err != nil {
			break
		} else if b[0] == 0x03 {
			continue
		} else if b[0] == '+' || b[0] == '-' {
			// ack
			input.Discard(1)
			if c.noAckTest && b[0] == '+' {
				c.noAck = true
			}
			c.noAckTest = false
		}
		if b, err = input.ReadBytes('#'); err != nil {
			break
		}
		if chk, err = input.Peek(2); err != nil {
			break
		}
		input.Discard(2)

		data := b[1 : len(b)-1]
		if bytes.Equal(checksum(data), chk) {
			c.ack('+')
			if err = c.Handle(unescape(data)); err != nil {
				break
			}
		} else {
			c.ack('-')
		}
	}
	loop.Unlock()
	if err != nil {
		fmt.Fprintf(os.Stderr, "GDB stub error: %v\n", err)
	}
	c.Close()
}

func (c *gdbClient) ack(b byte) {
	if !c.noAck {
		c.Write([]byte{b})
	}
}
<commit_msg>gdbstub: implement vMustReplyEmpty<commit_after>package debug

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"github.com/pkg/errors"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/lunixbochs/usercorn/go/models"
	"github.com/lunixbochs/usercorn/go/models/cpu"
)

func escape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	for _, c := range p {
		if c == '#' || c == '$' || c == '}' {
			out = append(out, '}')
			out = append(out, c^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func unescape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	escaped := false
	for i, c := range p {
		if escaped {
			continue
		}
		if c == '{' && i < len(p)-1 {
			escaped = true
			out = append(out, p[i+1]^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func checksum(p []byte) []byte {
	chk := 0
	for _, c := range p {
		chk = (chk + int(c)) % 256
	}
	return []byte(fmt.Sprintf("%02x", chk))
}

func parseRange(s string) (uint64, uint64) {
	tmp := strings.Split(s, ":")
	if len(tmp) == 0 {
		tmp = []string{s}
	}
	tmp = strings.Split(tmp[len(tmp)-1], ",")
	if len(tmp) != 2 {
		return 0, 0
	}
	a, _ := strconv.ParseUint(tmp[0], 16, 0)
	b, _ := strconv.ParseUint(tmp[1], 16, 0)
	return a, b
}

type Gdbstub struct {
	instances []models.Usercorn
}

func NewGdbstub(first models.Usercorn, extra ...models.Usercorn) *Gdbstub {
	instances := append([]models.Usercorn{first}, extra...)
	for _, u := range instances {
		u.Gate().Lock()
	}
	return &Gdbstub{instances}
}

func (d *Gdbstub) Run(c net.Conn) {
	fmt.Fprintf(os.Stderr, "GDB stub connected from %s\n", c.RemoteAddr())
	(&gdbClient{
		Conn: c,
		stub: d,
		u:    d.instances[0],

		breakpoints: make(map[uint64]cpu.Hook),

		verbose: false,
	}).Run()
}

type gdbClient struct {
	net.Conn
	noAck     bool
	noAckTest bool
	stub      *Gdbstub
	u         models.Usercorn

	regData  map[int]gdbReg
	regEnums map[int]int
	regList  []int

	breakpoints map[uint64]cpu.Hook

	verbose bool
}

func (c *gdbClient) fmtaddr(addr uint64) string {
	var tmp [8]byte
	packed, _ := c.u.PackAddr(tmp[:], addr)
	return hex.EncodeToString(packed)
}

func (c *gdbClient) Send(s string) error {
	if c.verbose {
		fmt.Printf("sending %v\n", s)
	}
	data := escape([]byte(s))
	data = []byte("$" + string(data) + "#" + string(checksum(data)))
	_, err := c.Write(data)
	return errors.Wrap(err, "gdbstub socket write failed")
}

func (c *gdbClient) Wait() {
	u := c.u
	pc, _ := u.RegRead(u.Arch().PC)
	c.Send(fmt.Sprintf("T%02xpc:%s;thread:1;", 0, c.fmtaddr(pc)))
}

func (c *gdbClient) Handle(cmdb []byte) error {
	if c.verbose {
		fmt.Printf("handling %v\n", string(cmdb))
	}
	u := c.u
	if len(cmdb) == 0 {
		return nil
	}
	b, rest := cmdb[0], string(cmdb[1:])
	var cmd, args string
	if strings.Contains(rest, ":") {
		tmp := strings.SplitN(rest, ":", 2)
		cmd, args = tmp[0], tmp[1]
	} else {
		cmd = rest
	}
	switch b {
	case 'q': // query
		switch cmd {
		case "Supported":
			c.Send("PacketSize=4000;qXfer:features:read+") // ;qXfer:memory-map:read+
		case "Attached":
			c.Send("1")
		case "Symbol":
			c.Send("OK")
		case "C":
			c.Send("OK")
		case "Xfer":
			if strings.HasPrefix(args, "features:read:target.xml:") {
				a, b := parseRange(args)
				tdesc := u.Arch().GdbXml
				if a >= 0 && a < uint64(len(tdesc)) {
					if a+b > uint64(len(tdesc)) {
						b = uint64(len(tdesc)) - a
					}
					c.Send("m" + tdesc[a:a+b])
				} else {
					c.Send("l")
				}
			} else {
				if c.verbose {
					fmt.Println("unknown q Xfer:", args)
				}
			}
		case "TStatus":
			c.Send("T0")
		case "Rcmd":
			tmp := strings.SplitN(cmd, ",", 2)
			if c.verbose {
				fmt.Println("would send input:", tmp[1])
			}
			// c.Send("O" + (output + "\n").encode("hex"))
			c.Send("OK")
		default:
			if c.verbose {
				fmt.Println("unknown cmd q", cmd, args)
			}
			c.Send("")
		}
	case 'Q': // set query
		switch cmd {
		case "StartNoAckMode":
			c.noAckTest = true
		default:
			if c.verbose {
				fmt.Println("unknown cmd Q", cmd, args)
			}
			c.Send("")
		}
	case 'v': // resume
		switch cmd {
		case "MustReplyEmpty":
			c.Send("")
		case "Cont?":
			c.Send("")
		}
	case 'g': // read regs
		/*
			var vals []string
			for _, v := range c.regList {
				if v > 0 {
					enum := v - 1
					r, _ := u.RegRead(enum)
					vals = append(vals, c.fmtaddr(r))
				}
			}
			c.Send(strings.Repeat("0", 8))
			// c.Send(strings.Join(vals, ""))
		*/
		// FIXME
		c.Send("00000000")
	case 'G': // write regs
		if c.verbose {
			fmt.Println("should write regs")
		}
	case 'p': // read one reg
		i, _ := strconv.ParseUint(cmd, 16, 0)
		if int(i) < len(c.regList) {
			v := c.regList[i]
			if v > 0 {
				val, _ := u.RegRead(v - 1)
				c.Send(c.fmtaddr(val))
			} else {
				c.Send("00000000")
			}
		} else {
			c.Send("")
		}
	case 'm': // read memory
		a, b := parseRange(rest)
		mem, err := u.MemRead(a, b)
		if err != nil {
			if c.verbose {
				fmt.Println("error reading mem", err)
			}
			c.Send("")
		} else {
			c.Send(hex.EncodeToString(mem))
		}
	case 'M': // write memory
		a, _ := parseRange(rest)
		data, err := hex.DecodeString(rest)
		if err != nil {
			if c.verbose {
				fmt.Println("error parsing hex", rest, err)
			}
			c.Send("")
		} else {
			err := u.MemWrite(a, data)
			if err != nil {
				if c.verbose {
					fmt.Println("error writing mem", err)
				}
			}
		}
	case 'Z': // add breakpoint
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if _, ok := c.breakpoints[addr]; ok {
			c.Send("OK")
			break
		}
		h, _ := u.HookAdd(cpu.HOOK_CODE, func(_ cpu.Cpu, addr uint64, size uint32) {
			u.Trampoline(func() error { return nil })
		}, addr, addr+1)
		c.breakpoints[addr] = h
		c.Send("OK")
	case 'z': // remove breakpoint
		// TODO: this seems to freeze gdb
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if h, ok := c.breakpoints[addr]; ok {
			u.HookDel(h)
		}
		c.Send("OK")
	case 'c': // continue
		u.Gate().UnlockStopRelock()
		c.Wait()
	case 's': // step
		first := true
		h, _ := u.HookAdd(cpu.HOOK_CODE, func(_ cpu.Cpu, addr uint64, size uint32) {
			if first {
				first = false
			} else {
				u.Trampoline(func() error { return nil })
				return
			}
		}, 1, 0)
		u.Gate().UnlockStopRelock()
		u.HookDel(h)
		c.Wait()
	case '?': // last signal
		c.Wait()
	case 'H': // do thread op
		if c.verbose {
			fmt.Println("thread op", b, cmd, args)
		}
		c.Send("OK")
	case 'D': // detach
		return errors.New("detached")
	case 'T': // thread
		c.Send("OK")
	default:
		if c.verbose {
			fmt.Printf("unknown command %c %s %s\n", b, cmd, args)
		}
	}
	return nil
}

type gdbReg struct {
	XMLName xml.Name `xml:"reg"`
	Name    string   `xml:"name,attr"`
	Bitsize int      `xml:"bitsize,attr"`
	Type    string   `xml:"type,attr"`
	Regnum  int      `xml:"regnum,attr"`
}

type gdbTarget struct {
	XMLName xml.Name `xml:"target"`
	Regs    []gdbReg `xml:"feature>reg"`
}

func (c *gdbClient) parseXml(x string) {
	regLookup := make(map[string]int)
	c.regData = make(map[int]gdbReg)
	c.regEnums = make(map[int]int)

	var target gdbTarget
	xml.Unmarshal([]byte(x), &target)
	base := 0
	for i, v := range target.Regs {
		if v.Regnum > 0 {
			base = v.Regnum - i
		}
		c.regData[base+i] = v
		regLookup[v.Name] = base + i
	}
	a := c.u.Arch()
	regNames := a.RegNames()
	max := 0
	for enum, name := range regNames {
		if i, ok := regLookup[name]; ok {
			c.regEnums[i] = enum
			if i > max {
				max = i
			}
		}
	}
	c.regList = make([]int, max+1)
	for i, v := range c.regEnums {
		c.regList[i] = v + 1
	}
}

func (c *gdbClient) Run() {
	c.parseXml(c.u.Arch().GdbXml)

	input := bufio.NewReader(c)
	var err error

	var loop sync.Mutex
	go func() {
		for {
			loop.Lock()
			b, err := input.Peek(1)
			if err != nil {
				break
			}
			// TODO: this won't interrupt pending syscalls
			if b[0] == '\x03' {
				input.Discard(1)
				c.u.Trampoline(func() error { return nil })
			}
			loop.Unlock()
			<-time.After(100 * time.Millisecond)
		}
	}()

	loop.Lock()
	for {
		// Locking in this order simplifies loop flow, and guarantees lock is unlocked each iteration.
		loop.Unlock()
		loop.Lock()
		var b, chk []byte

		b, err = input.Peek(1)
		if err != nil {
			break
		} else if b[0] == 0x03 {
			continue
		} else if b[0] == '+' || b[0] == '-' {
			// ack
			input.Discard(1)
			if c.noAckTest && b[0] == '+' {
				c.noAck = true
			}
			c.noAckTest = false
		}
		if b, err = input.ReadBytes('#'); err != nil {
			break
		}
		if chk, err = input.Peek(2); err != nil {
			break
		}
		input.Discard(2)

		data := b[1 : len(b)-1]
		if bytes.Equal(checksum(data), chk) {
			c.ack('+')
			if err = c.Handle(unescape(data)); err != nil {
				break
			}
		} else {
			c.ack('-')
		}
	}
	loop.Unlock()
	if err != nil {
		fmt.Fprintf(os.Stderr, "GDB stub error: %v\n", err)
	}
	c.Close()
}

func (c *gdbClient) ack(b byte) {
	if !c.noAck {
		c.Write([]byte{b})
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	"golang.org/x/net/context"
)

type LoginHandler struct {
	libkb.Contextified
	*BaseHandler
}

func NewLoginHandler(xp rpc.Transporter, g *libkb.GlobalContext) *LoginHandler {
	return &LoginHandler{
		BaseHandler:  NewBaseHandler(g, xp),
		Contextified: libkb.NewContextified(g),
	}
}

func (h *LoginHandler) GetConfiguredAccounts(context context.Context, sessionID int) ([]keybase1.ConfiguredAccount, error) {
	return h.G().GetConfiguredAccounts(context)
}

func (h *LoginHandler) Logout(ctx context.Context, arg keybase1.LogoutArg) (err error) {
	defer h.G().CTraceTimed(ctx, "Logout [service RPC]", func() error { return err })()
	mctx := libkb.NewMetaContext(ctx, h.G()).WithLogTag("LOGOUT")
	eng := engine.NewLogout(libkb.LogoutOptions{Force: arg.Force,
		KeepSecrets: arg.KeepSecrets})
	return engine.RunEngine2(mctx, eng)
}

func (h *LoginHandler) Deprovision(ctx context.Context, arg keybase1.DeprovisionArg) error {
	eng := engine.NewDeprovisionEngine(h.G(), arg.Username, arg.DoRevoke, libkb.LogoutOptions{KeepSecrets: false, Force: true})
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
		SessionID: arg.SessionID,
	}
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) RecoverAccountFromEmailAddress(ctx context.Context, email string) error {
	mctx := libkb.NewMetaContext(ctx, h.G())
	res, err := mctx.G().API.Post(mctx, libkb.APIArg{
		Endpoint:    "send-reset-pw",
		SessionType: libkb.APISessionTypeNONE,
		Args: libkb.HTTPArgs{
			"email_or_username": libkb.S{Val: email},
		},
		AppStatusCodes: []int{libkb.SCOk, libkb.SCBadLoginUserNotFound},
	})
	if err != nil {
		return err
	}
	if res.AppStatus.Code == libkb.SCBadLoginUserNotFound {
		return libkb.NotFoundError{}
	}
	return nil
}

func (h *LoginHandler) PaperKey(ctx context.Context, sessionID int) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(sessionID),
		LoginUI:   h.getLoginUI(sessionID),
		SecretUI:  h.getSecretUI(sessionID, h.G()),
		SessionID: sessionID,
	}
	eng := engine.NewPaperKey(h.G())
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) PaperKeySubmit(ctx context.Context, arg keybase1.PaperKeySubmitArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SessionID: arg.SessionID,
	}
	eng := engine.NewPaperKeySubmit(h.G(), arg.PaperPhrase)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) Unlock(ctx context.Context, sessionID int) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(sessionID),
		SecretUI:  h.getSecretUI(sessionID, h.G()),
		SessionID: sessionID,
	}
	eng := engine.NewUnlock(h.G())
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) UnlockWithPassphrase(ctx context.Context, arg keybase1.UnlockWithPassphraseArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
		SessionID: arg.SessionID,
	}
	eng := engine.NewUnlockWithPassphrase(h.G(), arg.Passphrase)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) Login(ctx context.Context, arg keybase1.LoginArg) error {
	uis := libkb.UIs{
		LogUI:       h.getLogUI(arg.SessionID),
		LoginUI:     h.getLoginUI(arg.SessionID),
		ProvisionUI: h.getProvisionUI(arg.SessionID),
		SecretUI:    h.getSecretUI(arg.SessionID, h.G()),
		GPGUI:       h.getGPGUI(arg.SessionID),
		SessionID:   arg.SessionID,
	}
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	eng := engine.NewLoginWithUserSwitch(h.G(), arg.DeviceType, arg.Username, arg.ClientType, arg.DoUserSwitch)
	eng.PaperKey = arg.PaperKey
	eng.DeviceName = arg.DeviceName
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) LoginProvisionedDevice(ctx context.Context, arg keybase1.LoginProvisionedDeviceArg) error {
	eng := engine.NewLoginProvisionedDevice(h.G(), arg.Username)

	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SessionID: arg.SessionID,
	}

	if arg.NoPassphrasePrompt {
		eng.SecretStoreOnly = true
	} else {
		uis.LoginUI = h.getLoginUI(arg.SessionID)
		uis.SecretUI = h.getSecretUI(arg.SessionID, h.G())
	}

	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) LoginWithPaperKey(ctx context.Context, arg keybase1.LoginWithPaperKeyArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
		SessionID: arg.SessionID,
	}
	eng := engine.NewLoginWithPaperKey(h.G(), arg.Username)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	err := engine.RunEngine2(m, eng)
	return err
}

func (h *LoginHandler) AccountDelete(ctx context.Context, sessionID int) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(sessionID),
		SessionID: sessionID,
		SecretUI:  h.getSecretUI(sessionID, h.G()),
	}
	eng := engine.NewAccountDelete(h.G())
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) LoginOneshot(ctx context.Context, arg keybase1.LoginOneshotArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SessionID: arg.SessionID,
	}
	eng := engine.NewLoginOneshot(h.G(), arg)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) IsOnline(ctx context.Context) (bool, error) {
	mctx := libkb.NewMetaContext(ctx, h.G())

	_, err := h.G().API.Post(mctx, libkb.APIArg{Endpoint: "ping"})
	return err == nil, nil
}

func (h *LoginHandler) RecoverPassphrase(ctx context.Context, arg keybase1.RecoverPassphraseArg) error {
	uis := libkb.UIs{
		LogUI:       h.getLogUI(arg.SessionID),
		LoginUI:     h.getLoginUI(arg.SessionID),
		SecretUI:    h.getSecretUI(arg.SessionID, h.G()),
		ProvisionUI: h.getProvisionUI(arg.SessionID),
		SessionID:   arg.SessionID,
	}
	eng := engine.NewPassphraseRecover(h.G(), arg)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}
<commit_msg>modify login isOnline to use ConnectivityMonitor (#20472)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	"golang.org/x/net/context"
)

type LoginHandler struct {
	libkb.Contextified
	*BaseHandler
}

func NewLoginHandler(xp rpc.Transporter, g *libkb.GlobalContext) *LoginHandler {
	return &LoginHandler{
		BaseHandler:  NewBaseHandler(g, xp),
		Contextified: libkb.NewContextified(g),
	}
}

func (h *LoginHandler) GetConfiguredAccounts(context context.Context, sessionID int) ([]keybase1.ConfiguredAccount, error) {
	return h.G().GetConfiguredAccounts(context)
}

func (h *LoginHandler) Logout(ctx context.Context, arg keybase1.LogoutArg) (err error) {
	defer h.G().CTraceTimed(ctx, "Logout [service RPC]", func() error { return err })()
	mctx := libkb.NewMetaContext(ctx, h.G()).WithLogTag("LOGOUT")
	eng := engine.NewLogout(libkb.LogoutOptions{Force: arg.Force,
		KeepSecrets: arg.KeepSecrets})
	return engine.RunEngine2(mctx, eng)
}

func (h *LoginHandler) Deprovision(ctx context.Context, arg keybase1.DeprovisionArg) error {
	eng := engine.NewDeprovisionEngine(h.G(), arg.Username, arg.DoRevoke, libkb.LogoutOptions{KeepSecrets: false, Force: true})
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
		SessionID: arg.SessionID,
	}
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) RecoverAccountFromEmailAddress(ctx context.Context, email string) error {
	mctx := libkb.NewMetaContext(ctx, h.G())
	res, err := mctx.G().API.Post(mctx, libkb.APIArg{
		Endpoint:    "send-reset-pw",
		SessionType: libkb.APISessionTypeNONE,
		Args: libkb.HTTPArgs{
			"email_or_username": libkb.S{Val: email},
		},
		AppStatusCodes: []int{libkb.SCOk, libkb.SCBadLoginUserNotFound},
	})
	if err != nil {
		return err
	}
	if res.AppStatus.Code == libkb.SCBadLoginUserNotFound {
		return libkb.NotFoundError{}
	}
	return nil
}

func (h *LoginHandler) PaperKey(ctx context.Context, sessionID int) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(sessionID),
		LoginUI:   h.getLoginUI(sessionID),
		SecretUI:  h.getSecretUI(sessionID, h.G()),
		SessionID: sessionID,
	}
	eng := engine.NewPaperKey(h.G())
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) PaperKeySubmit(ctx context.Context, arg keybase1.PaperKeySubmitArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SessionID: arg.SessionID,
	}
	eng := engine.NewPaperKeySubmit(h.G(), arg.PaperPhrase)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) Unlock(ctx context.Context, sessionID int) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(sessionID),
		SecretUI:  h.getSecretUI(sessionID, h.G()),
		SessionID: sessionID,
	}
	eng := engine.NewUnlock(h.G())
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) UnlockWithPassphrase(ctx context.Context, arg keybase1.UnlockWithPassphraseArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
		SessionID: arg.SessionID,
	}
	eng := engine.NewUnlockWithPassphrase(h.G(), arg.Passphrase)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) Login(ctx context.Context, arg keybase1.LoginArg) error {
	uis := libkb.UIs{
		LogUI:       h.getLogUI(arg.SessionID),
		LoginUI:     h.getLoginUI(arg.SessionID),
		ProvisionUI: h.getProvisionUI(arg.SessionID),
		SecretUI:    h.getSecretUI(arg.SessionID, h.G()),
		GPGUI:       h.getGPGUI(arg.SessionID),
		SessionID:   arg.SessionID,
	}
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	eng := engine.NewLoginWithUserSwitch(h.G(), arg.DeviceType, arg.Username, arg.ClientType, arg.DoUserSwitch)
	eng.PaperKey = arg.PaperKey
	eng.DeviceName = arg.DeviceName
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) LoginProvisionedDevice(ctx context.Context, arg keybase1.LoginProvisionedDeviceArg) error {
	eng := engine.NewLoginProvisionedDevice(h.G(), arg.Username)

	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SessionID: arg.SessionID,
	}

	if arg.NoPassphrasePrompt {
		eng.SecretStoreOnly = true
	} else {
		uis.LoginUI = h.getLoginUI(arg.SessionID)
		uis.SecretUI = h.getSecretUI(arg.SessionID, h.G())
	}

	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) LoginWithPaperKey(ctx context.Context, arg keybase1.LoginWithPaperKeyArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
		SessionID: arg.SessionID,
	}
	eng := engine.NewLoginWithPaperKey(h.G(), arg.Username)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	err := engine.RunEngine2(m, eng)
	return err
}

func (h *LoginHandler) AccountDelete(ctx context.Context, sessionID int) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(sessionID),
		SessionID: sessionID,
		SecretUI:  h.getSecretUI(sessionID, h.G()),
	}
	eng := engine.NewAccountDelete(h.G())
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) LoginOneshot(ctx context.Context, arg keybase1.LoginOneshotArg) error {
	uis := libkb.UIs{
		LogUI:     h.getLogUI(arg.SessionID),
		SessionID: arg.SessionID,
	}
	eng := engine.NewLoginOneshot(h.G(), arg)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}

func (h *LoginHandler) IsOnline(ctx context.Context) (bool, error) {
	return h.G().ConnectivityMonitor.IsConnected(ctx) == libkb.ConnectivityMonitorYes, nil
}

func (h *LoginHandler) RecoverPassphrase(ctx context.Context, arg keybase1.RecoverPassphraseArg) error {
	uis := libkb.UIs{
		LogUI:       h.getLogUI(arg.SessionID),
		LoginUI:     h.getLoginUI(arg.SessionID),
		SecretUI:    h.getSecretUI(arg.SessionID, h.G()),
		ProvisionUI: h.getProvisionUI(arg.SessionID),
		SessionID:   arg.SessionID,
	}
	eng := engine.NewPassphraseRecover(h.G(), arg)
	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
	return engine.RunEngine2(m, eng)
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package xurls

import (
	"regexp"
	"testing"
)

type testCase struct {
	in   string
	want interface{}
}

func doTest(t *testing.T, name string, re *regexp.Regexp, cases []testCase) {
	for _, c := range cases {
		got := re.FindString(c.in)
		want, _ := c.want.(string)
		if got != want {
			t.Errorf(`%s.FindString("%s") got "%s", want "%s"`, name, c.in, got, want)
		}
	}
}

var constantTestCases = []testCase{
	{``, nil},
	{` `, nil},
	{`:`, nil},
	{`::`, nil},
	{`:::`, nil},
	{`::::`, nil},
	{`.`, nil},
	{`..`, nil},
	{`...`, nil},
	{`1.1`, nil},
	{`.1.`, nil},
	{`1.1.1`, nil},
	{`1:1`, nil},
	{`:1:`, nil},
	{`1:1:1`, nil},
	{`://`, nil},
	{`foo`, nil},
	{`foo:`, nil},
	{`mailto:`, nil},
	{`randomxmpp:foo`, nil},
	{`foo://`, nil},
	{`http://`, nil},
	{`http:// foo`, nil},
	{`http:// foo`, nil},
	{`:foo`, nil},
	{`://foo`, nil},
	{`foo:bar`, nil},
	{`zzz.`, nil},
	{`.zzz`, nil},
	{`zzz.zzz`, nil},
	{`/some/path`, nil},
	{`localhost`, nil},
	{`com`, nil},
	{`.com`, nil},
	{`http`, nil},

	{`http://foo`, `http://foo`},
	{`http://FOO`, `http://FOO`},
	{`http://FAÀ`, `http://FAÀ`},
	{`https://localhost`, `https://localhost`},
	{`git+https://localhost`, `git+https://localhost`},
	{`foo.bar://localhost`, `foo.bar://localhost`},
	{`foo-bar://localhost`, `foo-bar://localhost`},
	{`mailto:foo`, `mailto:foo`},
	{`MAILTO:foo`, `MAILTO:foo`},
	{`sms:123`, `sms:123`},
	{`xmpp:foo@bar`, `xmpp:foo@bar`},
	{`bitcoin:Addr23?amount=1&message=foo`, `bitcoin:Addr23?amount=1&message=foo`},
	{`http://foo.com`, `http://foo.com`},
	{`http://foo.co.uk`, `http://foo.co.uk`},
	{`http://foo.random`, `http://foo.random`},
	{` http://foo.com/bar `, `http://foo.com/bar`},
	{` http://foo.com/bar more`, `http://foo.com/bar`},
	{`<http://foo.com/bar>`, `http://foo.com/bar`},
	{`<http://foo.com/bar>more`, `http://foo.com/bar`},
	{`.http://foo.com/bar.`, `http://foo.com/bar`},
	{`.http://foo.com/bar.more`, `http://foo.com/bar.more`},
	{`,http://foo.com/bar,`, `http://foo.com/bar`},
	{`,http://foo.com/bar,more`, `http://foo.com/bar,more`},
	{`(http://foo.com/bar)`, `http://foo.com/bar`},
	{`"http://foo.com/bar'`, `http://foo.com/bar`},
	{`"http://foo.com/bar'more`, `http://foo.com/bar'more`},
	{`"http://foo.com/bar"`, `http://foo.com/bar`},
	{`http://a.b/a.,:;-+_()?@|&=#$~!*%'a`, `http://a.b/a.,:;-+_()?@|&=#$~!*%'a`},
	{`http://foo.com/path_(more)`, `http://foo.com/path_(more)`},
	{`(http://foo.com/path_(more))`, `http://foo.com/path_(more)`},
	{`http://foo.com/path_(even)-(more)`, `http://foo.com/path_(even)-(more)`},
	{`http://foo.com/path_(even)(more)`, `http://foo.com/path_(even)(more)`},
	{`http://foo.com/path_(even_(nested))`, `http://foo.com/path_(even_(nested))`},
	{`(http://foo.com/path_(even_(nested)))`, `http://foo.com/path_(even_(nested))`},
	{`http://foo.com/path#fragment`, `http://foo.com/path#fragment`},
	{`http://test.foo.com/`, `http://test.foo.com/`},
	{`http://foo.com/path`, `http://foo.com/path`},
	{`http://foo.com:8080/path`, `http://foo.com:8080/path`},
	{`http://1.1.1.1/path`, `http://1.1.1.1/path`},
	{`http://1080::8:800:200c:417a/path`, `http://1080::8:800:200c:417a/path`},
	{`http://中国.中国/foo中国`, `http://中国.中国/foo中国`},
	{`http://xn-foo.xn--p1acf/path`, `http://xn-foo.xn--p1acf/path`},
	{`http://✪foo.bar/pa✪th`, `http://✪foo.bar/pa✪th`},
	{`✪http://✪foo.bar/pa✪th✪`, `http://✪foo.bar/pa✪th`},
	{`what is http://foo.com?`, `http://foo.com`},
	{`what is http://foo.com/path?`, `http://foo.com/path`},
	{`the http://foo.com!`, `http://foo.com`},
	{`https://test.foo.bar/path?a=b`, `https://test.foo.bar/path?a=b`},
	{`ftp://user@foo.bar`, `ftp://user@foo.bar`},
	{`http://foo.com/@"style="color:red"onmouseover=func()`, `http://foo.com/`},
}

func TestRegexes(t *testing.T) {
	doTest(t, "Relaxed", Relaxed, constantTestCases)
	doTest(t, "Strict", Strict, constantTestCases)
	doTest(t, "Relaxed", Relaxed, []testCase{
		{`foo.a`, nil},
		{`foo.com`, `foo.com`},
		{`foo.com bar.com`, `foo.com`},
		{`foo.com-foo`, `foo.com`},
		{`foo.company`, `foo.company`},
		{`foo.comrandom`, nil},
		{`foo.onion`, `foo.onion`},
		{`foo.i2p`, `foo.i2p`},
		{`中国.中国`, `中国.中国`},
		{`中国.中国/foo中国`, `中国.中国/foo中国`},
		{`foo.com/`, `foo.com/`},
		{`1.1.1.1`, `1.1.1.1`},
		{`10.50.23.250`, `10.50.23.250`},
		{`121.1.1.1`, `121.1.1.1`},
		{`255.1.1.1`, `255.1.1.1`},
		{`300.1.1.1`, nil},
		{`1.1.1.300`, nil},
		{`1080:0:0:0:8:800:200C:4171`, `1080:0:0:0:8:800:200C:4171`},
		{`3ffe:2a00:100:7031::1`, `3ffe:2a00:100:7031::1`},
		{`1080::8:800:200c:417a`, `1080::8:800:200c:417a`},
		{`foo.com:8080`, `foo.com:8080`},
		{`foo.com:8080/path`, `foo.com:8080/path`},
		{`test.foo.com`, `test.foo.com`},
		{`test.foo.com/path`, `test.foo.com/path`},
		{`test.foo.com/path/more/`, `test.foo.com/path/more/`},
		{`TEST.FOO.COM/PATH`, `TEST.FOO.COM/PATH`},
		{`TEST.FÓO.COM/PÁTH`, `TEST.FÓO.COM/PÁTH`},
		{`foo.com/path_(more)`, `foo.com/path_(more)`},
		{`foo.com/path_(even)_(more)`, `foo.com/path_(even)_(more)`},
		{`foo.com/path_(more)/more`, `foo.com/path_(more)/more`},
		{`foo.com/path_(more)/end)`, `foo.com/path_(more)/end`},
		{`www.foo.com`, `www.foo.com`},
		{` foo.com/bar `, `foo.com/bar`},
		{` foo.com/bar more`, `foo.com/bar`},
		{`<foo.com/bar>`, `foo.com/bar`},
		{`<foo.com/bar>more`, `foo.com/bar`},
		{`,foo.com/bar.`, `foo.com/bar`},
		{`,foo.com/bar.more`, `foo.com/bar.more`},
		{`,foo.com/bar,`, `foo.com/bar`},
		{`,foo.com/bar,more`, `foo.com/bar,more`},
		{`(foo.com/bar)`, `foo.com/bar`},
		{`"foo.com/bar'`, `foo.com/bar`},
		{`"foo.com/bar'more`, `foo.com/bar'more`},
		{`"foo.com/bar"`, `foo.com/bar`},
		{`what is foo.com?`, `foo.com`},
		{`the foo.com!`, `foo.com`},

		{`foo@bar`, nil},
		{`foo@bar.a`, nil},
		{`foo@bar.com`, `foo@bar.com`},
		{`foo@bar.com bar@bar.com`, `foo@bar.com`},
		{`foo@bar.onion`, `foo@bar.onion`},
		{`foo@中国.中国`, `foo@中国.中国`},
		{`foo@test.bar.com`, `foo@test.bar.com`},
		{`FOO@TEST.BAR.COM`, `FOO@TEST.BAR.COM`},
		{`foo@bar.com/path`, `foo@bar.com`},
		{`foo+test@bar.com`, `foo+test@bar.com`},
		{`foo+._%-@bar.com`, `foo+._%-@bar.com`},
	})
	doTest(t, "Strict", Strict, []testCase{
		{`http:// foo.com`, nil},
		{`foo.a`, nil},
		{`foo.com`, nil},
		{`foo.com/`, nil},
		{`1.1.1.1`, nil},
		{`3ffe:2a00:100:7031::1`, nil},
		{`test.foo.com:8080/path`, nil},
		{`foo@bar.com`, nil},
	})
}

func TestStrictMatchingError(t *testing.T) {
	for _, c := range []struct {
		exp     string
		wantErr bool
	}{
		{`http://`, false},
		{`https?://`, false},
		{`http://|mailto:`, false},
		{`http://(`, true},
	} {
		_, err := StrictMatching(c.exp)
		if c.wantErr && err == nil {
			t.Errorf(`StrictMatching("%s") did not error as expected`, c.exp)
		} else if !c.wantErr && err != nil {
			t.Errorf(`StrictMatching("%s") unexpectedly errored`, c.exp)
		}
	}
}

func TestStrictMatching(t *testing.T) {
	strictMatching, _ := StrictMatching("http://|ftps?://|mailto:")
	doTest(t, "StrictMatching", strictMatching, []testCase{
		{`foo.com`, nil},
		{`foo@bar.com`, nil},
		{`http://foo`, `http://foo`},
		{`https://foo`, nil},
		{`ftp://foo`, `ftp://foo`},
		{`ftps://foo`, `ftps://foo`},
		{`mailto:foo`, `mailto:foo`},
		{`sms:123`, nil},
	})
}

func bench(b *testing.B, re *regexp.Regexp, str string) {
	for i := 0; i < b.N; i++ {
		re.FindAllString(str, -1)
	}
}

func BenchmarkStrictEmpty(b *testing.B) {
	bench(b, Strict, "foo")
}

func BenchmarkStrictSingle(b *testing.B) {
	bench(b, Strict, "http://foo.foo foo.com")
}

func BenchmarkStrictMany(b *testing.B) {
	bench(b, Strict, ` foo bar http://foo.foo
	foo.com bitcoin:address ftp://
	xmpp:foo@bar.com`)
}

func BenchmarkRelaxedEmpty(b *testing.B) {
	bench(b, Relaxed, "foo")
}

func BenchmarkRelaxedSingle(b *testing.B) {
	bench(b, Relaxed, "http://foo.foo foo.com")
}

func BenchmarkRelaxedMany(b *testing.B) {
	bench(b, Relaxed, ` foo bar http://foo.foo
	foo.com bitcoin:address ftp://
	xmpp:foo@bar.com`)
}
<commit_msg>Add a few more test cases<commit_after>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package xurls

import (
	"regexp"
	"testing"
)

type testCase struct {
	in   string
	want interface{}
}

func doTest(t *testing.T, name string, re *regexp.Regexp, cases []testCase) {
	for _, c := range cases {
		got := re.FindString(c.in)
		want, _ := c.want.(string)
		if got != want {
			t.Errorf(`%s.FindString("%s") got "%s", want "%s"`, name, c.in, got, want)
		}
	}
}

var constantTestCases = []testCase{
	{``, nil},
	{` `, nil},
	{`:`, nil},
	{`::`, nil},
	{`:::`, nil},
	{`::::`, nil},
	{`.`, nil},
	{`..`, nil},
	{`...`, nil},
	{`1.1`, nil},
	{`.1.`, nil},
	{`1.1.1`, nil},
	{`1:1`, nil},
	{`:1:`, nil},
	{`1:1:1`, nil},
	{`://`, nil},
	{`foo`, nil},
	{`foo:`, nil},
	{`mailto:`, nil},
	{`foo://`, nil},
	{`http://`, nil},
	{`http:// foo`, nil},
	{`http:// foo`, nil},
	{`:foo`, nil},
	{`://foo`, nil},
	{`foorandom:bar`, nil},
	{`foo.randombar`, nil},
	{`zzz.`, nil},
	{`.zzz`, nil},
	{`zzz.zzz`, nil},
	{`/some/path`, nil},
	{`rel/path`, nil},
	{`localhost`, nil},
	{`com`, nil},
	{`.com`, nil},
	{`com.`, nil},
	{`http`, nil},

	{`http://foo`, `http://foo`},
	{`http://FOO`, `http://FOO`},
	{`http://FAÀ`, `http://FAÀ`},
	{`https://localhost`, `https://localhost`},
	{`git+https://localhost`, `git+https://localhost`},
	{`foo.bar://localhost`, `foo.bar://localhost`},
	{`foo-bar://localhost`, `foo-bar://localhost`},
	{`mailto:foo`, `mailto:foo`},
	{`MAILTO:foo`, `MAILTO:foo`},
	{`sms:123`, `sms:123`},
	{`xmpp:foo@bar`, `xmpp:foo@bar`},
	{`bitcoin:Addr23?amount=1&message=foo`, `bitcoin:Addr23?amount=1&message=foo`},
	{`http://foo.com`, `http://foo.com`},
	{`http://foo.co.uk`, `http://foo.co.uk`},
	{`http://foo.random`, `http://foo.random`},
	{` http://foo.com/bar `, `http://foo.com/bar`},
	{` http://foo.com/bar more`, `http://foo.com/bar`},
	{`<http://foo.com/bar>`, `http://foo.com/bar`},
	{`<http://foo.com/bar>more`, `http://foo.com/bar`},
	{`.http://foo.com/bar.`, `http://foo.com/bar`},
	{`.http://foo.com/bar.more`, `http://foo.com/bar.more`},
	{`,http://foo.com/bar,`, `http://foo.com/bar`},
	{`,http://foo.com/bar,more`, `http://foo.com/bar,more`},
	{`(http://foo.com/bar)`, `http://foo.com/bar`},
	{`"http://foo.com/bar'`, `http://foo.com/bar`},
	{`"http://foo.com/bar'more`, `http://foo.com/bar'more`},
	{`"http://foo.com/bar"`, `http://foo.com/bar`},
	{`http://a.b/a.,:;-+_()?@|&=#$~!*%'a`, `http://a.b/a.,:;-+_()?@|&=#$~!*%'a`},
	{`http://foo.com/path_(more)`, `http://foo.com/path_(more)`},
	{`(http://foo.com/path_(more))`, `http://foo.com/path_(more)`},
	{`http://foo.com/path_(even)-(more)`, `http://foo.com/path_(even)-(more)`},
	{`http://foo.com/path_(even)(more)`, `http://foo.com/path_(even)(more)`},
	{`http://foo.com/path_(even_(nested))`, `http://foo.com/path_(even_(nested))`},
	{`(http://foo.com/path_(even_(nested)))`, `http://foo.com/path_(even_(nested))`},
	{`http://foo.com/path#fragment`, `http://foo.com/path#fragment`},
	{`http://test.foo.com/`, `http://test.foo.com/`},
	{`http://foo.com/path`, `http://foo.com/path`},
	{`http://foo.com:8080/path`, `http://foo.com:8080/path`},
	{`http://1.1.1.1/path`, `http://1.1.1.1/path`},
	{`http://1080::8:800:200c:417a/path`, `http://1080::8:800:200c:417a/path`},
	{`http://中国.中国/foo中国`, `http://中国.中国/foo中国`},
	{`http://xn-foo.xn--p1acf/path`, `http://xn-foo.xn--p1acf/path`},
	{`http://✪foo.bar/pa✪th`, `http://✪foo.bar/pa✪th`},
	{`✪http://✪foo.bar/pa✪th✪`, `http://✪foo.bar/pa✪th`},
	{`what is http://foo.com?`, `http://foo.com`},
	{`go visit http://foo.com/path.`, `http://foo.com/path`},
	{`go visit http://foo.com/path...`, `http://foo.com/path`},
	{`what is http://foo.com/path?`, `http://foo.com/path`},
	{`the http://foo.com!`, `http://foo.com`},
	{`https://test.foo.bar/path?a=b`, `https://test.foo.bar/path?a=b`},
	{`ftp://user@foo.bar`, `ftp://user@foo.bar`},
	{`http://foo.com/@"style="color:red"onmouseover=func()`, `http://foo.com/`},
}

func TestRegexes(t *testing.T) {
	doTest(t, "Relaxed", Relaxed, constantTestCases)
	doTest(t, "Strict", Strict, constantTestCases)
	doTest(t, "Relaxed", Relaxed, []testCase{
		{`foo.a`, nil},
		{`foo.com`, `foo.com`},
		{`foo.com bar.com`, `foo.com`},
		{`foo.com-foo`, `foo.com`},
		{`foo.company`, `foo.company`},
		{`foo.comrandom`, nil},
		{`foo.onion`, `foo.onion`},
		{`foo.i2p`, `foo.i2p`},
		{`中国.中国`, `中国.中国`},
		{`中国.中国/foo中国`, `中国.中国/foo中国`},
		{`foo.com/`, `foo.com/`},
		{`1.1.1.1`, `1.1.1.1`},
		{`10.50.23.250`, `10.50.23.250`},
		{`121.1.1.1`, `121.1.1.1`},
		{`255.1.1.1`, `255.1.1.1`},
		{`300.1.1.1`, nil},
		{`1.1.1.300`, nil},
		{`1080:0:0:0:8:800:200C:4171`, `1080:0:0:0:8:800:200C:4171`},
		{`3ffe:2a00:100:7031::1`, `3ffe:2a00:100:7031::1`},
		{`1080::8:800:200c:417a`, `1080::8:800:200c:417a`},
		{`foo.com:8080`, `foo.com:8080`},
		{`foo.com:8080/path`, `foo.com:8080/path`},
		{`test.foo.com`, `test.foo.com`},
		{`test.foo.com/path`, `test.foo.com/path`},
		{`test.foo.com/path/more/`, `test.foo.com/path/more/`},
		{`TEST.FOO.COM/PATH`, `TEST.FOO.COM/PATH`},
		{`TEST.FÓO.COM/PÁTH`, `TEST.FÓO.COM/PÁTH`},
		{`foo.com/path_(more)`, `foo.com/path_(more)`},
		{`foo.com/path_(even)_(more)`, `foo.com/path_(even)_(more)`},
		{`foo.com/path_(more)/more`, `foo.com/path_(more)/more`},
		{`foo.com/path_(more)/end)`, `foo.com/path_(more)/end`},
		{`www.foo.com`, `www.foo.com`},
		{` foo.com/bar `, `foo.com/bar`},
		{` foo.com/bar more`, `foo.com/bar`},
		{`<foo.com/bar>`, `foo.com/bar`},
		{`<foo.com/bar>more`, `foo.com/bar`},
		{`,foo.com/bar.`, `foo.com/bar`},
		{`,foo.com/bar.more`, `foo.com/bar.more`},
		{`,foo.com/bar,`, `foo.com/bar`},
		{`,foo.com/bar,more`, `foo.com/bar,more`},
		{`(foo.com/bar)`, `foo.com/bar`},
		{`"foo.com/bar'`, `foo.com/bar`},
		{`"foo.com/bar'more`, `foo.com/bar'more`},
		{`"foo.com/bar"`, `foo.com/bar`},
		{`what is foo.com?`, `foo.com`},
		{`the foo.com!`, `foo.com`},

		{`foo@bar`, nil},
		{`foo@bar.a`, nil},
		{`foo@bar.com`, `foo@bar.com`},
		{`foo@bar.com bar@bar.com`, `foo@bar.com`},
		{`foo@bar.onion`, `foo@bar.onion`},
		{`foo@中国.中国`, `foo@中国.中国`},
		{`foo@test.bar.com`, `foo@test.bar.com`},
		{`FOO@TEST.BAR.COM`, `FOO@TEST.BAR.COM`},
		{`foo@bar.com/path`, `foo@bar.com`},
		{`foo+test@bar.com`, `foo+test@bar.com`},
		{`foo+._%-@bar.com`, `foo+._%-@bar.com`},
	})
	doTest(t, "Strict", Strict, []testCase{
		{`http:// foo.com`, nil},
		{`foo.a`, nil},
		{`foo.com`, nil},
		{`foo.com/`, nil},
		{`1.1.1.1`, nil},
		{`3ffe:2a00:100:7031::1`, nil},
		{`test.foo.com:8080/path`, nil},
		{`foo@bar.com`, nil},
	})
}

func TestStrictMatchingError(t *testing.T) {
	for _, c := range []struct {
		exp     string
		wantErr bool
	}{
		{`http://`, false},
		{`https?://`, false},
		{`http://|mailto:`, false},
		{`http://(`, true},
	} {
		_, err := StrictMatching(c.exp)
		if c.wantErr && err == nil {
			t.Errorf(`StrictMatching("%s") did not error as expected`, c.exp)
		} else if !c.wantErr && err != nil {
			t.Errorf(`StrictMatching("%s") unexpectedly errored`, c.exp)
		}
	}
}

func TestStrictMatching(t *testing.T) {
	strictMatching, _ := StrictMatching("http://|ftps?://|mailto:")
	doTest(t, "StrictMatching", strictMatching, []testCase{
		{`foo.com`, nil},
		{`foo@bar.com`, nil},
		{`http://foo`, `http://foo`},
		{`https://foo`, nil},
		{`ftp://foo`, `ftp://foo`},
		{`ftps://foo`, `ftps://foo`},
		{`mailto:foo`, `mailto:foo`},
		{`sms:123`, nil},
	})
}

func bench(b *testing.B, re *regexp.Regexp, str string) {
	for i := 0; i < b.N; i++ {
		re.FindAllString(str, -1)
	}
}

func BenchmarkStrictEmpty(b *testing.B) {
	bench(b, Strict, "foo")
}

func BenchmarkStrictSingle(b *testing.B) {
	bench(b, Strict, "http://foo.foo foo.com")
}

func BenchmarkStrictMany(b *testing.B) {
	bench(b, Strict, ` foo bar http://foo.foo
	foo.com bitcoin:address ftp://
	xmpp:foo@bar.com`)
}

func BenchmarkRelaxedEmpty(b *testing.B) {
	bench(b, Relaxed, "foo")
}

func BenchmarkRelaxedSingle(b *testing.B) {
	bench(b, Relaxed, "http://foo.foo foo.com")
}

func BenchmarkRelaxedMany(b *testing.B) {
	bench(b, Relaxed, ` foo bar http://foo.foo
	foo.com bitcoin:address ftp://
	xmpp:foo@bar.com`)
}
<|endoftext|>