<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"

	"github.com/jessevdk/go-flags"
)

const version = "v0.0.1"

type opts struct {
	Help    bool   `short:"h" long:"help" description:"Print this message and quit"`
	Version bool   `short:"v" long:"version" description:"Print version information and quit"`
	OutDir  string `short:"o" long:"output" description:"Specify an output directory for Ghost posts"`
}

func main() {
	opts := &opts{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		return
	}

	if opts.Help || (len(args) == 0 && len(os.Args) < 2) {
		fmt.Fprintf(os.Stderr, helpText)
		return
	}

	if opts.Version {
		fmt.Fprintf(os.Stderr, "md2ghost: %s\n", version)
		return
	}

	var fp *os.File
	var pattern string

	// MEMO: args[0] != "" だと panic: runtime error: index out of range
	if len(args) != 0 {
		pattern = args[0] + "/*.md"
	} else {
		pattern = "." + "/*.md"
	}

	files, err := filepath.Glob(pattern)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	for _, file := range files {
		fp, err = os.Open(file)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
		defer fp.Close()

		scanner := bufio.NewScanner(fp)
		for scanner.Scan() {
			fmt.Println(scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
	}
}

const helpText = `md2ghost - Convert a markdown files into Ghost posts.

Usage: md2ghost [option] <file|directory>

Options:

  -o, --output  Specify an output directory for Ghost posts
  -h, --help    Print this message and quit
  -v, --version Print version information and quit

Example:

  $ md2ghost .
  $ md2ghost -o path/to/output_directory path/to/your_directory
`
<commit_msg>Add Ghost JSON format structure<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"

	"github.com/jessevdk/go-flags"
)

const version = "v0.0.1"

type opts struct {
	Help    bool   `short:"h" long:"help" description:"Print this message and quit"`
	Version bool   `short:"v" long:"version" description:"Print version information and quit"`
	OutDir  string `short:"o" long:"output" description:"Specify an output directory for Ghost posts"`
}

type GhostJSON struct {
	Meta Meta `json:"meta"`
	Data Data `json:"data"`
}

type Meta struct {
	ExportedOn int64  `json:"exported_on"`
	Version    string `json:"version"`
}

type Data struct {
	Posts []struct {
		AuthorID        int64       `json:"author_id"`
		CreatedAt       int64       `json:"created_at"`
		CreatedBy       int64       `json:"created_by"`
		Featured        int64       `json:"featured"`
		HTML            string      `json:"html"`
		ID              int64       `json:"id"`
		Image           interface{} `json:"image"`
		Language        string      `json:"language"`
		Markdown        string      `json:"markdown"`
		MetaDescription interface{} `json:"meta_description"`
		MetaTitle       interface{} `json:"meta_title"`
		Page            int64       `json:"page"`
		PublishedAt     int64       `json:"published_at"`
		PublishedBy     int64       `json:"published_by"`
		Slug            string      `json:"slug"`
		Status          string      `json:"status"`
		Title           string      `json:"title"`
		UpdatedAt       int64       `json:"updated_at"`
		UpdatedBy       int64       `json:"updated_by"`
	} `json:"posts"`
	PostsTags []struct {
		PostID int64 `json:"post_id"`
		TagID  int64 `json:"tag_id"`
	} `json:"posts_tags"`
	RolesUsers []struct {
		RoleID int64 `json:"role_id"`
		UserID int64 `json:"user_id"`
	} `json:"roles_users"`
	Tags []struct {
		Description string `json:"description"`
		ID          int64  `json:"id"`
		Name        string `json:"name"`
		Slug        string `json:"slug"`
	} `json:"tags"`
	Users []struct {
		Accessibility   interface{} `json:"accessibility"`
		Bio             interface{} `json:"bio"`
		Cover           interface{} `json:"cover"`
		CreatedAt       int64       `json:"created_at"`
		CreatedBy       int64       `json:"created_by"`
		Email           string      `json:"email"`
		ID              int64       `json:"id"`
		Image           interface{} `json:"image"`
		Language        string      `json:"language"`
		LastLogin       interface{} `json:"last_login"`
		Location        interface{} `json:"location"`
		MetaDescription interface{} `json:"meta_description"`
		MetaTitle       interface{} `json:"meta_title"`
		Name            string      `json:"name"`
		Slug            string      `json:"slug"`
		Status          string      `json:"status"`
		UpdatedAt       int64       `json:"updated_at"`
		UpdatedBy       int64       `json:"updated_by"`
		Website         interface{} `json:"website"`
	} `json:"users"`
}

func main() {
	opts := &opts{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		return
	}

	if opts.Help || (len(args) == 0 && len(os.Args) < 2) {
		fmt.Fprintf(os.Stderr, helpText)
		return
	}

	if opts.Version {
		fmt.Fprintf(os.Stderr, "md2ghost: %s\n", version)
		return
	}

	var fp *os.File
	var pattern string

	// MEMO: args[0] != "" だと panic: runtime error: index out of range
	if len(args) != 0 {
		pattern = args[0] + "/*.md"
	} else {
		pattern = "." + "/*.md"
	}

	files, err := filepath.Glob(pattern)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	for _, file := range files {
		fp, err = os.Open(file)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
		defer fp.Close()

		scanner := bufio.NewScanner(fp)
		for scanner.Scan() {
			fmt.Println(scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
	}
}

const helpText = `md2ghost - Convert a markdown files into Ghost posts.

Usage: md2ghost [option] <file|directory>

Options:

  -o, --output  Specify an output directory for Ghost posts
  -h, --help    Print this message and quit
  -v, --version Print version information and quit

Example:

  $ md2ghost .
  $ md2ghost -o path/to/output_directory path/to/your_directory
`
<|endoftext|>


<commit_before>/*
 * Pure Go SMART library
 * Copyright 2017 Daniel Swarbrick
 *
 * Broadcom (formerly Avago, LSI) MegaRAID ioctl functions
 * TODO:
 * - Improve code comments, refer to in-kernel structs
 * - Device Scan:
 *   - Walk /sys/class/scsi_host/ directory
 *   - "host%d" symlinks enumerate hosts
 *   - "host%d/proc_name" should contain the value "megaraid_sas"
 */

package smart

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	MAX_IOCTL_SGE = 16

	MFI_CMD_PD_SCSI_IO = 0x04
	MFI_CMD_DCMD       = 0x05

	MR_DCMD_PD_GET_LIST = 0x02010000 // Obsolete / deprecated command

	MFI_FRAME_DIR_NONE  = 0x0000
	MFI_FRAME_DIR_WRITE = 0x0008
	MFI_FRAME_DIR_READ  = 0x0010
	MFI_FRAME_DIR_BOTH  = 0x0018
)

type megasas_sge64 struct {
	phys_addr uint32
	length    uint32
	_padding  uint32
}

type Iovec struct {
	Base uint64 // FIXME: This is not portable to 32-bit platforms!
	Len  uint64
}

type megasas_dcmd_frame struct {
	cmd           uint8
	reserved_0    uint8
	cmd_status    uint8
	reserved_1    [4]uint8
	sge_count     uint8
	context       uint32
	pad_0         uint32
	flags         uint16
	timeout       uint16
	data_xfer_len uint32
	opcode        uint32
	mbox          [12]byte      // FIXME: This is actually a union of [12]uint8 / [6]uint16 / [3]uint32
	sgl           megasas_sge64 // FIXME: This is actually a union of megasas_sge64 / megasas_sge32
}

type megasas_pthru_frame struct {
	cmd                    uint8
	sense_len              uint8
	cmd_status             uint8
	scsi_status            uint8
	target_id              uint8
	lun                    uint8
	cdb_len                uint8
	sge_count              uint8
	context                uint32
	pad_0                  uint32
	flags                  uint16
	timeout                uint16
	data_xfer_len          uint32
	sense_buf_phys_addr_lo uint32
	sense_buf_phys_addr_hi uint32
	cdb                    [16]byte
	sgl                    megasas_sge64
}

type megasas_iocpacket struct {
	host_no   uint16
	__pad1    uint16
	sgl_off   uint32
	sge_count uint32
	sense_off uint32
	sense_len uint32
	// FIXME: This is actually a union of megasas_header / megasas_pthru_frame / megasas_dcmd_frame
	frame [128]byte
	// FIXME: Go is inserting 4 bytes of padding before this in order to 64-bit align the sgl member
	sgl [MAX_IOCTL_SGE]Iovec
}

// Megasas physical device address
type MegasasPDAddress struct {
	DeviceId          uint16
	EnclosureId       uint16
	EnclosureIndex    uint8
	SlotNumber        uint8
	SCSIDevType       uint8
	ConnectPortBitmap uint8
	SASAddr           [2]uint64
}

// Holder for megasas ioctl device
type MegasasIoctl struct {
	DeviceMajor int
	fd          int
}

var (
	// 0xc1944d01 - Beware: cannot use unsafe.Sizeof(megasas_iocpacket{}) due to Go struct padding!
	MEGASAS_IOC_FIRMWARE = _iowr('M', 1, 404)
)

// MakeDev returns the device ID for the specified major and minor numbers, equivalent to
// makedev(3). Based on gnu_dev_makedev macro, may be platform dependent!
func MakeDev(major, minor uint) uint {
	return (minor & 0xff) | ((major & 0xfff) << 8) |
		((minor &^ 0xff) << 12) | ((major &^ 0xfff) << 32)
}

// PackedBytes is a convenience method that will pack a megasas_iocpacket struct in little-endian
// format and return it as a byte slice
func (ioc *megasas_iocpacket) PackedBytes() []byte {
	b := new(bytes.Buffer)
	binary.Write(b, nativeEndian, ioc)
	return b.Bytes()
}

// CreateMegasasIoctl determines the device ID for the MegaRAID SAS ioctl device, creates it
// if necessary, and returns a MegasasIoctl struct to manage the device.
func CreateMegasasIoctl() (MegasasIoctl, error) {
	var (
		m   MegasasIoctl
		err error
	)

	// megaraid_sas driver does not automatically create ioctl device node, so find out the device
	// major number and create it.
	if file, err := os.Open("/proc/devices"); err == nil {
		defer file.Close()

		scanner := bufio.NewScanner(file)
		for scanner.Scan() {
			if strings.HasSuffix(scanner.Text(), "megaraid_sas_ioctl") {
				if _, err := fmt.Sscanf(scanner.Text(), "%d", &m.DeviceMajor); err == nil {
					break
				}
			}
		}

		if m.DeviceMajor == 0 {
			log.Println("Could not determine megaraid major number!")
			return m, nil
		}

		syscall.Mknod("/dev/megaraid_sas_ioctl_node", syscall.S_IFCHR, int(MakeDev(uint(m.DeviceMajor), 0)))
	} else {
		return m, err
	}

	m.fd, err = syscall.Open("/dev/megaraid_sas_ioctl_node", syscall.O_RDWR, 0600)

	if err != nil {
		return m, err
	}

	return m, nil
}

// Close closes the file descriptor of the MegasasIoctl instance
func (m *MegasasIoctl) Close() {
	syscall.Close(m.fd)
}

// MFI sends a MegaRAID Firmware Interface (MFI) command to the specified host
func (m *MegasasIoctl) MFI(host uint16, opcode uint32, b []byte) error {
	ioc := megasas_iocpacket{host_no: host}

	// Approximation of C union behaviour
	dcmd := (*megasas_dcmd_frame)(unsafe.Pointer(&ioc.frame))
	dcmd.cmd = MFI_CMD_DCMD
	dcmd.opcode = opcode
	dcmd.data_xfer_len = uint32(len(b))
	dcmd.sge_count = 1

	ioc.sge_count = 1
	ioc.sgl_off = uint32(unsafe.Offsetof(dcmd.sgl))
	ioc.sgl[0] = Iovec{uint64(uintptr(unsafe.Pointer(&b[0]))), uint64(len(b))}

	iocBuf := ioc.PackedBytes()

	// Note pointer to first item in iocBuf buffer
	if err := ioctl(uintptr(m.fd), MEGASAS_IOC_FIRMWARE, uintptr(unsafe.Pointer(&iocBuf[0]))); err != nil {
		return err
	}

	return nil
}

// PassThru sends a SCSI command to a MegaRAID controller
func (m *MegasasIoctl) PassThru(host uint16, diskNum uint8, cdb []byte, buf []byte, dxfer_dir int) {
	ioc := megasas_iocpacket{host_no: host}

	// Approximation of C union behaviour
	pthru := (*megasas_pthru_frame)(unsafe.Pointer(&ioc.frame))
	pthru.cmd_status = 0xff
	pthru.cmd = MFI_CMD_PD_SCSI_IO
	pthru.target_id = diskNum
	pthru.cdb_len = uint8(len(cdb))

	// FIXME: Don't use SG_* here
	switch dxfer_dir {
	case SG_DXFER_NONE:
		pthru.flags = MFI_FRAME_DIR_NONE
	case SG_DXFER_FROM_DEV:
		pthru.flags = MFI_FRAME_DIR_READ
	case SG_DXFER_TO_DEV:
		pthru.flags = MFI_FRAME_DIR_WRITE
	}

	copy(pthru.cdb[:], cdb)

	pthru.data_xfer_len = uint32(len(buf))
	pthru.sge_count = 1

	ioc.sge_count = 1
	ioc.sgl_off = uint32(unsafe.Offsetof(pthru.sgl))
	ioc.sgl[0] = Iovec{uint64(uintptr(unsafe.Pointer(&buf[0]))), uint64(len(buf))}

	iocBuf := ioc.PackedBytes()

	// Note pointer to first item in iocBuf buffer
	if err := ioctl(uintptr(m.fd), MEGASAS_IOC_FIRMWARE, uintptr(unsafe.Pointer(&iocBuf[0]))); err != nil {
		log.Fatal(err)
	}
}

// GetDeviceList retrieves a list of physical devices attached to the specified host
func (m *MegasasIoctl) GetDeviceList(host uint16) ([]MegasasPDAddress, error) {
	respBuf := make([]byte, 4096)

	if err := m.MFI(0, MR_DCMD_PD_GET_LIST, respBuf); err != nil {
		log.Println(err)
		return nil, err
	}

	respCount := nativeEndian.Uint32(respBuf[4:])

	// Create a device array large enough to hold the specified number of devices
	devices := make([]MegasasPDAddress, respCount)
	binary.Read(bytes.NewBuffer(respBuf[8:]), nativeEndian, &devices)

	return devices, nil
}

func OpenMegasasIoctl() error {
	var cdb, respBuf []byte

	m, _ := CreateMegasasIoctl()
	fmt.Printf("%#v\n", m)

	defer m.Close()

	// FIXME: Don't assume that host is always zero
	devices, _ := m.GetDeviceList(0)

	fmt.Println("\nEncl.  Slot  Device Id  SAS Address")
	for _, pd := range devices {
		if pd.SCSIDevType == 0 { // SCSI disk
			fmt.Printf("%5d   %3d      %5d  %#x\n", pd.EnclosureId, pd.SlotNumber, pd.DeviceId, pd.SASAddr[0])
		}
	}

	fmt.Println()

	for _, pd := range devices {
		if pd.SCSIDevType == 0 { // SCSI disk
			cdb = []byte{SCSI_INQUIRY, 0, 0, 0, INQ_REPLY_LEN, 0}
			respBuf = make([]byte, 512)
			m.PassThru(0, uint8(pd.DeviceId), cdb, respBuf, SG_DXFER_FROM_DEV)
			fmt.Printf("diskNum: %d  INQUIRY data: %.8s  %.16s  %.4s\n",
				pd.DeviceId, respBuf[8:], respBuf[16:], respBuf[32:])
		}
	}

	fmt.Println()

	// Send ATA IDENTIFY command as a CDB16 passthru command
	cdb = []byte{SCSI_ATA_PASSTHRU_16, 0x08, 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x00}
	respBuf = make([]byte, 512)
	m.PassThru(0, 26, cdb, respBuf, SG_DXFER_FROM_DEV)

	ident_buf := IdentifyDeviceData{}
	binary.Read(bytes.NewBuffer(respBuf), nativeEndian, &ident_buf)

	fmt.Printf("Serial Number: %s\n", swapBytes(ident_buf.SerialNumber[:]))
	fmt.Printf("Firmware Revision: %s\n", swapBytes(ident_buf.FirmwareRevision[:]))
	fmt.Printf("Model Number: %s\n", swapBytes(ident_buf.ModelNumber[:]))

	fmt.Println()

	// Send ATA SMART READ command as a CDB16 passthru command
	cdb = []byte{SCSI_ATA_PASSTHRU_16, 0x08, 0x0e, 0x00, 0xd0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0xc2, 0x00, 0xb0, 0x00}
	respBuf = make([]byte, 512)
	m.PassThru(0, 26, cdb, respBuf, SG_DXFER_FROM_DEV)

	smart := smartPage{}
	binary.Read(bytes.NewBuffer(respBuf[:362]), nativeEndian, &smart)

	fmt.Printf("SMART structure version: %d\n", smart.Version)
	fmt.Printf("ID# ATTRIBUTE_NAME           FLAG     VALUE WORST RESERVED RAW_VALUE     VENDOR_BYTES\n")

	for _, attr := range smart.Attrs {
		if attr.Id != 0 {
			var rawValue uint64

			for i := 5; i >= 0; i-- {
				rawValue |= uint64(attr.VendorBytes[i]) << uint64(i*8)
			}

			fmt.Printf("%3d %-24s %#04x   %03d   %03d   %03d      %-12d  %v (%s)\n",
				attr.Id, "(name)", attr.Flags, attr.Value, attr.Worst, attr.Reserved,
				rawValue, attr.VendorBytes, "(conv)")
		}
	}

	return nil
}
<commit_msg>Use common printSMART() function<commit_after>/*
 * Pure Go SMART library
 * Copyright 2017 Daniel Swarbrick
 *
 * Broadcom (formerly Avago, LSI) MegaRAID ioctl functions
 * TODO:
 * - Improve code comments, refer to in-kernel structs
 * - Device Scan:
 *   - Walk /sys/class/scsi_host/ directory
 *   - "host%d" symlinks enumerate hosts
 *   - "host%d/proc_name" should contain the value "megaraid_sas"
 */

package smart

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	MAX_IOCTL_SGE = 16

	MFI_CMD_PD_SCSI_IO = 0x04
	MFI_CMD_DCMD       = 0x05

	MR_DCMD_PD_GET_LIST = 0x02010000 // Obsolete / deprecated command

	MFI_FRAME_DIR_NONE  = 0x0000
	MFI_FRAME_DIR_WRITE = 0x0008
	MFI_FRAME_DIR_READ  = 0x0010
	MFI_FRAME_DIR_BOTH  = 0x0018
)

type megasas_sge64 struct {
	phys_addr uint32
	length    uint32
	_padding  uint32
}

type Iovec struct {
	Base uint64 // FIXME: This is not portable to 32-bit platforms!
	Len  uint64
}

type megasas_dcmd_frame struct {
	cmd           uint8
	reserved_0    uint8
	cmd_status    uint8
	reserved_1    [4]uint8
	sge_count     uint8
	context       uint32
	pad_0         uint32
	flags         uint16
	timeout       uint16
	data_xfer_len uint32
	opcode        uint32
	mbox          [12]byte      // FIXME: This is actually a union of [12]uint8 / [6]uint16 / [3]uint32
	sgl           megasas_sge64 // FIXME: This is actually a union of megasas_sge64 / megasas_sge32
}

type megasas_pthru_frame struct {
	cmd                    uint8
	sense_len              uint8
	cmd_status             uint8
	scsi_status            uint8
	target_id              uint8
	lun                    uint8
	cdb_len                uint8
	sge_count              uint8
	context                uint32
	pad_0                  uint32
	flags                  uint16
	timeout                uint16
	data_xfer_len          uint32
	sense_buf_phys_addr_lo uint32
	sense_buf_phys_addr_hi uint32
	cdb                    [16]byte
	sgl                    megasas_sge64
}

type megasas_iocpacket struct {
	host_no   uint16
	__pad1    uint16
	sgl_off   uint32
	sge_count uint32
	sense_off uint32
	sense_len uint32
	// FIXME: This is actually a union of megasas_header / megasas_pthru_frame / megasas_dcmd_frame
	frame [128]byte
	// FIXME: Go is inserting 4 bytes of padding before this in order to 64-bit align the sgl member
	sgl [MAX_IOCTL_SGE]Iovec
}

// Megasas physical device address
type MegasasPDAddress struct {
	DeviceId          uint16
	EnclosureId       uint16
	EnclosureIndex    uint8
	SlotNumber        uint8
	SCSIDevType       uint8
	ConnectPortBitmap uint8
	SASAddr           [2]uint64
}

// Holder for megasas ioctl device
type MegasasIoctl struct {
	DeviceMajor int
	fd          int
}

var (
	// 0xc1944d01 - Beware: cannot use unsafe.Sizeof(megasas_iocpacket{}) due to Go struct padding!
	MEGASAS_IOC_FIRMWARE = _iowr('M', 1, 404)
)

// MakeDev returns the device ID for the specified major and minor numbers, equivalent to
// makedev(3). Based on gnu_dev_makedev macro, may be platform dependent!
func MakeDev(major, minor uint) uint {
	return (minor & 0xff) | ((major & 0xfff) << 8) |
		((minor &^ 0xff) << 12) | ((major &^ 0xfff) << 32)
}

// PackedBytes is a convenience method that will pack a megasas_iocpacket struct in little-endian
// format and return it as a byte slice
func (ioc *megasas_iocpacket) PackedBytes() []byte {
	b := new(bytes.Buffer)
	binary.Write(b, nativeEndian, ioc)
	return b.Bytes()
}

// CreateMegasasIoctl determines the device ID for the MegaRAID SAS ioctl device, creates it
// if necessary, and returns a MegasasIoctl struct to manage the device.
func CreateMegasasIoctl() (MegasasIoctl, error) {
	var (
		m   MegasasIoctl
		err error
	)

	// megaraid_sas driver does not automatically create ioctl device node, so find out the device
	// major number and create it.
	if file, err := os.Open("/proc/devices"); err == nil {
		defer file.Close()

		scanner := bufio.NewScanner(file)
		for scanner.Scan() {
			if strings.HasSuffix(scanner.Text(), "megaraid_sas_ioctl") {
				if _, err := fmt.Sscanf(scanner.Text(), "%d", &m.DeviceMajor); err == nil {
					break
				}
			}
		}

		if m.DeviceMajor == 0 {
			log.Println("Could not determine megaraid major number!")
			return m, nil
		}

		syscall.Mknod("/dev/megaraid_sas_ioctl_node", syscall.S_IFCHR, int(MakeDev(uint(m.DeviceMajor), 0)))
	} else {
		return m, err
	}

	m.fd, err = syscall.Open("/dev/megaraid_sas_ioctl_node", syscall.O_RDWR, 0600)

	if err != nil {
		return m, err
	}

	return m, nil
}

// Close closes the file descriptor of the MegasasIoctl instance
func (m *MegasasIoctl) Close() {
	syscall.Close(m.fd)
}

// MFI sends a MegaRAID Firmware Interface (MFI) command to the specified host
func (m *MegasasIoctl) MFI(host uint16, opcode uint32, b []byte) error {
	ioc := megasas_iocpacket{host_no: host}

	// Approximation of C union behaviour
	dcmd := (*megasas_dcmd_frame)(unsafe.Pointer(&ioc.frame))
	dcmd.cmd = MFI_CMD_DCMD
	dcmd.opcode = opcode
	dcmd.data_xfer_len = uint32(len(b))
	dcmd.sge_count = 1

	ioc.sge_count = 1
	ioc.sgl_off = uint32(unsafe.Offsetof(dcmd.sgl))
	ioc.sgl[0] = Iovec{uint64(uintptr(unsafe.Pointer(&b[0]))), uint64(len(b))}

	iocBuf := ioc.PackedBytes()

	// Note pointer to first item in iocBuf buffer
	if err := ioctl(uintptr(m.fd), MEGASAS_IOC_FIRMWARE, uintptr(unsafe.Pointer(&iocBuf[0]))); err != nil {
		return err
	}

	return nil
}

// PassThru sends a SCSI command to a MegaRAID controller
func (m *MegasasIoctl) PassThru(host uint16, diskNum uint8, cdb []byte, buf []byte, dxfer_dir int) {
	ioc := megasas_iocpacket{host_no: host}

	// Approximation of C union behaviour
	pthru := (*megasas_pthru_frame)(unsafe.Pointer(&ioc.frame))
	pthru.cmd_status = 0xff
	pthru.cmd = MFI_CMD_PD_SCSI_IO
	pthru.target_id = diskNum
	pthru.cdb_len = uint8(len(cdb))

	// FIXME: Don't use SG_* here
	switch dxfer_dir {
	case SG_DXFER_NONE:
		pthru.flags = MFI_FRAME_DIR_NONE
	case SG_DXFER_FROM_DEV:
		pthru.flags = MFI_FRAME_DIR_READ
	case SG_DXFER_TO_DEV:
		pthru.flags = MFI_FRAME_DIR_WRITE
	}

	copy(pthru.cdb[:], cdb)

	pthru.data_xfer_len = uint32(len(buf))
	pthru.sge_count = 1

	ioc.sge_count = 1
	ioc.sgl_off = uint32(unsafe.Offsetof(pthru.sgl))
	ioc.sgl[0] = Iovec{uint64(uintptr(unsafe.Pointer(&buf[0]))), uint64(len(buf))}

	iocBuf := ioc.PackedBytes()

	// Note pointer to first item in iocBuf buffer
	if err := ioctl(uintptr(m.fd), MEGASAS_IOC_FIRMWARE, uintptr(unsafe.Pointer(&iocBuf[0]))); err != nil {
		log.Fatal(err)
	}
}

// GetDeviceList retrieves a list of physical devices attached to the specified host
func (m *MegasasIoctl) GetDeviceList(host uint16) ([]MegasasPDAddress, error) {
	respBuf := make([]byte, 4096)

	if err := m.MFI(0, MR_DCMD_PD_GET_LIST, respBuf); err != nil {
		log.Println(err)
		return nil, err
	}

	respCount := nativeEndian.Uint32(respBuf[4:])

	// Create a device array large enough to hold the specified number of devices
	devices := make([]MegasasPDAddress, respCount)
	binary.Read(bytes.NewBuffer(respBuf[8:]), nativeEndian, &devices)

	return devices, nil
}

func OpenMegasasIoctl() error {
	var cdb, respBuf []byte

	m, _ := CreateMegasasIoctl()
	fmt.Printf("%#v\n", m)

	defer m.Close()

	// FIXME: Don't assume that host is always zero
	devices, _ := m.GetDeviceList(0)

	fmt.Println("\nEncl.  Slot  Device Id  SAS Address")
	for _, pd := range devices {
		if pd.SCSIDevType == 0 { // SCSI disk
			fmt.Printf("%5d   %3d      %5d  %#x\n", pd.EnclosureId, pd.SlotNumber, pd.DeviceId, pd.SASAddr[0])
		}
	}

	fmt.Println()

	for _, pd := range devices {
		if pd.SCSIDevType == 0 { // SCSI disk
			cdb = []byte{SCSI_INQUIRY, 0, 0, 0, INQ_REPLY_LEN, 0}
			respBuf = make([]byte, 512)
			m.PassThru(0, uint8(pd.DeviceId), cdb, respBuf, SG_DXFER_FROM_DEV)
			fmt.Printf("diskNum: %d  INQUIRY data: %.8s  %.16s  %.4s\n",
				pd.DeviceId, respBuf[8:], respBuf[16:], respBuf[32:])
		}
	}

	// Send ATA IDENTIFY command as a CDB16 passthru command
	cdb = []byte{SCSI_ATA_PASSTHRU_16, 0x08, 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x00}
	respBuf = make([]byte, 512)
	m.PassThru(0, 26, cdb, respBuf, SG_DXFER_FROM_DEV)

	ident_buf := IdentifyDeviceData{}
	binary.Read(bytes.NewBuffer(respBuf), nativeEndian, &ident_buf)

	fmt.Println("\nATA IDENTIFY data follows:")
	fmt.Printf("Serial Number: %s\n", swapBytes(ident_buf.SerialNumber[:]))
	fmt.Printf("Firmware Revision: %s\n", swapBytes(ident_buf.FirmwareRevision[:]))
	fmt.Printf("Model Number: %s\n", swapBytes(ident_buf.ModelNumber[:]))

	db, err := openDriveDb("drivedb.toml")
	if err != nil {
		return err
	}

	thisDrive := db.lookupDrive(ident_buf.ModelNumber[:])
	fmt.Printf("Drive DB contains %d entries. Using model: %s\n", len(db.Drives), thisDrive.Family)

	// Send ATA SMART READ command as a CDB16 passthru command
	cdb = []byte{SCSI_ATA_PASSTHRU_16, 0x08, 0x0e, 0x00, 0xd0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0xc2, 0x00, 0xb0, 0x00}
	respBuf = make([]byte, 512)
	m.PassThru(0, 26, cdb, respBuf, SG_DXFER_FROM_DEV)

	smart := smartPage{}
	binary.Read(bytes.NewBuffer(respBuf[:362]), nativeEndian, &smart)
	printSMART(smart, thisDrive)

	return nil
}
<|endoftext|>


<commit_before>package security

import (
	"errors"

	"github.com/helioslabs/gozw/zwave/command-class"
	"github.com/helioslabs/gozw/zwave/command-class/security"
)

type EncryptedMessage struct {
	SenderNonce      []byte
	EncryptedPayload []byte
	ReceiverNonceID  byte
	HMAC             []byte
}

func (cmd EncryptedMessage) CommandClassID() byte {
	return byte(commandclass.Security)
}

func (cmd EncryptedMessage) CommandID() byte {
	return byte(security.CommandMessageEncapsulation)
}

func (cmd *EncryptedMessage) UnmarshalBinary(data []byte) error {
	// According to the docs, we must copy data if we wish to retain it after returning

	if len(data) < 17 {
		return errors.New("Payload length underflow")
	}

	payload := make([]byte, len(data))
	copy(payload, data)

	cmd.SenderNonce = payload[0:8]
	cmd.EncryptedPayload = payload[8 : len(payload)-9]
	cmd.ReceiverNonceID = payload[8+len(cmd.EncryptedPayload)]
	cmd.HMAC = payload[9+len(cmd.EncryptedPayload):]

	return nil
}

func (cmd *EncryptedMessage) MarshalBinary() (payload []byte, err error) {
	payload = make([]byte, 0)

	payload = append(payload, cmd.SenderNonce...)
	payload = append(payload, cmd.EncryptedPayload...)
	payload = append(payload, cmd.ReceiverNonceID)
	payload = append(payload, cmd.HMAC...)

	return payload, nil
}
<commit_msg>Fix *the* bug with security<commit_after>package security

import (
	"errors"

	"github.com/helioslabs/gozw/zwave/command-class"
	"github.com/helioslabs/gozw/zwave/command-class/security"
)

type EncryptedMessage struct {
	SenderNonce      []byte
	EncryptedPayload []byte
	ReceiverNonceID  byte
	HMAC             []byte
}

func (cmd EncryptedMessage) CommandClassID() byte {
	return byte(commandclass.Security)
}

func (cmd EncryptedMessage) CommandID() byte {
	return byte(security.CommandMessageEncapsulation)
}

func (cmd *EncryptedMessage) UnmarshalBinary(data []byte) error {
	// According to the docs, we must copy data if we wish to retain it after returning

	if len(data) < 19 {
		return errors.New("Payload length underflow")
	}

	payload := make([]byte, len(data))
	copy(payload, data)

	cmd.SenderNonce = payload[2:10]
	cmd.EncryptedPayload = payload[10 : len(payload)-9]
	cmd.ReceiverNonceID = payload[10+len(cmd.EncryptedPayload)]
	cmd.HMAC = payload[11+len(cmd.EncryptedPayload):]

	return nil
}

func (cmd *EncryptedMessage) MarshalBinary() (payload []byte, err error) {
	payload = make([]byte, 0)

	payload = append(payload, cmd.CommandClassID())
	payload = append(payload, cmd.CommandID())
	payload = append(payload, cmd.SenderNonce...)
	payload = append(payload, cmd.EncryptedPayload...)
	payload = append(payload, cmd.ReceiverNonceID)
	payload = append(payload, cmd.HMAC...)

	return payload, nil
}
<|endoftext|>


<commit_before>package pddl

import (
	"testing"
	"strings"
	"regexp"
)

type checkDomainTest struct {
	pddl string
	errorRegexp string
	test func(string, *Domain, *testing.T)
}

func (c checkDomainTest) run(t *testing.T) {
	d, p, err := Parse("", strings.NewReader(c.pddl))
	if p != nil {
		t.Fatalf("%s\nis a problem, not a domain", c.pddl)
	}
	if err != nil {
		t.Fatalf("%s\nparse error: %s", c.pddl, err)
	}
	switch err := Check(d, nil); {
	case err == nil && c.errorRegexp == "":
		if c.test != nil {
			c.test(c.pddl, d, t)
		}
	case err == nil && c.errorRegexp != "":
		t.Errorf("%s\nexpected error matching '%s'", c.pddl, c.errorRegexp)
	case err != nil && c.errorRegexp == "":
		t.Errorf("%s\nunexpected error '%s'", c.pddl, err)
	case err != nil && c.errorRegexp != "":
		re := regexp.MustCompile(c.errorRegexp)
		if !re.Match([]byte(err.Error())) {
			t.Errorf("%s\nexpected error matching '%s', got '%s'",
				c.pddl, c.errorRegexp, err.Error())
		}
	}
}

var reqsDefTests = []checkDomainTest{
	{ `(define (domain d) (:requirements :strips))`, "", nil },
	{ `(define (domain d) (:requirements :foo))`, "not supported", nil },
	{ `(define (domain d) (:requirements :strips :strips))`, "multiple", nil },
	{ `(define (domain d) (:requirements :strips :adl :strips))`, "multiple", nil },
}

func TestCheckReqsDef(t *testing.T) {
	for _, test := range reqsDefTests {
		test.run(t)
	}
}

var requirementsTests = []checkDomainTest{
	// typing
	{ `(define (domain d) (:types t))`, ":typing", nil },
	{ `(define (domain d) (:requirements :typing) (:types t))`, "", nil },
	{ `(define (domain d) (:requirements :adl) (:types t))`, "", nil },

	// negative-preconditions
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (not (p))))`,
		":negative-preconditions", nil },
	{ `(define (domain d)
		(:requirements :negative-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (not (p))))`,
		"", nil },

	// disjunctive-preconditions
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (or (p) (p))))`,
		":disjunctive-preconditions", nil },
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (imply (p) (p))))`,
		":disjunctive-preconditions", nil },
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (not (and (p) (p)))))`,
		":disjunctive-preconditions", nil },
	{ `(define (domain d)
		(:requirements :disjunctive-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (or (p) (p))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :disjunctive-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (imply (p) (p))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :disjunctive-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (not (and (p) (p)))))`,
		"", nil },

	// equality
/*	// This doesn't parse since = is not allowed as an identifier…
	{ `(define (domain d)
		(:constants c)
		(:action a :parameters () :precondition (=  c c)))`,
		"", nil },
*/

	// universal-preconditions
	{ `(define (domain d)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (forall (?x) (p ?x))))`,
		":universal-preconditions", nil },
	{ `(define (domain d)
		(:requirements :universal-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (forall (?x) (p ?x))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :quantified-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (forall (?x) (p ?x))))`,
		"", nil },

	// existential-preconditions
	{ `(define (domain d)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (exists (?x) (p ?x))))`,
		":existential-preconditions", nil },
	{ `(define (domain d)
		(:requirements :existential-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (exists (?x) (p ?x))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :quantified-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (exists (?x) (p ?x))))`,
		"", nil },

	// conditional-effects
	{ `(define (domain d)
		(:predicates (p ?x))
		(:action a :parameters () :effect (forall (?x) (p ?x))))`,
		":conditional-effects", nil },
	{ `(define (domain d)
		(:predicates (p) (q))
		(:action a :parameters () :effect (when (p) (q))))`,
		":conditional-effects", nil },
	{ `(define (domain d)
		(:requirements :conditional-effects)
		(:predicates (p ?x))
		(:action a :parameters () :effect (forall (?x) (p ?x))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :conditional-effects)
		(:predicates (p) (q))
		(:action a :parameters () :effect (when (p) (q))))`,
		"", nil },

	// :action-costs
	{ `(define (domain d)
		(:functions (total-cost)))`,
		":action-costs", nil },
	{ `(define (domain d)
		(:predicates (p) (q))
		(:action a :parameters () :effect (increase total-cost 1)))`,
		":action-costs", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost ?x))
		(:action a :parameters (?x) :effect (increase (total-cost ?x) 1)))`,
		"0-ary total-cost", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (foo-bar))
		(:action a :parameters (?x) :effect (increase foo-bar 1)))`,
		"0-ary total-cost", nil },
/*
	// Why does the parser reject a negative number?
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost))
		(:action a :parameters (?x) :effect (increase total-cost -1)))`,
		"negative", nil },
*/
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost))
		(:action a :parameters (?x) :effect (increase total-cost (total-cost))))`,
		"total-cost", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost)))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost))
		(:action a :parameters () :effect (increase total-cost 1)))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost))
		(:action a :parameters () :effect (increase total-cost 500)))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost) (f))
		(:action a :parameters () :effect (increase total-cost (f))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost) (f ?x))
		(:action a :parameters (?x) :effect (increase total-cost (f ?x))))`,
		"", nil },
}

func TestRequirements(t *testing.T) {
	for _, test := range requirementsTests {
		test.run(t)
	}
}

var typesDefTests = []checkDomainTest{
	{ `(define (domain d) (:requirements :typing) (:types t - s))`, "undefined", nil },
	{ `(define (domain d) (:requirements :typing) (:types t))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types t s))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types t s - object))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types t - s s - object))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types object))`, "",
		func(pddl string, d *Domain, t *testing.T) {
			if len(d.Types) == 1 {
				return
			}
			t.Errorf("%s\nexpected 1 type (object), got %d", pddl, len(d.Types))
		},
	},
	{ `(define (domain d) (:requirements :typing) (:types t))`, "",
		func(pddl string, d *Domain, t *testing.T) {
			if len(d.Types) == 2 {
				return
			}
			t.Errorf("%s\nexpected 2 type (object), got %d", pddl, len(d.Types))
		},
	},
	{ `(define (domain d) (:requirements :typing) (:types t))`, "",
		checkSupers("t", []string{"t", "object"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types t - s s))`, "",
		checkSupers("t", []string{"t", "object", "s"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types t - s s - t))`, "",
		checkSupers("t", []string{"t", "object", "s"}),
	},
}

// checkSupers returns a function that checks
// that the supers of the named type match
// the list of super types.
func checkSupers(typName string, supers []string) func(string, *Domain, *testing.T) {
	return func(pddl string, d *Domain, t *testing.T) {
		typ := (*Type)(nil)
		for i := range d.Types {
			if d.Types[i].Str == typName {
				typ = &d.Types[i]
				break
			}
		}
		if typ == nil {
			t.Fatalf("%s\ntype %s not found", pddl, typName)
		}
		expect := map[string]bool{}
		for _, s := range supers {
			expect[s] = true
		}
		seen := map[string]bool{}
		for _, s := range typ.Supers {
			if !expect[s.Str] {
				t.Errorf("%s\nunexpected super type %s: %v",
					pddl, s.Str, typ.Supers)
				return
			}
			if seen[s.Str] {
				t.Errorf("%s\nsuper type %s seen multiple times: %v",
					pddl, s.Str, typ.Supers)
				return
			}
			seen[s.Str] = true
		}
	}
}

func TestCheckTypessDef(t *testing.T) {
	for _, test := range typesDefTests {
		test.run(t)
	}
}<commit_msg>Add a couple more tests.<commit_after>package pddl

import (
	"testing"
	"strings"
	"regexp"
)

type checkDomainTest struct {
	pddl string
	errorRegexp string
	test func(string, *Domain, *testing.T)
}

func (c checkDomainTest) run(t *testing.T) {
	d, p, err := Parse("", strings.NewReader(c.pddl))
	if p != nil {
		t.Fatalf("%s\nis a problem, not a domain", c.pddl)
	}
	if err != nil {
		t.Fatalf("%s\nparse error: %s", c.pddl, err)
	}
	switch err := Check(d, nil); {
	case err == nil && c.errorRegexp == "":
		if c.test != nil {
			c.test(c.pddl, d, t)
		}
	case err == nil && c.errorRegexp != "":
		t.Errorf("%s\nexpected error matching '%s'", c.pddl, c.errorRegexp)
	case err != nil && c.errorRegexp == "":
		t.Errorf("%s\nunexpected error '%s'", c.pddl, err)
	case err != nil && c.errorRegexp != "":
		re := regexp.MustCompile(c.errorRegexp)
		if !re.Match([]byte(err.Error())) {
			t.Errorf("%s\nexpected error matching '%s', got '%s'",
				c.pddl, c.errorRegexp, err.Error())
		}
	}
}

var reqsDefTests = []checkDomainTest{
	{ `(define (domain d) (:requirements :strips))`, "", nil },
	{ `(define (domain d) (:requirements :foo))`, "not supported", nil },
	{ `(define (domain d) (:requirements :strips :strips))`, "multiple", nil },
	{ `(define (domain d) (:requirements :strips :adl :strips))`, "multiple", nil },
}

func TestCheckReqsDef(t *testing.T) {
	for _, test := range reqsDefTests {
		test.run(t)
	}
}

var requirementsTests = []checkDomainTest{
	// typing
	{ `(define (domain d) (:types t))`, ":typing", nil },
	{ `(define (domain d) (:requirements :typing) (:types t))`, "", nil },
	{ `(define (domain d) (:requirements :adl) (:types t))`, "", nil },

	// negative-preconditions
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (not (p))))`,
		":negative-preconditions", nil },
	{ `(define (domain d)
		(:requirements :negative-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (not (p))))`,
		"", nil },

	// disjunctive-preconditions
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (or (p) (p))))`,
		":disjunctive-preconditions", nil },
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (imply (p) (p))))`,
		":disjunctive-preconditions", nil },
	{ `(define (domain d)
		(:predicates (p))
		(:action a :parameters () :precondition (not (and (p) (p)))))`,
		":disjunctive-preconditions", nil },
	{ `(define (domain d)
		(:requirements :disjunctive-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (or (p) (p))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :disjunctive-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (imply (p) (p))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :disjunctive-preconditions)
		(:predicates (p))
		(:action a :parameters () :precondition (not (and (p) (p)))))`,
		"", nil },

	// equality
/*	// This doesn't parse since = is not allowed as an identifier…
	{ `(define (domain d)
		(:constants c)
		(:action a :parameters () :precondition (=  c c)))`,
		"", nil },
*/

	// universal-preconditions
	{ `(define (domain d)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (forall (?x) (p ?x))))`,
		":universal-preconditions", nil },
	{ `(define (domain d)
		(:requirements :universal-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (forall (?x) (p ?x))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :quantified-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (forall (?x) (p ?x))))`,
		"", nil },

	// existential-preconditions
	{ `(define (domain d)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (exists (?x) (p ?x))))`,
		":existential-preconditions", nil },
	{ `(define (domain d)
		(:requirements :existential-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (exists (?x) (p ?x))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :quantified-preconditions)
		(:predicates (p ?x))
		(:action a :parameters () :precondition (exists (?x) (p ?x))))`,
		"", nil },

	// conditional-effects
	{ `(define (domain d)
		(:predicates (p ?x))
		(:action a :parameters () :effect (forall (?x) (p ?x))))`,
		":conditional-effects", nil },
	{ `(define (domain d)
		(:predicates (p) (q))
		(:action a :parameters () :effect (when (p) (q))))`,
		":conditional-effects", nil },
	{ `(define (domain d)
		(:requirements :conditional-effects)
		(:predicates (p ?x))
		(:action a :parameters () :effect (forall (?x) (p ?x))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :conditional-effects)
		(:predicates (p) (q))
		(:action a :parameters () :effect (when (p) (q))))`,
		"", nil },

	// :action-costs
	{ `(define (domain d)
		(:functions (total-cost)))`,
		":action-costs", nil },
	{ `(define (domain d)
		(:predicates (p) (q))
		(:action a :parameters () :effect (increase total-cost 1)))`,
		":action-costs", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost ?x))
		(:action a :parameters (?x) :effect (increase (total-cost ?x) 1)))`,
		"0-ary total-cost", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (foo-bar))
		(:action a :parameters (?x) :effect (increase foo-bar 1)))`,
		"0-ary total-cost", nil },
/*
	// Why does the parser reject a negative number?
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost))
		(:action a :parameters (?x) :effect (increase total-cost -1)))`,
		"negative", nil },
*/
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost))
		(:action a :parameters (?x) :effect (increase total-cost (total-cost))))`,
		"total-cost", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:functions (total-cost)))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost))
		(:action a :parameters () :effect (increase total-cost 1)))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost))
		(:action a :parameters () :effect (increase total-cost 500)))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost) (f))
		(:action a :parameters () :effect (increase total-cost (f))))`,
		"", nil },
	{ `(define (domain d)
		(:requirements :action-costs)
		(:predicates (p) (q))
		(:functions (total-cost) (f ?x))
		(:action a :parameters (?x) :effect (increase total-cost (f ?x))))`,
		"", nil },
}

func TestRequirements(t *testing.T) {
	for _, test := range requirementsTests {
		test.run(t)
	}
}

var typesDefTests = []checkDomainTest{
	{ `(define (domain d) (:requirements :typing) (:types t - s))`, "undefined", nil },
	{ `(define (domain d) (:requirements :typing) (:types t))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types t s))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types t s - object))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types t - s s - object))`, "", nil },
	{ `(define (domain d) (:requirements :typing) (:types object))`, "",
		func(pddl string, d *Domain, t *testing.T) {
			if len(d.Types) == 1 {
				return
			}
			t.Errorf("%s\nexpected 1 type (object), got %d", pddl, len(d.Types))
		},
	},
	{ `(define (domain d) (:requirements :typing) (:types t))`, "",
		func(pddl string, d *Domain, t *testing.T) {
			if len(d.Types) == 2 {
				return
			}
			t.Errorf("%s\nexpected 2 type (object), got %d", pddl, len(d.Types))
		},
	},
	{ `(define (domain d))`, "",
		checkSupers("object", []string{"object"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types object))`, "",
		checkSupers("object", []string{"object"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types t))`, "",
		checkSupers("t", []string{"t", "object"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types t - s s))`, "",
		checkSupers("t", []string{"t", "s", "object"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types t - s s - t))`, "",
		checkSupers("t", []string{"t", "s", "object"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types t - s s - u u))`, "",
		checkSupers("t", []string{"t", "s", "u", "object"}),
	},
	{ `(define (domain d) (:requirements :typing) (:types t - s s - u u))`, "",
		checkSupers("s", []string{"s", "u", "object"}),
	},
}

// checkSupers returns a function that checks
// that the supers of the named type match
// the list of super types.
func checkSupers(typName string, supers []string) func(string, *Domain, *testing.T) {
	return func(pddl string, d *Domain, t *testing.T) {
		typ := (*Type)(nil)
		for i := range d.Types {
			if d.Types[i].Str == typName {
				typ = &d.Types[i]
				break
			}
		}
		if typ == nil {
			t.Fatalf("%s\ntype %s not found", pddl, typName)
		}
		expect := map[string]bool{}
		for _, s := range supers {
			expect[s] = true
		}
		seen := map[string]bool{}
		for _, s := range typ.Supers {
			if !expect[s.Str] {
				t.Errorf("%s\nunexpected super type %s: %v",
					pddl, s.Str, typ.Supers)
				return
			}
			if seen[s.Str] {
				t.Errorf("%s\nsuper type %s seen multiple times: %v",
					pddl, s.Str, typ.Supers)
				return
			}
			seen[s.Str] = true
		}
	}
}

func TestCheckTypessDef(t *testing.T) {
	for _, test := range typesDefTests {
		test.run(t)
	}
}<|endoftext|>


<commit_before>package macaroons

import (
	"context"
	"crypto/rand"
	"fmt"
	"io"

	"github.com/coreos/bbolt"

	"github.com/btcsuite/btcwallet/snacl"
)

const (
	// RootKeyLen is the length of a root key.
	RootKeyLen = 32
)

var (
	// rootKeyBucketName is the name of the root key store bucket.
	rootKeyBucketName = []byte("macrootkeys")

	// defaultRootKeyID is the ID of the default root key. The first is
	// just 0, to emulate the memory storage that comes with bakery.
	//
	// TODO(aakselrod): Add support for key rotation.
	defaultRootKeyID = []byte("0")

	// encryptedKeyID is the name of the database key that stores the
	// encryption key, encrypted with a salted + hashed password. The
	// format is 32 bytes of salt, and the rest is encrypted key.
	encryptedKeyID = []byte("enckey")

	// ErrAlreadyUnlocked specifies that the store has already been
	// unlocked.
	ErrAlreadyUnlocked = fmt.Errorf("macaroon store already unlocked")

	// ErrStoreLocked specifies that the store needs to be unlocked with
	// a password.
	ErrStoreLocked = fmt.Errorf("macaroon store is locked")

	// ErrPasswordRequired specifies that a nil password has been passed.
	ErrPasswordRequired = fmt.Errorf("a non-nil password is required")
)

// RootKeyStorage implements the bakery.RootKeyStorage interface.
type RootKeyStorage struct {
	*bbolt.DB

	encKey *snacl.SecretKey
}

// NewRootKeyStorage creates a RootKeyStorage instance.
// TODO(aakselrod): Add support for encryption of data with passphrase.
func NewRootKeyStorage(db *bbolt.DB) (*RootKeyStorage, error) {
	// If the store's bucket doesn't exist, create it.
	err := db.Update(func(tx *bbolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists(rootKeyBucketName)
		return err
	})
	if err != nil {
		return nil, err
	}

	// Return the DB wrapped in a RootKeyStorage object.
	return &RootKeyStorage{db, nil}, nil
}

// CreateUnlock sets an encryption key if one is not already set, otherwise it
// checks if the password is correct for the stored encryption key.
func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
	// Check if we've already unlocked the store; return an error if so.
	if r.encKey != nil {
		return ErrAlreadyUnlocked
	}

	// Check if a nil password has been passed; return an error if so.
	if password == nil {
		return ErrPasswordRequired
	}

	return r.Update(func(tx *bbolt.Tx) error {
		bucket := tx.Bucket(rootKeyBucketName)
		dbKey := bucket.Get(encryptedKeyID)
		if len(dbKey) > 0 {
			// We've already stored a key, so try to unlock with
			// the password.
			encKey := &snacl.SecretKey{}
			err := encKey.Unmarshal(dbKey)
			if err != nil {
				return err
			}

			err = encKey.DeriveKey(password)
			if err != nil {
				return err
			}

			r.encKey = encKey
			return nil
		}

		// We haven't yet stored a key, so create a new one.
		encKey, err := snacl.NewSecretKey(password, snacl.DefaultN,
			snacl.DefaultR, snacl.DefaultP)
		if err != nil {
			return err
		}

		err = bucket.Put(encryptedKeyID, encKey.Marshal())
		if err != nil {
			return err
		}

		r.encKey = encKey
		return nil
	})
}

// Get implements the Get method for the bakery.RootKeyStorage interface.
func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
	if r.encKey == nil {
		return nil, ErrStoreLocked
	}
	var rootKey []byte
	err := r.View(func(tx *bbolt.Tx) error {
		dbKey := tx.Bucket(rootKeyBucketName).Get(id)
		if len(dbKey) == 0 {
			return fmt.Errorf("root key with id %s doesn't exist",
				string(id))
		}

		decKey, err := r.encKey.Decrypt(dbKey)
		if err != nil {
			return err
		}

		rootKey = make([]byte, len(decKey))
		copy(rootKey[:], decKey)
		return nil
	})
	if err != nil {
		return nil, err
	}

	return rootKey, nil
}

// RootKey implements the RootKey method for the bakery.RootKeyStorage
// interface.
// TODO(aakselrod): Add support for key rotation.
func (r *RootKeyStorage) RootKey(_ context.Context) ([]byte, []byte, error) {
	if r.encKey == nil {
		return nil, nil, ErrStoreLocked
	}
	var rootKey []byte
	id := defaultRootKeyID
	err := r.Update(func(tx *bbolt.Tx) error {
		ns := tx.Bucket(rootKeyBucketName)
		dbKey := ns.Get(id)

		// If there's a root key stored in the bucket, decrypt it and
		// return it.
		if len(dbKey) != 0 {
			decKey, err := r.encKey.Decrypt(dbKey)
			if err != nil {
				return err
			}

			rootKey = make([]byte, len(decKey))
			copy(rootKey[:], decKey[:])
			return nil
		}

		// Otherwise, create a RootKeyLen-byte root key, encrypt it,
		// and store it in the bucket.
		rootKey = make([]byte, RootKeyLen)
		if _, err := io.ReadFull(rand.Reader, rootKey[:]); err != nil {
			return err
		}

		encKey, err := r.encKey.Encrypt(rootKey)
		if err != nil {
			return err
		}
		return ns.Put(id, encKey)
	})
	if err != nil {
		return nil, nil, err
	}

	return rootKey, id, nil
}

// Close closes the underlying database and zeroes the encryption key stored
// in memory.
func (r *RootKeyStorage) Close() error {
	if r.encKey != nil {
		r.encKey.Zero()
	}
	return r.DB.Close()
}
<commit_msg>macaroons: add encKeyMtx to prevent race condition<commit_after>package macaroons

import (
	"context"
	"crypto/rand"
	"fmt"
	"io"
	"sync"

	"github.com/coreos/bbolt"

	"github.com/btcsuite/btcwallet/snacl"
)

const (
	// RootKeyLen is the length of a root key.
	RootKeyLen = 32
)

var (
	// rootKeyBucketName is the name of the root key store bucket.
	rootKeyBucketName = []byte("macrootkeys")

	// defaultRootKeyID is the ID of the default root key. The first is
	// just 0, to emulate the memory storage that comes with bakery.
	//
	// TODO(aakselrod): Add support for key rotation.
	defaultRootKeyID = []byte("0")

	// encryptedKeyID is the name of the database key that stores the
	// encryption key, encrypted with a salted + hashed password. The
	// format is 32 bytes of salt, and the rest is encrypted key.
	encryptedKeyID = []byte("enckey")

	// ErrAlreadyUnlocked specifies that the store has already been
	// unlocked.
	ErrAlreadyUnlocked = fmt.Errorf("macaroon store already unlocked")

	// ErrStoreLocked specifies that the store needs to be unlocked with
	// a password.
	ErrStoreLocked = fmt.Errorf("macaroon store is locked")

	// ErrPasswordRequired specifies that a nil password has been passed.
	ErrPasswordRequired = fmt.Errorf("a non-nil password is required")
)

// RootKeyStorage implements the bakery.RootKeyStorage interface.
type RootKeyStorage struct {
	*bbolt.DB

	encKeyMtx sync.RWMutex
	encKey    *snacl.SecretKey
}

// NewRootKeyStorage creates a RootKeyStorage instance.
// TODO(aakselrod): Add support for encryption of data with passphrase.
func NewRootKeyStorage(db *bbolt.DB) (*RootKeyStorage, error) {
	// If the store's bucket doesn't exist, create it.
	err := db.Update(func(tx *bbolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists(rootKeyBucketName)
		return err
	})
	if err != nil {
		return nil, err
	}

	// Return the DB wrapped in a RootKeyStorage object.
	return &RootKeyStorage{DB: db, encKey: nil}, nil
}

// CreateUnlock sets an encryption key if one is not already set, otherwise it
// checks if the password is correct for the stored encryption key.
func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
	r.encKeyMtx.Lock()
	defer r.encKeyMtx.Unlock()

	// Check if we've already unlocked the store; return an error if so.
	if r.encKey != nil {
		return ErrAlreadyUnlocked
	}

	// Check if a nil password has been passed; return an error if so.
	if password == nil {
		return ErrPasswordRequired
	}

	return r.Update(func(tx *bbolt.Tx) error {
		bucket := tx.Bucket(rootKeyBucketName)
		dbKey := bucket.Get(encryptedKeyID)
		if len(dbKey) > 0 {
			// We've already stored a key, so try to unlock with
			// the password.
			encKey := &snacl.SecretKey{}
			err := encKey.Unmarshal(dbKey)
			if err != nil {
				return err
			}

			err = encKey.DeriveKey(password)
			if err != nil {
				return err
			}

			r.encKey = encKey
			return nil
		}

		// We haven't yet stored a key, so create a new one.
		encKey, err := snacl.NewSecretKey(password, snacl.DefaultN,
			snacl.DefaultR, snacl.DefaultP)
		if err != nil {
			return err
		}

		err = bucket.Put(encryptedKeyID, encKey.Marshal())
		if err != nil {
			return err
		}

		r.encKey = encKey
		return nil
	})
}

// Get implements the Get method for the bakery.RootKeyStorage interface.
func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
	r.encKeyMtx.RLock()
	defer r.encKeyMtx.RUnlock()

	if r.encKey == nil {
		return nil, ErrStoreLocked
	}
	var rootKey []byte
	err := r.View(func(tx *bbolt.Tx) error {
		dbKey := tx.Bucket(rootKeyBucketName).Get(id)
		if len(dbKey) == 0 {
			return fmt.Errorf("root key with id %s doesn't exist",
				string(id))
		}

		decKey, err := r.encKey.Decrypt(dbKey)
		if err != nil {
			return err
		}

		rootKey = make([]byte, len(decKey))
		copy(rootKey[:], decKey)
		return nil
	})
	if err != nil {
		return nil, err
	}

	return rootKey, nil
}

// RootKey implements the RootKey method for the bakery.RootKeyStorage
// interface.
// TODO(aakselrod): Add support for key rotation.
func (r *RootKeyStorage) RootKey(_ context.Context) ([]byte, []byte, error) {
	r.encKeyMtx.RLock()
	defer r.encKeyMtx.RUnlock()

	if r.encKey == nil {
		return nil, nil, ErrStoreLocked
	}
	var rootKey []byte
	id := defaultRootKeyID
	err := r.Update(func(tx *bbolt.Tx) error {
		ns := tx.Bucket(rootKeyBucketName)
		dbKey := ns.Get(id)

		// If there's a root key stored in the bucket, decrypt it and
		// return it.
		if len(dbKey) != 0 {
			decKey, err := r.encKey.Decrypt(dbKey)
			if err != nil {
				return err
			}

			rootKey = make([]byte, len(decKey))
			copy(rootKey[:], decKey[:])
			return nil
		}

		// Otherwise, create a RootKeyLen-byte root key, encrypt it,
		// and store it in the bucket.
		rootKey = make([]byte, RootKeyLen)
		if _, err := io.ReadFull(rand.Reader, rootKey[:]); err != nil {
			return err
		}

		encKey, err := r.encKey.Encrypt(rootKey)
		if err != nil {
			return err
		}
		return ns.Put(id, encKey)
	})
	if err != nil {
		return nil, nil, err
	}

	return rootKey, id, nil
}

// Close closes the underlying database and zeroes the encryption key stored
// in memory.
func (r *RootKeyStorage) Close() error {
	r.encKeyMtx.Lock()
	defer r.encKeyMtx.Unlock()

	if r.encKey != nil {
		r.encKey.Zero()
	}
	return r.DB.Close()
}
<|endoftext|>


<commit_before>package gobrake

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"
)

type ctxKey string

const traceCtxKey ctxKey = "ab_route_trace"

type routeBreakdownKey struct {
	Method   string    `json:"method"`
	Route    string    `json:"route"`
	RespType string    `json:"responseType"`
	Time     time.Time `json:"time"`
}

type routeBreakdown struct {
	routeBreakdownKey

	routeStat
	Groups map[string]*routeStat `json:"groups"`
}

func (b *routeBreakdown) Add(total time.Duration, groups map[string]time.Duration) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if b.Groups == nil {
		b.Groups = make(map[string]*routeStat)
	}

	_ = b.routeStat.Add(durInMs(total))

	var sum time.Duration
	for _, dur := range groups {
		sum += dur
	}

	other := total - sum
	if other < 0 {
		other = time.Microsecond
	}

	if groups == nil {
		groups = make(map[string]time.Duration)
	}
	groups["other"] = other

	for name, dur := range groups {
		s, ok := b.Groups[name]
		if !ok {
			s = newRouteStat()
			b.Groups[name] = s
		}
		_ = s.Add(durInMs(dur))
	}
}

func (b *routeBreakdown) Pack() error {
	max := b.routeStat.Count
	for _, v := range b.Groups {
		if v.Count > max {
			max = v.Count
		}
	}

	addZeroes(&b.routeStat, max)
	err := b.routeStat.Pack()
	if err != nil {
		return err
	}

	for _, v := range b.Groups {
		addZeroes(v, max)

		err = v.Pack()
		if err != nil {
			return err
		}
	}

	return nil
}

func addZeroes(s *routeStat, max int) {
	for i := s.Count; i < max; i++ {
		_ = s.Add(0)
	}
}

type routeBreakdowns struct {
	opt    *NotifierOptions
	apiURL string

	flushTimer *time.Timer
	addWG      *sync.WaitGroup

	mu sync.Mutex
	m  map[routeBreakdownKey]*routeBreakdown
}

func newRouteBreakdowns(opt *NotifierOptions) *routeBreakdowns {
	return &routeBreakdowns{
		opt: opt,
		apiURL: fmt.Sprintf("%s/api/v5/projects/%d/routes-breakdowns",
			opt.Host, opt.ProjectId),
	}
}

func (s *routeBreakdowns) init() {
	if s.flushTimer == nil {
		s.flushTimer = time.AfterFunc(flushPeriod, s.Flush)
		s.addWG = new(sync.WaitGroup)
		s.m = make(map[routeBreakdownKey]*routeBreakdown)
	}
}

// Flush sends to Airbrake route stats.
func (s *routeBreakdowns) Flush() {
	s.mu.Lock()

	s.flushTimer = nil
	addWG := s.addWG
	s.addWG = nil
	m := s.m
	s.m = nil

	s.mu.Unlock()

	if m == nil {
		return
	}

	addWG.Wait()
	err := s.send(m)
	if err != nil {
		logger.Printf("routeBreakdowns.send failed: %s", err)
	}
}

type breakdownsOut struct {
	Env    string            `json:"environment"`
	Routes []*routeBreakdown `json:"routes"`
}

func (s *routeBreakdowns) send(m map[routeBreakdownKey]*routeBreakdown) error {
	var routes []*routeBreakdown
	for _, v := range m {
		err := v.Pack()
		if err != nil {
			return err
		}
		routes = append(routes, v)
	}

	buf := buffers.Get().(*bytes.Buffer)
	defer buffers.Put(buf)
	buf.Reset()

	out := breakdownsOut{
		Env:    s.opt.Environment,
		Routes: routes,
	}
	err := json.NewEncoder(buf).Encode(out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", s.apiURL, buf)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", "Bearer "+s.opt.ProjectKey)
	req.Header.Set("Content-Type", "application/json")
	resp, err := s.opt.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	buf.Reset()
	_, err = buf.ReadFrom(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return nil
	}

	switch resp.StatusCode {
	case http.StatusUnauthorized:
		return errUnauthorized
	}

	err = fmt.Errorf("got unexpected response status=%q", resp.Status)
	return err
}

func (s *routeBreakdowns) Notify(c context.Context, trace *RouteTrace) error {
	if trace.StatusCode < 200 || (trace.StatusCode >= 300 && trace.StatusCode < 400) {
		// ignore
		return nil
	}

	key := routeBreakdownKey{
		Method:   trace.Method,
		Route:    trace.Route,
		RespType: trace.respType(),
		Time:     trace.Start.UTC().Truncate(time.Minute),
	}

	s.mu.Lock()
	s.init()
	b, ok := s.m[key]
	if !ok {
		b = &routeBreakdown{
			routeBreakdownKey: key,
		}
		s.m[key] = b
	}
	addWG := s.addWG
	addWG.Add(1)
	s.mu.Unlock()

	trace.mu.Lock()
	total := trace.End.Sub(trace.Start)
	groups := trace.groups
	trace.groups = nil
	trace.mu.Unlock()

	b.Add(total, groups)
	addWG.Done()

	return nil
}

type RouteTrace struct {
	Method      string
	Route       string
	StatusCode  int
	ContentType string

	Start time.Time
	End   time.Time

	mu     sync.Mutex
	groups map[string]time.Duration
}

func (t *RouteTrace) respType() string {
	if t.StatusCode >= 400 {
		return "error"
	}
	if t.ContentType == "" {
		return ""
	}
	ind := strings.LastIndexByte(t.ContentType, '/')
	if ind != -1 {
		return t.ContentType[ind+1:]
	}
	return t.ContentType
}

func NewRouteTrace(c context.Context, trace *RouteTrace) (context.Context, *RouteTrace) {
	if trace.Start.IsZero() {
		trace.Start = time.Now()
	}
	c = context.WithValue(c, traceCtxKey, trace)
	return c, trace
}

func RouteTraceFromContext(c context.Context) *RouteTrace {
	t, _ := c.Value(traceCtxKey).(*RouteTrace)
	return t
}

func (t *RouteTrace) Group(name string) Group {
	s := &group{
		trace: t,
		name:  name,
		start: time.Now(),
	}
	return s
}

func (t *RouteTrace) IncGroup(name string, dur time.Duration) {
	t.mu.Lock()
	if t.groups == nil {
		t.groups = make(map[string]time.Duration)
	}
	t.groups[name] += dur
	t.mu.Unlock()
}

type Group interface {
	Finish()
}

type group struct {
	trace *RouteTrace
	name  string
	start time.Time
}

func (g *group) Finish() {
	since := time.Since(g.start)
	g.trace.IncGroup(g.name, since)
}

func durInMs(dur time.Duration) float64 {
	return float64(dur) / float64(time.Millisecond)
}
<commit_msg>Tweak tracing api<commit_after>package gobrake

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"
	"time"
)

type ctxKey string

const traceCtxKey ctxKey = "ab_route_trace"

type routeBreakdownKey struct {
	Method   string    `json:"method"`
	Route    string    `json:"route"`
	RespType string    `json:"responseType"`
	Time     time.Time `json:"time"`
}

type routeBreakdown struct {
	routeBreakdownKey

	routeStat
	Groups map[string]*routeStat `json:"groups"`
}

func (b *routeBreakdown) Add(total time.Duration, groups map[string]time.Duration) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if b.Groups == nil {
		b.Groups = make(map[string]*routeStat)
	}

	_ = b.routeStat.Add(durInMs(total))

	var sum time.Duration
	for _, dur := range groups {
		sum += dur
	}

	other := total - sum
	if other < 0 {
		other = time.Microsecond
	}

	if groups == nil {
		groups = make(map[string]time.Duration)
	}
	groups["other"] = other

	for name, dur := range groups {
		s, ok := b.Groups[name]
		if !ok {
			s = newRouteStat()
			b.Groups[name] = s
		}
		_ = s.Add(durInMs(dur))
	}
}

func (b *routeBreakdown) Pack() error {
	max := b.routeStat.Count
	for _, v := range b.Groups {
		if v.Count > max {
			max = v.Count
		}
	}

	addZeroes(&b.routeStat, max)
	err := b.routeStat.Pack()
	if err != nil {
		return err
	}

	for _, v := range b.Groups {
		addZeroes(v, max)

		err = v.Pack()
		if err != nil {
			return err
		}
	}

	return nil
}

func addZeroes(s *routeStat, max int) {
	for i := s.Count; i < max; i++ {
		_ = s.Add(0)
	}
}

type routeBreakdowns struct {
	opt    *NotifierOptions
	apiURL string

	flushTimer *time.Timer
	addWG      *sync.WaitGroup

	mu sync.Mutex
	m  map[routeBreakdownKey]*routeBreakdown
}

func newRouteBreakdowns(opt *NotifierOptions) *routeBreakdowns {
	return &routeBreakdowns{
		opt: opt,
		apiURL: fmt.Sprintf("%s/api/v5/projects/%d/routes-breakdowns",
			opt.Host, opt.ProjectId),
	}
}

func (s *routeBreakdowns) init() {
	if s.flushTimer == nil {
		s.flushTimer = time.AfterFunc(flushPeriod, s.Flush)
		s.addWG = new(sync.WaitGroup)
		s.m = make(map[routeBreakdownKey]*routeBreakdown)
	}
}

// Flush sends to Airbrake route stats.
func (s *routeBreakdowns) Flush() {
	s.mu.Lock()

	s.flushTimer = nil
	addWG := s.addWG
	s.addWG = nil
	m := s.m
	s.m = nil

	s.mu.Unlock()

	if m == nil {
		return
	}

	addWG.Wait()
	err := s.send(m)
	if err != nil {
		logger.Printf("routeBreakdowns.send failed: %s", err)
	}
}

type breakdownsOut struct {
	Env    string            `json:"environment"`
	Routes []*routeBreakdown `json:"routes"`
}

func (s *routeBreakdowns) send(m map[routeBreakdownKey]*routeBreakdown) error {
	var routes []*routeBreakdown
	for _, v := range m {
		err := v.Pack()
		if err != nil {
			return err
		}
		routes = append(routes, v)
	}

	buf := buffers.Get().(*bytes.Buffer)
	defer buffers.Put(buf)
	buf.Reset()

	out := breakdownsOut{
		Env:    s.opt.Environment,
		Routes: routes,
	}
	err := json.NewEncoder(buf).Encode(out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", s.apiURL, buf)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", "Bearer "+s.opt.ProjectKey)
	req.Header.Set("Content-Type", "application/json")
	resp, err := s.opt.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	buf.Reset()
	_, err = buf.ReadFrom(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return nil
	}

	switch resp.StatusCode {
	case http.StatusUnauthorized:
		return errUnauthorized
	}

	err = fmt.Errorf("got unexpected response status=%q", resp.Status)
	return err
}

func (s *routeBreakdowns) Notify(c context.Context, trace *RouteTrace) error {
	if trace.StatusCode < 200 || (trace.StatusCode >= 300 && trace.StatusCode < 400) {
		// ignore
		return nil
	}

	key := routeBreakdownKey{
		Method:   trace.Method,
		Route:    trace.Route,
		RespType: trace.respType(),
		Time:     trace.Start.UTC().Truncate(time.Minute),
	}

	s.mu.Lock()
	s.init()
	b, ok := s.m[key]
	if !ok {
		b = &routeBreakdown{
			routeBreakdownKey: key,
		}
		s.m[key] = b
	}
	addWG := s.addWG
	addWG.Add(1)
	s.mu.Unlock()

	total := trace.End.Sub(trace.Start)
	groups := trace.flushGroups()

	b.Add(total, groups)
	addWG.Done()

	return nil
}

type RouteTrace struct {
	Method      string
	Route       string
	StatusCode  int
	ContentType string

	Start time.Time
	End   time.Time

	spansMu sync.RWMutex
	spans   map[string]Span

	groupsMu sync.Mutex
	groups   map[string]time.Duration
}

func NewRouteTrace(c context.Context, trace *RouteTrace) (context.Context, *RouteTrace) {
	if trace.Start.IsZero() {
		trace.Start = time.Now()
	}
	c = context.WithValue(c, traceCtxKey, trace)
	return c, trace
}

func RouteTraceFromContext(c context.Context) *RouteTrace {
	t, _ := c.Value(traceCtxKey).(*RouteTrace)
	return t
}

func (t *RouteTrace) Span(name string) Span {
	if t == nil {
		return noopSpan{}
	}

	s := &span{
		trace: t,
		name:  name,
		start: time.Now(),
	}

	t.spansMu.Lock()
	if t.spans == nil {
		t.spans = make(map[string]Span)
	}
	t.spans[name] = s
	t.spansMu.Unlock()

	return s
}

func (t *RouteTrace) StartSpan(name string) {
	_ = t.Span(name)
}

func (t *RouteTrace) FinishSpan(name string) {
	t.spansMu.RLock()
	s := t.spans[name]
	t.spansMu.RUnlock()

	if s == nil {
		log.Printf("no span with name=%q is in progress", name)
		return
	}
	s.Finish()
}

func (t *RouteTrace) IncGroup(name string, dur time.Duration) {
	t.groupsMu.Lock()
	if t.groups == nil {
		t.groups = make(map[string]time.Duration)
	}
	t.groups[name] += dur
	t.groupsMu.Unlock()
}

func (t *RouteTrace) flushGroups() map[string]time.Duration {
	t.groupsMu.Lock()
	groups := t.groups
	t.groups = nil
	t.groupsMu.Unlock()
	return groups
}

func (t *RouteTrace) respType() string {
	if t.StatusCode >= 400 {
		return "error"
	}
	if t.ContentType == "" {
		return ""
	}
	ind := strings.LastIndexByte(t.ContentType, '/')
	if ind != -1 {
		return t.ContentType[ind+1:]
	}
	return t.ContentType
}

type Span interface {
	Finish()
}

type span struct {
	trace *RouteTrace
	name  string
	start time.Time
}

var _ Span = (*span)(nil)

func (s *span) Finish() {
	since := time.Since(s.start)
	s.trace.IncGroup(s.name, since)
}

type noopSpan struct{}

var _ Span = noopSpan{}

func (noopSpan) Finish() {}

func durInMs(dur time.Duration) float64 {
	return float64(dur) / float64(time.Millisecond)
}
<|endoftext|>


<commit_before>package router

import (
	"encoding/json"
	"fmt"
	"time"
)

func (router *Router) GenerateStatusJSON(version, encryption string) ([]byte, error) {
	return json.Marshal(struct {
		Version         string
		Encryption      string
		Name, Interface string
		Macs            *MacCache
		Peers           *Peers
		Routes          *Routes
	}{version, encryption, router.Ourself.Name.String(), fmt.Sprintf("%v", router.Iface), router.Macs, router.Peers, router.Routes})
	// leaving out ConectionMaker due to async complexities
}

func (cache *MacCache) MarshalJSON() ([]byte, error) {
	type cacheEntry struct {
		Mac      string
		PeerName PeerName
		LastSeen time.Time
	}
	entries := make([]*cacheEntry, 0)
	for key, entry := range cache.table {
		entries = append(entries, &cacheEntry{intmac(key).String(), entry.peer.Name, entry.lastSeen})
	}
	return json.Marshal(entries)
}

func (peers *Peers) MarshalJSON() ([]byte, error) {
	ps := make([]*Peer, 0)
	peers.ForEach(func(_ PeerName, peer *Peer) { ps = append(ps, peer) })
	return json.Marshal(ps)
}

func (routes *Routes) MarshalJSON() ([]byte, error) {
	routes.RLock()
	defer routes.RUnlock()
	type uni struct {
		Dest, Via PeerName
	}
	type broad struct {
		Source PeerName
		Via    []PeerName
	}
	var r struct {
		Unicast   []*uni
		Broadcast []*broad
	}
	for name, hop := range routes.unicast {
		r.Unicast = append(r.Unicast, &uni{name, hop})
	}
	for name, hops := range routes.broadcast {
		r.Broadcast = append(r.Broadcast, &broad{name, hops})
	}
	return json.Marshal(r)
}

func (peer *Peer) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Name        string
		UID         uint64
		Version     uint64
		Connections []Connection
	}{peer.Name.String(), peer.UID, peer.version, peer.Connections()})
}

func (conn *RemoteConnection) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct{ RemoteName, TcpAddr string }{conn.Remote().Name.String(), conn.RemoteTCPAddr()})
}

func (name PeerName) MarshalJSON() ([]byte, error) {
	return json.Marshal(name.String())
}
<commit_msg>Add peer nickname to JSON output<commit_after>package router

import (
	"encoding/json"
	"fmt"
	"time"
)

func (router *Router) GenerateStatusJSON(version, encryption string) ([]byte, error) {
	return json.Marshal(struct {
		Version         string
		Encryption      string
		Name, Interface string
		Macs            *MacCache
		Peers           *Peers
		Routes          *Routes
	}{version, encryption, router.Ourself.Name.String(), fmt.Sprintf("%v", router.Iface), router.Macs, router.Peers, router.Routes})
	// leaving out ConectionMaker due to async complexities
}

func (cache *MacCache) MarshalJSON() ([]byte, error) {
	type cacheEntry struct {
		Mac      string
		PeerName PeerName
		LastSeen time.Time
	}
	entries := make([]*cacheEntry, 0)
	for key, entry := range cache.table {
		entries = append(entries, &cacheEntry{intmac(key).String(), entry.peer.Name, entry.lastSeen})
	}
	return json.Marshal(entries)
}

func (peers *Peers) MarshalJSON() ([]byte, error) {
	ps := make([]*Peer, 0)
	peers.ForEach(func(_ PeerName, peer *Peer) { ps = append(ps, peer) })
	return json.Marshal(ps)
}

func (routes *Routes) MarshalJSON() ([]byte, error) {
	routes.RLock()
	defer routes.RUnlock()
	type uni struct {
		Dest, Via PeerName
	}
	type broad struct {
		Source PeerName
		Via    []PeerName
	}
	var r struct {
		Unicast   []*uni
		Broadcast []*broad
	}
	for name, hop := range routes.unicast {
		r.Unicast = append(r.Unicast, &uni{name, hop})
	}
	for name, hops := range routes.broadcast {
		r.Broadcast = append(r.Broadcast, &broad{name, hops})
	}
	return json.Marshal(r)
}

func (peer *Peer) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Name        string
		Nickname    string
		UID         uint64
		Version     uint64
		Connections []Connection
	}{peer.Name.String(), peer.NickName, peer.UID, peer.version, peer.Connections()})
}

func (conn *RemoteConnection) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct{ RemoteName, TcpAddr string }{conn.Remote().Name.String(), conn.RemoteTCPAddr()})
}

func (name PeerName) MarshalJSON() ([]byte, error) {
	return json.Marshal(name.String())
}
<|endoftext|>


<commit_before>package hador

var _ Router = RouterFunc(nil)

// RouterFunc is a function type implemented Router interface.
type RouterFunc func(method Method, pattern string, handler interface{}, filters ...Filter) *Leaf

// AddRoute calls RouterFunc function. It is the most important method of RouterFunc.
// All other methods call this method finally.
func (r RouterFunc) AddRoute(method Method, pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r(method, pattern, handler, filters...)
}

// Get adds a new route binded with GET method.
func (r RouterFunc) Get(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Get().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Post adds a new route binded with POST method.
func (r RouterFunc) Post(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Post().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Put adds a new route binded with PUT method.
func (r RouterFunc) Put(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Put().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Delete adds a new route binded with DELETE method.
func (r RouterFunc) Delete(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Delete().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Patch adds a new route binded with Patch method.
func (r RouterFunc) Patch(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Patch().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Trace adds a new route binded with TRACE method.
func (r RouterFunc) Trace(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Trace().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Connect adds a new route binded with CONNECT method.
func (r RouterFunc) Connect(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Connect().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Options adds a new route binded with OPTIONS method.
func (r RouterFunc) Options(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Options().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Head adds a new route binded with HEAD method.
func (r RouterFunc) Head(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Head().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Any adds a new route binded with all method.
func (r RouterFunc) Any(pattern string, handler interface{}, filters ...Filter) *Leaf {
	for _, method := range Methods {
		r.AddRoute(method, pattern, handler, filters...)
	}
	return nil
}

// Route returns a setter-chain to add a new route step-by-step.
func (r RouterFunc) Route() MethodSetter {
	return func(method Method) PatternSetter {
		return func(pattern string) HandlerSetter {
			return func(handler interface{}, filters ...Filter) *Leaf {
				return r.AddRoute(method, pattern, handler, filters...)
			}
		}
	}
}

// Group adds multi routes one time.
func (r RouterFunc) Group(pattern string, fn func(Router), filters ...Filter) {
	fn(RouterFunc(
		func(method Method, subpattern string, handler interface{}, subfilters ...Filter) *Leaf {
			return r.AddRoute(method,
				pattern+subpattern,
				handler,
				append(filters, subfilters...)...)
		}))
}

// AddController adds routes of all methods by calling controller's matched method.
func (r RouterFunc) AddController(pattern string, controller ControllerInterface, filters ...Filter) {
	controllerFilter := &ControllerFilter{controller: controller}
	filters = append([]Filter{controllerFilter}, filters...)
	r.Group(pattern, func(sub Router) {
		for _, method := range Methods {
			handler := handlerForMethod(controller, method)
			leaf := sub.AddRoute(method, "/", handler)
			docFn := docMethodForMethod(controller, method)
			docFn(leaf)
		}
	}, filters...)
}
<commit_msg>Pretty style<commit_after>package hador

var _ Router = RouterFunc(nil)

// RouterFunc is a function type implemented Router interface.
type RouterFunc func(method Method, pattern string, handler interface{}, filters ...Filter) *Leaf

// AddRoute calls RouterFunc function. It is the most important method of RouterFunc.
// All other methods call this method finally.
func (r RouterFunc) AddRoute(method Method, pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r(method, pattern, handler, filters...)
}

// Route returns a setter-chain to add a new route step-by-step.
func (r RouterFunc) Route() MethodSetter {
	return func(method Method) PatternSetter {
		return func(pattern string) HandlerSetter {
			return func(handler interface{}, filters ...Filter) *Leaf {
				return r.AddRoute(method, pattern, handler, filters...)
			}
		}
	}
}

// Get adds a new route binded with GET method.
func (r RouterFunc) Get(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Get().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Post adds a new route binded with POST method.
func (r RouterFunc) Post(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Post().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Put adds a new route binded with PUT method.
func (r RouterFunc) Put(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Put().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Delete adds a new route binded with DELETE method.
func (r RouterFunc) Delete(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Delete().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Patch adds a new route binded with Patch method.
func (r RouterFunc) Patch(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Patch().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Trace adds a new route binded with TRACE method.
func (r RouterFunc) Trace(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Trace().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Connect adds a new route binded with CONNECT method.
func (r RouterFunc) Connect(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Connect().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Options adds a new route binded with OPTIONS method.
func (r RouterFunc) Options(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Options().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Head adds a new route binded with HEAD method.
func (r RouterFunc) Head(pattern string, handler interface{}, filters ...Filter) *Leaf {
	return r.Route().Head().Pattern(pattern).Handler(handler).AddFilters(filters...)
}

// Any adds a new route binded with all method.
func (r RouterFunc) Any(pattern string, handler interface{}, filters ...Filter) *Leaf {
	for _, method := range Methods {
		r.Route().Method(method).Pattern(pattern).Handler(handler).AddFilters(filters...)
	}
	return nil
}

// Group adds multi routes one time.
func (r RouterFunc) Group(pattern string, fn func(Router), filters ...Filter) {
	fn(RouterFunc(
		func(method Method, subpattern string, handler interface{}, subfilters ...Filter) *Leaf {
			return r.Route().
				Method(method).
				Pattern(pattern + subpattern).
				Handler(handler).
				AddFilters(filters...).
				AddFilters(subfilters...)
		}))
}

// AddController adds routes of all methods by calling controller's matched method.
func (r RouterFunc) AddController(pattern string, controller ControllerInterface, filters ...Filter) {
	controllerFilter := &ControllerFilter{controller: controller}
	filters = append([]Filter{controllerFilter}, filters...)
	r.Group(pattern, func(sub Router) {
		for _, method := range Methods {
			handler := handlerForMethod(controller, method)
			leaf := sub.Route().Method(method).Pattern("/").Handler(handler)
			docFn := docMethodForMethod(controller, method)
			docFn(leaf)
		}
	}, filters...)
}
<|endoftext|>


<commit_before>package rest

import (
	"fmt"
	"strings"
	"testing"

	"github.com/kr/pretty"
)

var urls = []string{
	"/user",
	"/user/test",
	"/user/:id",
	"/user/:id/param",
	"///user/:id/param1/param2/:id/param3//",
}

func TestSplit(t *testing.T) {
	for _, url := range urls {
		splitted := split(url)
		fmt.Printf("%q\n", splitted)
		if strings.Join(splitted, "/") != strings.Trim(url, "/") {
			t.Error(url, splitted)
		}
	}
}

func TestRouter(t *testing.T) {
	var r router
	for _, url := range urls {
		if err := r.add(url, url); err != nil {
			t.Error(err)
		}
	}
	for _, url := range urls {
		handler, params := r.lookup(url)
		if handler == nil {
			t.Error("Nil handler:", url)
		}
		pretty.Println(handler, params)
	}
	url := "/user/:id/param1/"
	handler, params := r.lookup(url)
	if handler != nil {
		t.Error("Bad handler:", url)
	}
	pretty.Println(handler, params)
}
<commit_msg>router test<commit_after>package rest

import (
	"fmt"
	"strings"
	"testing"
)

var urls = []string{
	"///user/:id/param1/param2/:id/param3//",
	"/user/:id/param",
	"/user/:id",
	"/user/test",
	"/user",
}

func TestSplit(t *testing.T) {
	for _, url := range urls {
		splitted := split(url)
		fmt.Printf("%q\n", splitted)
		if strings.Join(splitted, "/") != strings.Trim(url, "/") {
			t.Error(url, splitted)
		}
	}
}

func TestRouter(t *testing.T) {
	var r router
	for _, url := range urls {
		if err := r.add(url, url); err != nil {
			t.Error(err)
		}
	}
	for _, url := range urls {
		handler, params := r.lookup(url)
		if handler == nil {
			t.Error("Nil handler:", url)
		}
		fmt.Println(handler, params)
	}
	url := "/user/:id/param1/"
	handler, params := r.lookup(url)
	if handler != nil {
		t.Error("Bad handler:", url)
	}
	fmt.Println(handler, params)
	handler, params = r.lookup("/user/test/mama/1/2/3/4/5/6/7/8/9/0/")
	if handler != nil {
		t.Error("Bad handler:", url)
	}
}

func TestLongRouter(t *testing.T) {
	var r router
	url := `1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/` +
		`1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/` +
		`1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/` +
		`1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/` +
		`1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/` +
		`1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/1/2/3/4/5/6/7/8/9/0/`
	if err := r.add(url, "long"); err == nil {
		t.Error("must be error")
	}
}

func TestOnlyStaticRouter(t *testing.T) {
	var r router
	var urls = []string{
		"test/url",
		"test2/url",
		"test2/add",
		"test/add",
		"test",
	}
	for _, url := range urls {
		if err := r.add(url, "long"); err != nil {
			t.Error(err)
		}
	}
	handler, _ := r.lookup("/test")
	if handler == nil {
		t.Error("Bad handler:", "/test")
	}
	handler, _ = r.lookup("/test2")
	if handler != nil {
		t.Error("Bad handler:", "/test2")
	}
}
<|endoftext|>


<commit_before>package router

import (
	. "launchpad.net/gocheck"
	"testing"
)

func Test(t *testing.T) {
	TestingT(t)
}
<commit_msg>Supressing log output for go test<commit_after>package router

import (
	. "launchpad.net/gocheck"
	"log"
	"os"
	"testing"
)

func Test(t *testing.T) {
	file, _ := os.OpenFile("/dev/null", os.O_WRONLY, 0666)
	log.SetOutput(file)

	TestingT(t)
}
<|endoftext|>