<commit_before>package class

import (
	"encoding/binary"
	"io"
)

func readAttributes(r io.Reader, constPool ConstantPool) (Attributes, error) {
	var count uint16
	err := binary.Read(r, byteOrder, &count)
	if err != nil {
		return nil, err
	}

	attrs := make(Attributes, 0, count)

	for i := uint16(0); i < count; i++ {
		attr, err := readAttribute(r, constPool)
		if err != nil {
			return nil, err
		}

		attrs = append(attrs, attr)
	}

	return attrs, nil
}

func readAttribute(r io.Reader, constPool ConstantPool) (Attribute, error) {
	attrBase := baseAttribute{}

	err := multiError([]error{
		binary.Read(r, byteOrder, &attrBase.NameIndex),
		binary.Read(r, byteOrder, &attrBase.Length),
	})

	if err != nil {
		return nil, err
	}

	return fillAttribute(r, attrBase, constPool)
}

func fillAttribute(r io.Reader, attrBase baseAttribute, constPool ConstantPool) (Attribute, error) {
	var attr Attribute
	name := constPool.GetString(attrBase.NameIndex)

	switch name {
	case "ConstantValue":
		attrBase.attrType = ConstantValueTag
		attr = &ConstantValue{baseAttribute: attrBase}
	case "Code":
		attrBase.attrType = CodeTag
		attr = &Code{baseAttribute: attrBase}
	// case "StackMapTable":
	//     attrBase.attrType = StackMapTableTag
	//     attr = &StackMapTable{baseAttribute: attrBase}
	case "Exceptions":
		attrBase.attrType = ExceptionsTag
		attr = &Exceptions{baseAttribute: attrBase}
	case "InnerClasses":
		attrBase.attrType = InnerClassesTag
		attr = &InnerClasses{baseAttribute: attrBase}
	case "EnclosingMethod":
		attrBase.attrType = EnclosingMethodTag
		attr = &EnclosingMethod{baseAttribute: attrBase}
	case "Synthetic":
		attrBase.attrType = SyntheticTag
		attr = &Synthetic{baseAttribute: attrBase}
	case "Signature":
		attrBase.attrType = SignatureTag
		attr = &Signature{baseAttribute: attrBase}
	case "SourceFile":
		attrBase.attrType = SourceFileTag
		attr = &SourceFile{baseAttribute: attrBase}
	case "SourceDebugExtension":
		attrBase.attrType = SourceDebugExtensionTag
		attr = &SourceDebugExtension{baseAttribute: attrBase}
	case "LineNumberTable":
		attrBase.attrType = LineNumberTableTag
		attr = &LineNumberTable{baseAttribute: attrBase}
	case "LocalVariableTable":
		attrBase.attrType = LocalVariableTableTag
		attr = &LocalVariableTable{baseAttribute: attrBase}
	case "LocalVariableTypeTable":
		attrBase.attrType = LocalVariableTypeTableTag
		attr = &LocalVariableTypeTable{baseAttribute: attrBase}
	case "Deprecated":
		attrBase.attrType = DeprecatedTag
		attr = &Deprecated{baseAttribute: attrBase}
	// case "RuntimeVisibleAnnotations":
	// 	attrBase.attrType = RuntimeVisibleAnnotationsTag
	// 	attr = &RuntimeVisibleAnnotations{baseAttribute: attrBase}
	// case "RuntimeInvisibleAnnotations":
	// 	attrBase.attrType = RuntimeInvisibleAnnotationsTag
	// 	attr = &RuntimeInvisibleAnnotations{baseAttribute: attrBase}
	// case "RuntimeVisibleParameterAnnotations":
	// 	attrBase.attrType = RuntimeVisibleParameterAnnotationsTag
	// 	attr = &RuntimeVisibleParameterAnnotations{baseAttribute: attrBase}
	// case "RuntimeInvisibleParameterAnnotations":
	// 	attrBase.attrType = RuntimeInvisibleParameterAnnotationsTag
	// 	attr = &RuntimeInvisibleParameterAnnotations{baseAttribute: attrBase}
	// case "AnnotationDefault":
	// 	attrBase.attrType = AnnotationDefaultTag
	// 	attr = &AnnotationDefault{baseAttribute: attrBase}
	case "BootstrapMethods":
		attrBase.attrType = BootstrapMethodsTag
		attr = &BootstrapMethods{baseAttribute: attrBase}
	default:
		attrBase.attrType = UnknownTag
		attr = &UnknownAttr{baseAttribute: attrBase}
	}

	err := attr.Read(r, constPool)
	if err != nil {
		return nil, err
	}

	return attr, nil
}

type AttributeType uint8

type baseAttribute struct {
	attrType  AttributeType
	NameIndex ConstPoolIndex
	Length    uint16
}

func (b baseAttribute) GetTag() AttributeType {
	return b.attrType
}

func (_ baseAttribute) ConstantValue() *ConstantValue { panic("jclass: value is not ConstantValue") }
func (_ baseAttribute) Code() *Code                   { panic("jclass: value is not Code") }
func (_ baseAttribute) StackMapTable() *StackMapTable { panic("jclass: value is not StackMapTable") }
func (_ baseAttribute) Exceptions() *Exceptions       { panic("jclass: value is not Exceptions") }
func (_ baseAttribute) InnerClasses() *InnerClasses   { panic("jclass: value is not InnerClasses") }
func (_ baseAttribute) EnclosingMethod() *EnclosingMethod {
	panic("jclass: value is not EnclosingMethod")
}
func (_ baseAttribute) Synthetic() *Synthetic   { panic("jclass: value is not Synthetic") }
func (_ baseAttribute) Signature() *Signature   { panic("jclass: value is not Signature") }
func (_ baseAttribute) SourceFile() *SourceFile { panic("jclass: value is not SourceFile") }
func (_ baseAttribute) SourceDebugExtension() *SourceDebugExtension {
	panic("jclass: value is not SourceDebugExtension")
}
func (_ baseAttribute) LineNumberTable() *LineNumberTable {
	panic("jclass: value is not LineNumberTable")
}
func (_ baseAttribute) LocalVariableTable() *LocalVariableTable {
	panic("jclass: value is not LocalVariableTable")
}
func (_ baseAttribute) LocalVariableTypeTable() *LocalVariableTypeTable {
	panic("jclass: value is not LocalVariableTypeTable")
}
func (_ baseAttribute) Deprecated() *Deprecated { panic("jclass: value is not Deprecated") }
func (_ baseAttribute) RuntimeVisibleAnnotations() *RuntimeVisibleAnnotations {
	panic("jclass: value is not RuntimeVisibleAnnotations")
}
func (_ baseAttribute) RuntimeInvisibleAnnotations() *RuntimeInvisibleAnnotations {
	panic("jclass: value is not RuntimeInvisibleAnnotations")
}
func (_ baseAttribute) RuntimeVisibleParameterAnnotations() *RuntimeVisibleParameterAnnotations {
	panic("jclass: value is not RuntimeVisibleParameterAnnotations")
}
func (_ baseAttribute) RuntimeInvisibleParameterAnnotations() *RuntimeInvisibleParameterAnnotations {
	panic("jclass: value is not RuntimeInvisibleParameterAnnotations")
}
func (_ baseAttribute) AnnotationDefault() *AnnotationDefault {
	panic("jclass: value is not AnnotationDefault")
}
func (_ baseAttribute) BootstrapMethods() *BootstrapMethods {
	panic("jclass: value is not BootstrapMethods")
}

// field_info, may single
// ACC_STATIC only
type ConstantValue struct {
	baseAttribute
	Index ConstPoolIndex
}

func (a *ConstantValue) ConstantValue() *ConstantValue { return a }

func (a *ConstantValue) Read(r io.Reader, constPool ConstantPool) error {
	return binary.Read(r, byteOrder, &a.Index)
}

func (a *ConstantValue) Dump(w io.Writer) error { return nil }

// method_info, single
// not if native or abstract
type Code struct {
	baseAttribute

	MaxStackSize   uint16
	MaxLocalsCount uint16

	ByteCode        []uint8
	ExceptionsTable []CodeExceptions

	// only LineNumberTable, LocalVariableTable,
	// LocalVariableTypeTable, StackMapTable
	Attributes
}

type CodeExceptions struct {
	StartPC   uint16
	EndPC     uint16
	HandlerPC uint16
	// may be zero, then used for finally
	CatchType ConstPoolIndex
}

func (a *Code) Code() *Code { return a }

func (a *Code) Read(r io.Reader, constPool ConstantPool) error {
	var err error

	var codeLen uint32
	err = multiError([]error{
		binary.Read(r, byteOrder, &a.MaxStackSize),
		binary.Read(r, byteOrder, &a.MaxLocalsCount),
		binary.Read(r, byteOrder, &codeLen),
	})
	if err != nil {
		return err
	}

	a.ByteCode = make([]uint8, codeLen)
	err = binary.Read(r, byteOrder, a.ByteCode)
	if err != nil {
		return err
	}

	var exceptionsCount uint16
	err = binary.Read(r, byteOrder, &exceptionsCount)
	if err != nil {
		return err
	}

	a.Exceptions = make([]CodeException, exceptionsCount)
	err = binary.Read(r, byteOrder, a.Exceptions)
	if err != nil {
		return err
	}

	a.Attributes, err = readAttributes(r, constPool)
	return err
}

type StackMapTable struct {
	baseAttribute
}

func (a *StackMapTable) StackMapTable() *StackMapTable { return a }

// method_info, may single
type Exceptions struct {
	baseAttribute
	ExceptionsTable []ConstPoolIndex
}

func (a *Exceptions) Exceptions() *Exceptions { return a }

func (a *Exceptions) Read(r io.Reader, _ ConstantPool) error {
	var exceptionsCount uint16
	err := binary.Read(r, byteOrder, &exceptionsCount)
	if err != nil {
		return err
	}

	a.ExceptionsTable = make([]ConstPoolIndex, exceptionsCount)
	return binary.Read(r, byteOrder, a.ExceptionsTable)
}

// ClassFile, may single
type InnerClasses struct {
	baseAttribute

	ClassesCount uint16
	Classes      []struct {
		InnerClassIndex  ConstPoolIndex
		OuterClassIndex  ConstPoolIndex
		InnerName        ConstPoolIndex
		InnerAccessFlags AccessFlags
	}
}

func (_ *InnerClasses) InnerClasses() *InnerClasses { return a }

// ClassFile, may single
// iff local class or anonymous class
type EnclosingMethod struct {
	baseAttribute
	ClassIndex  ConstPoolIndex
	MethodIndex ConstPoolIndex
}

func (_ *EnclosingMethod) EnclosingMethod() *EnclosingMethod { return a }

// ClassFile, method_info or field_info, may single
// if compiler generated
// instead maybe: ACC_SYNTHETIC
type Synthetic baseAttribute

func (_ *Synthetic) Synthetic() *Synthetic { return a }

// ClassFile, field_info, or method_info, may single
type Signature struct {
	baseAttribute
	SignatureIndex ConstPoolIndex
}

func (_ *Signature) Signature() *Signature { return a }

// ClassFile, may single
type SourceFile struct {
	baseAttribute
	SourceFileIndex ConstPoolIndex
}

func (_ *SourceFile) SourceFile() *SourceFile { return a }

// ClassFile, may single
type SourceDebugExtension struct {
	baseAttribute
	DebugExtension string
}

func (_ *SourceDebugExtension) SourceDebugExtension() *SourceDebugExtension { return a }

// Code, may multiple
type LineNumberTable struct {
	baseAttribute
	TableLength uint16
	Table       []struct {
		StartPC    uint16
		LineNumber uint16
	}
}

func (_ *LineNumberTable) LineNumberTable() *LineNumberTable { return a }

// Code, may multiple
type LocalVariableTable struct {
	baseAttribute
	TableLength uint16
	Table       []struct {
		StartPC         uint16
		Length          uint16
		NameIndex       ConstPoolIndex
		DescriptorIndex ConstPoolIndex
		// index into local variable array of current frame
		Index uint16
	}
}

func (_ *LocalVariableTable) LocalVariableTable() *LocalVariableTable { return a }

// Code, may multiple
type LocalVariableTypeTable struct {
	baseAttribute
	TableLength uint16
	Table       []struct {
		StartPC        uint16
		Length         uint16
		NameIndex      ConstPoolIndex
		SignatureIndex ConstPoolIndex
		// index into local variable array of current frame
		Index uint16
	}
}

func (_ *LocalVariableTypeTable) LocalVariableTypeTable() *LocalVariableTypeTable { return a }

// ClassFile, field_info, or method_info, may single
type Deprecated baseAttribute

func (_ *Deprecated) Deprecated() *Deprecated { return a }

type RuntimeVisibleAnnotations struct {
	baseAttribute
}

func (_ *RuntimeVisibleAnnotations) RuntimeVisibleAnnotations() *RuntimeVisibleAnnotations { return a }

type RuntimeInvisibleAnnotations struct {
	baseAttribute
}

func (_ *RuntimeInvisibleAnnotations) RuntimeInvisibleAnnotations() *RuntimeInvisibleAnnotations {
	return a
}

type RuntimeVisibleParameterAnnotations struct {
	baseAttribute
}

func (_ *RuntimeVisibleParameterAnnotations) RuntimeVisibleParameterAnnotations() *RuntimeVisibleParameterAnnotations {
	return a
}

type RuntimeInvisibleParameterAnnotations struct {
	baseAttribute
}

func (_ *RuntimeInvisibleParameterAnnotations) RuntimeInvisibleParameterAnnotations() *RuntimeInvisibleParameterAnnotations {
	return a
}

type AnnotationDefault struct {
	baseAttribute
}

func (_ *AnnotationDefault) AnnotationDefault() *AnnotationDefault { return a }

// ClassFile, may single
// iff constpool conatains CONSTANT_InvokeDynamic_info
type BootstrapMethods struct {
	baseAttribute
	MethodsCount uint16
	Methods      []struct {
		MethodRef ConstPoolIndex
		ArgsCount uint16
		Args      []ConstPoolIndex
	}
}

func (_ *BootstrapMethods) BootstrapMethods() *BootstrapMethods { return a }
<commit_msg>Added read for more attribute types<commit_after>package class

import (
	"encoding/binary"
	"io"
)

func readAttributes(r io.Reader, constPool ConstantPool) (Attributes, error) {
	var count uint16
	err := binary.Read(r, byteOrder, &count)
	if err != nil {
		return nil, err
	}

	attrs := make(Attributes, 0, count)

	for i := uint16(0); i < count; i++ {
		attr, err := readAttribute(r, constPool)
		if err != nil {
			return nil, err
		}

		attrs = append(attrs, attr)
	}

	return attrs, nil
}

func readAttribute(r io.Reader, constPool ConstantPool) (Attribute, error) {
	attrBase := baseAttribute{}

	err := multiError([]error{
		binary.Read(r, byteOrder, &attrBase.NameIndex),
		binary.Read(r, byteOrder, &attrBase.Length),
	})

	if err != nil {
		return nil, err
	}

	return fillAttribute(r, attrBase, constPool)
}

func fillAttribute(r io.Reader, attrBase baseAttribute, constPool ConstantPool) (Attribute, error) {
	var attr Attribute
	name := constPool.GetString(attrBase.NameIndex)

	switch name {
	case "ConstantValue":
		attrBase.attrType = ConstantValueTag
		attr = &ConstantValue{baseAttribute: attrBase}
	case "Code":
		attrBase.attrType = CodeTag
		attr = &Code{baseAttribute: attrBase}
	// case "StackMapTable":
	//     attrBase.attrType = StackMapTableTag
	//     attr = &StackMapTable{baseAttribute: attrBase}
	case "Exceptions":
		attrBase.attrType = ExceptionsTag
		attr = &Exceptions{baseAttribute: attrBase}
	case "InnerClasses":
		attrBase.attrType = InnerClassesTag
		attr = &InnerClasses{baseAttribute: attrBase}
	case "EnclosingMethod":
		attrBase.attrType = EnclosingMethodTag
		attr = &EnclosingMethod{baseAttribute: attrBase}
	case "Synthetic":
		attrBase.attrType = SyntheticTag
		attr = &Synthetic{baseAttribute: attrBase}
	case "Signature":
		attrBase.attrType = SignatureTag
		attr = &Signature{baseAttribute: attrBase}
	case "SourceFile":
		attrBase.attrType = SourceFileTag
		attr = &SourceFile{baseAttribute: attrBase}
	case "SourceDebugExtension":
		attrBase.attrType = SourceDebugExtensionTag
		attr = &SourceDebugExtension{baseAttribute: attrBase}
	case "LineNumberTable":
		attrBase.attrType = LineNumberTableTag
		attr = &LineNumberTable{baseAttribute: attrBase}
	case "LocalVariableTable":
		attrBase.attrType = LocalVariableTableTag
		attr = &LocalVariableTable{baseAttribute: attrBase}
	case "LocalVariableTypeTable":
		attrBase.attrType = LocalVariableTypeTableTag
		attr = &LocalVariableTypeTable{baseAttribute: attrBase}
	case "Deprecated":
		attrBase.attrType = DeprecatedTag
		attr = &Deprecated{baseAttribute: attrBase}
	// case "RuntimeVisibleAnnotations":
	// 	attrBase.attrType = RuntimeVisibleAnnotationsTag
	// 	attr = &RuntimeVisibleAnnotations{baseAttribute: attrBase}
	// case "RuntimeInvisibleAnnotations":
	// 	attrBase.attrType = RuntimeInvisibleAnnotationsTag
	// 	attr = &RuntimeInvisibleAnnotations{baseAttribute: attrBase}
	// case "RuntimeVisibleParameterAnnotations":
	// 	attrBase.attrType = RuntimeVisibleParameterAnnotationsTag
	// 	attr = &RuntimeVisibleParameterAnnotations{baseAttribute: attrBase}
	// case "RuntimeInvisibleParameterAnnotations":
	// 	attrBase.attrType = RuntimeInvisibleParameterAnnotationsTag
	// 	attr = &RuntimeInvisibleParameterAnnotations{baseAttribute: attrBase}
	// case "AnnotationDefault":
	// 	attrBase.attrType = AnnotationDefaultTag
	// 	attr = &AnnotationDefault{baseAttribute: attrBase}
	case "BootstrapMethods":
		attrBase.attrType = BootstrapMethodsTag
		attr = &BootstrapMethods{baseAttribute: attrBase}
	default:
		attrBase.attrType = UnknownTag
		attr = &UnknownAttr{baseAttribute: attrBase}
	}

	err := attr.Read(r, constPool)
	if err != nil {
		return nil, err
	}

	return attr, nil
}

type AttributeType uint8

type baseAttribute struct {
	attrType  AttributeType
	NameIndex ConstPoolIndex
	Length    uint16
}

func (b baseAttribute) GetTag() AttributeType {
	return b.attrType
}

func (_ baseAttribute) ConstantValue() *ConstantValue { panic("jclass: value is not ConstantValue") }
func (_ baseAttribute) Code() *Code                   { panic("jclass: value is not Code") }
func (_ baseAttribute) StackMapTable() *StackMapTable { panic("jclass: value is not StackMapTable") }
func (_ baseAttribute) Exceptions() *Exceptions       { panic("jclass: value is not Exceptions") }
func (_ baseAttribute) InnerClasses() *InnerClasses   { panic("jclass: value is not InnerClasses") }
func (_ baseAttribute) EnclosingMethod() *EnclosingMethod {
	panic("jclass: value is not EnclosingMethod")
}
func (_ baseAttribute) Synthetic() *Synthetic   { panic("jclass: value is not Synthetic") }
func (_ baseAttribute) Signature() *Signature   { panic("jclass: value is not Signature") }
func (_ baseAttribute) SourceFile() *SourceFile { panic("jclass: value is not SourceFile") }
func (_ baseAttribute) SourceDebugExtension() *SourceDebugExtension {
	panic("jclass: value is not SourceDebugExtension")
}
func (_ baseAttribute) LineNumberTable() *LineNumberTable {
	panic("jclass: value is not LineNumberTable")
}
func (_ baseAttribute) LocalVariableTable() *LocalVariableTable {
	panic("jclass: value is not LocalVariableTable")
}
func (_ baseAttribute) LocalVariableTypeTable() *LocalVariableTypeTable {
	panic("jclass: value is not LocalVariableTypeTable")
}
func (_ baseAttribute) Deprecated() *Deprecated { panic("jclass: value is not Deprecated") }
func (_ baseAttribute) RuntimeVisibleAnnotations() *RuntimeVisibleAnnotations {
	panic("jclass: value is not RuntimeVisibleAnnotations")
}
func (_ baseAttribute) RuntimeInvisibleAnnotations() *RuntimeInvisibleAnnotations {
	panic("jclass: value is not RuntimeInvisibleAnnotations")
}
func (_ baseAttribute) RuntimeVisibleParameterAnnotations() *RuntimeVisibleParameterAnnotations {
	panic("jclass: value is not RuntimeVisibleParameterAnnotations")
}
func (_ baseAttribute) RuntimeInvisibleParameterAnnotations() *RuntimeInvisibleParameterAnnotations {
	panic("jclass: value is not RuntimeInvisibleParameterAnnotations")
}
func (_ baseAttribute) AnnotationDefault() *AnnotationDefault {
	panic("jclass: value is not AnnotationDefault")
}
func (_ baseAttribute) BootstrapMethods() *BootstrapMethods {
	panic("jclass: value is not BootstrapMethods")
}

// field_info, may single
// ACC_STATIC only
type ConstantValue struct {
	baseAttribute
	Index ConstPoolIndex
}

func (a *ConstantValue) ConstantValue() *ConstantValue { return a }

func (a *ConstantValue) Read(r io.Reader, constPool ConstantPool) error {
	return binary.Read(r, byteOrder, &a.Index)
}

func (a *ConstantValue) Dump(w io.Writer) error { return nil }

// method_info, single
// not if native or abstract
type Code struct {
	baseAttribute

	MaxStackSize   uint16
	MaxLocalsCount uint16

	ByteCode        []uint8
	ExceptionsTable []CodeExceptions

	// only LineNumberTable, LocalVariableTable,
	// LocalVariableTypeTable, StackMapTable
	Attributes
}

type CodeExceptions struct {
	StartPC   uint16
	EndPC     uint16
	HandlerPC uint16
	// may be zero, then used for finally
	CatchType ConstPoolIndex
}

func (a *Code) Code() *Code { return a }

func (a *Code) Read(r io.Reader, constPool ConstantPool) error {
	var err error

	var codeLen uint32
	err = multiError([]error{
		binary.Read(r, byteOrder, &a.MaxStackSize),
		binary.Read(r, byteOrder, &a.MaxLocalsCount),
		binary.Read(r, byteOrder, &codeLen),
	})
	if err != nil {
		return err
	}

	a.ByteCode = make([]uint8, codeLen)
	err = binary.Read(r, byteOrder, a.ByteCode)
	if err != nil {
		return err
	}

	var exceptionsCount uint16
	err = binary.Read(r, byteOrder, &exceptionsCount)
	if err != nil {
		return err
	}

	a.Exceptions = make([]CodeException, exceptionsCount)
	err = binary.Read(r, byteOrder, a.Exceptions)
	if err != nil {
		return err
	}

	a.Attributes, err = readAttributes(r, constPool)
	return err
}

type StackMapTable struct {
	baseAttribute
}

func (a *StackMapTable) StackMapTable() *StackMapTable { return a }

// method_info, may single
type Exceptions struct {
	baseAttribute
	ExceptionsTable []ConstPoolIndex
}

func (a *Exceptions) Exceptions() *Exceptions { return a }

func (a *Exceptions) Read(r io.Reader, _ ConstantPool) error {
	var exceptionsCount uint16
	err := binary.Read(r, byteOrder, &exceptionsCount)
	if err != nil {
		return err
	}

	a.ExceptionsTable = make([]ConstPoolIndex, exceptionsCount)
	return binary.Read(r, byteOrder, a.ExceptionsTable)
}

// ClassFile, may single
type InnerClasses struct {
	baseAttribute
	Classes []InnerClass
}

type InnerClass struct {
	InnerClassIndex  ConstPoolIndex
	OuterClassIndex  ConstPoolIndex
	InnerName        ConstPoolIndex
	InnerAccessFlags AccessFlags
}

func (_ *InnerClasses) InnerClasses() *InnerClasses { return a }

func (a *InnerClasses) Read(r io.Reader, _ ConstantPool) error {
	var classesCount uint16
	err := binary.Read(r, byteOrder, &classesCount)
	if err != nil {
		return err
	}

	a.Classes = make([]InnerClass, classesCount)
	return binary.Read(r, byteOrder, a.Classes)
}

// ClassFile, may single
// iff local class or anonymous class
type EnclosingMethod struct {
	baseAttribute
	ClassIndex  ConstPoolIndex
	MethodIndex ConstPoolIndex
}

func (_ *EnclosingMethod) EnclosingMethod() *EnclosingMethod { return a }

func (a *EnclosingMethod) Read(r io.Reader, _ ConstantPool) error {
	return multiError([]error{
		binary.Read(r, byteOrder, &a.ClassIndex),
		binary.Read(r, byteOrder, &a.MethodIndex),
	})
}

// ClassFile, method_info or field_info, may single
// if compiler generated
// instead maybe: ACC_SYNTHETIC
type Synthetic struct{ baseAttribute }

func (_ *Synthetic) Synthetic() *Synthetic { return a }

// ClassFile, field_info, or method_info, may single
type Signature struct {
	baseAttribute
	SignatureIndex ConstPoolIndex
}

func (_ *Signature) Signature() *Signature { return a }

// ClassFile, may single
type SourceFile struct {
	baseAttribute
	SourceFileIndex ConstPoolIndex
}

func (_ *SourceFile) SourceFile() *SourceFile { return a }

// ClassFile, may single
type SourceDebugExtension struct {
	baseAttribute
	DebugExtension string
}

func (_ *SourceDebugExtension) SourceDebugExtension() *SourceDebugExtension { return a }

// Code, may multiple
type LineNumberTable struct {
	baseAttribute
	TableLength uint16
	Table       []struct {
		StartPC    uint16
		LineNumber uint16
	}
}

func (_ *LineNumberTable) LineNumberTable() *LineNumberTable { return a }

// Code, may multiple
type LocalVariableTable struct {
	baseAttribute
	TableLength uint16
	Table       []struct {
		StartPC         uint16
		Length          uint16
		NameIndex       ConstPoolIndex
		DescriptorIndex ConstPoolIndex
		// index into local variable array of current frame
		Index uint16
	}
}

func (_ *LocalVariableTable) LocalVariableTable() *LocalVariableTable { return a }

// Code, may multiple
type LocalVariableTypeTable struct {
	baseAttribute
	TableLength uint16
	Table       []struct {
		StartPC        uint16
		Length         uint16
		NameIndex      ConstPoolIndex
		SignatureIndex ConstPoolIndex
		// index into local variable array of current frame
		Index uint16
	}
}

func (_ *LocalVariableTypeTable) LocalVariableTypeTable() *LocalVariableTypeTable { return a }

// ClassFile, field_info, or method_info, may single
type Deprecated struct{ baseAttribute }

func (_ *Deprecated) Deprecated() *Deprecated { return a }

type RuntimeVisibleAnnotations struct {
	baseAttribute
}

func (_ *RuntimeVisibleAnnotations) RuntimeVisibleAnnotations() *RuntimeVisibleAnnotations { return a }

type RuntimeInvisibleAnnotations struct {
	baseAttribute
}

func (_ *RuntimeInvisibleAnnotations) RuntimeInvisibleAnnotations() *RuntimeInvisibleAnnotations {
	return a
}

type RuntimeVisibleParameterAnnotations struct {
	baseAttribute
}

func (_ *RuntimeVisibleParameterAnnotations) RuntimeVisibleParameterAnnotations() *RuntimeVisibleParameterAnnotations {
	return a
}

type RuntimeInvisibleParameterAnnotations struct {
	baseAttribute
}

func (_ *RuntimeInvisibleParameterAnnotations) RuntimeInvisibleParameterAnnotations() *RuntimeInvisibleParameterAnnotations {
	return a
}

type AnnotationDefault struct {
	baseAttribute
}

func (_ *AnnotationDefault) AnnotationDefault() *AnnotationDefault { return a }

// ClassFile, may single
// iff constpool conatains CONSTANT_InvokeDynamic_info
type BootstrapMethods struct {
	baseAttribute
	MethodsCount uint16
	Methods      []struct {
		MethodRef ConstPoolIndex
		ArgsCount uint16
		Args      []ConstPoolIndex
	}
}

func (_ *BootstrapMethods) BootstrapMethods() *BootstrapMethods { return a }
<|endoftext|>


<commit_before>package provider

import (
	. "github.com/smartystreets/goconvey/convey"
	"io/ioutil"
	"os"
	"testing"
)

func TestResolve(t *testing.T) {
	Convey("Passing no provider should return an error", t, func() {
		// Point to a tempdir to avoid pollution from dev env
		tempDir, _ := ioutil.TempDir("", "summontest")
		defer os.RemoveAll(tempDir)
		DefaultPath = tempDir

		_, err := Resolve("")

		So(err, ShouldNotBeNil)
	})

	Convey("Passing the provider via CLI should return it without error", t, func() {
		expected := "/usr/bin/myprovider"
		provider, err := Resolve(expected)

		So(provider, ShouldEqual, expected)
		So(err, ShouldBeNil)
	})

	Convey("Setting the provider via environment variable works", t, func() {
		expected := "/opt/providers/custom"
		os.Setenv("SUMMON_PROVIDER", expected)
		provider, err := Resolve("")
		os.Unsetenv("SUMMON_PROVIDER")

		So(provider, ShouldEqual, expected)
		So(err, ShouldBeNil)
	})

	Convey("Given a provider path", t, func() {
		tempDir, _ := ioutil.TempDir("", "summontest")
		defer os.RemoveAll(tempDir)
		DefaultPath = tempDir

		Convey("If there is 1 executable, return it as the provider", func() {
			f, err := ioutil.TempFile(DefaultPath, "")
			provider, err := Resolve("")

			So(provider, ShouldEqual, f.Name())
			So(err, ShouldBeNil)
		})

		Convey("If there are > 1 executables, return an error to user", func() {
			// Create 2 exes in provider path
			ioutil.TempFile(DefaultPath, "")
			ioutil.TempFile(DefaultPath, "")
			_, err := Resolve("")

			So(err, ShouldNotBeNil)
		})
	})
}

func TestCall(t *testing.T) {
	Convey("When I call a provider", t, func() {
		Convey("If it returns exit code 0, return stdout", func() {
			arg := "provider.go"
			out, err := Call("ls", arg)

			So(out, ShouldEqual, arg)
			So(err, ShouldBeNil)
		})
		Convey("If it returns exit code > 0, return stderr", func() {
			out, err := Call("ls", "README.notafile")

			So(out, ShouldBeBlank)
			So(err.Error(), ShouldContainSubstring, "No such file or directory")
		})
	})
}
<commit_msg>fix tests from commit 5df0fde<commit_after>package provider

import (
	. "github.com/smartystreets/goconvey/convey"
	"io/ioutil"
	"os"
	"testing"
)

func TestResolve(t *testing.T) {
	Convey("Passing no provider should return an error", t, func() {
		// Point to a tempdir to avoid pollution from dev env
		tempDir, _ := ioutil.TempDir("", "summontest")
		defer os.RemoveAll(tempDir)
		DefaultPath = tempDir

		_, err := Resolve("")

		So(err, ShouldNotBeNil)
	})

	Convey("Passing the provider via CLI should return it without error", t, func() {
		expected := "/bin/bash"
		provider, err := Resolve(expected)

		So(err, ShouldBeNil)
		So(provider, ShouldEqual, expected)

	})

	Convey("Setting the provider via environment variable works", t, func() {
		expected := "/bin/bash"
		os.Setenv("SUMMON_PROVIDER", expected)
		provider, err := Resolve("")
		os.Unsetenv("SUMMON_PROVIDER")

		So(err, ShouldBeNil)
		So(provider, ShouldEqual, expected)

	})

	Convey("Given a provider path", t, func() {
		tempDir, _ := ioutil.TempDir("", "summontest")
		defer os.RemoveAll(tempDir)
		DefaultPath = tempDir

		Convey("If there is 1 executable, return it as the provider", func() {
			f, err := ioutil.TempFile(DefaultPath, "")
			f.Chmod(755)
			provider, err := Resolve("")

			So(err, ShouldBeNil)
			So(provider, ShouldEqual, f.Name())

		})

		Convey("If there are > 1 executables, return an error to user", func() {
			// Create 2 exes in provider path
			ioutil.TempFile(DefaultPath, "")
			ioutil.TempFile(DefaultPath, "")
			_, err := Resolve("")

			So(err, ShouldNotBeNil)
		})
	})
}

func TestCall(t *testing.T) {
	Convey("When I call a provider", t, func() {
		Convey("If it returns exit code 0, return stdout", func() {
			arg := "provider.go"
			out, err := Call("ls", arg)

			So(out, ShouldEqual, arg)
			So(err, ShouldBeNil)
		})
		Convey("If it returns exit code > 0, return stderr", func() {
			out, err := Call("ls", "README.notafile")

			So(out, ShouldBeBlank)
			So(err.Error(), ShouldContainSubstring, "No such file or directory")
		})
	})
}
<|endoftext|>


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

package command

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
)

var memberPeerURLs string

// NewMemberCommand returns the cobra command for "member".
func NewMemberCommand() *cobra.Command {
	mc := &cobra.Command{
		Use:   "member <subcommand>",
		Short: "Membership related commands",
	}

	mc.AddCommand(NewMemberAddCommand())
	mc.AddCommand(NewMemberRemoveCommand())
	mc.AddCommand(NewMemberUpdateCommand())
	mc.AddCommand(NewMemberListCommand())

	return mc
}

// NewMemberAddCommand returns the cobra command for "member add".
func NewMemberAddCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "add <memberName> [options]",
		Short: "Adds a member into the cluster",

		Run: memberAddCommandFunc,
	}

	cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the new member.")

	return cc
}

// NewMemberRemoveCommand returns the cobra command for "member remove".
func NewMemberRemoveCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "remove <memberID>",
		Short: "Removes a member from the cluster",

		Run: memberRemoveCommandFunc,
	}

	return cc
}

// NewMemberUpdateCommand returns the cobra command for "member update".
func NewMemberUpdateCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "update <memberID> [options]",
		Short: "Updates a member in the cluster",

		Run: memberUpdateCommandFunc,
	}

	cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the updated member.")

	return cc
}

// NewMemberListCommand returns the cobra command for "member list".
func NewMemberListCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "list",
		Short: "Lists all members in the cluster",
		Long: `When --write-out is set to simple, this command prints out comma-separated member lists for each endpoint.
The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs.
`,

		Run: memberListCommandFunc,
	}

	return cc
}

// memberAddCommandFunc executes the "member add" command.
func memberAddCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member name not provided."))
	}
	newMemberName := args[0]

	if len(memberPeerURLs) == 0 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided."))
	}

	urls := strings.Split(memberPeerURLs, ",")
	ctx, cancel := commandCtx(cmd)
	cli := mustClientFromCmd(cmd)
	resp, err := cli.MemberAdd(ctx, urls)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}
	newID := resp.Member.ID

	display.MemberAdd(*resp)

	if _, ok := (display).(*simplePrinter); ok {
		ctx, cancel = commandCtx(cmd)
		listResp, err := cli.MemberList(ctx)
		// get latest member list; if there's failover new member might have outdated list
		for {
			if err != nil {
				ExitWithError(ExitError, err)
			}
			if listResp.Header.MemberId == resp.Header.MemberId {
				break
			}
			// quorum get to sync cluster list
			gresp, gerr := cli.Get(ctx, "_")
			if gerr != nil {
				ExitWithError(ExitError, err)
			}
			resp.Header.MemberId = gresp.Header.MemberId
			listResp, err = cli.MemberList(ctx)
		}
		cancel()

		conf := []string{}
		for _, memb := range listResp.Members {
			for _, u := range memb.PeerURLs {
				n := memb.Name
				if memb.ID == newID {
					n = newMemberName
				}
				conf = append(conf, fmt.Sprintf("%s=%s", n, u))
			}
		}

		fmt.Print("\n")
		fmt.Printf("ETCD_NAME=%q\n", newMemberName)
		fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ","))
		fmt.Printf("ETCD_INITIAL_ADVERTISE_PEER_URLS=%q\n", memberPeerURLs)
		fmt.Printf("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n")
	}
}

// memberRemoveCommandFunc executes the "member remove" command.
func memberRemoveCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided"))
	}

	id, err := strconv.ParseUint(args[0], 16, 64)
	if err != nil {
		ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err))
	}

	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).MemberRemove(ctx, id)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}
	display.MemberRemove(id, *resp)
}

// memberUpdateCommandFunc executes the "member update" command.
func memberUpdateCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided"))
	}

	id, err := strconv.ParseUint(args[0], 16, 64)
	if err != nil {
		ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err))
	}

	if len(memberPeerURLs) == 0 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided."))
	}

	urls := strings.Split(memberPeerURLs, ",")

	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).MemberUpdate(ctx, id, urls)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}

	display.MemberUpdate(id, *resp)
}

// memberListCommandFunc executes the "member list" command.
func memberListCommandFunc(cmd *cobra.Command, args []string) {
	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).MemberList(ctx)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}

	display.MemberList(*resp)
}
<commit_msg>etcdctl: Prettier error handling in member add<commit_after>// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package command

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
)

var memberPeerURLs string

// NewMemberCommand returns the cobra command for "member".
func NewMemberCommand() *cobra.Command {
	mc := &cobra.Command{
		Use:   "member <subcommand>",
		Short: "Membership related commands",
	}

	mc.AddCommand(NewMemberAddCommand())
	mc.AddCommand(NewMemberRemoveCommand())
	mc.AddCommand(NewMemberUpdateCommand())
	mc.AddCommand(NewMemberListCommand())

	return mc
}

// NewMemberAddCommand returns the cobra command for "member add".
func NewMemberAddCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "add <memberName> [options]",
		Short: "Adds a member into the cluster",

		Run: memberAddCommandFunc,
	}

	cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the new member.")

	return cc
}

// NewMemberRemoveCommand returns the cobra command for "member remove".
func NewMemberRemoveCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "remove <memberID>",
		Short: "Removes a member from the cluster",

		Run: memberRemoveCommandFunc,
	}

	return cc
}

// NewMemberUpdateCommand returns the cobra command for "member update".
func NewMemberUpdateCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "update <memberID> [options]",
		Short: "Updates a member in the cluster",

		Run: memberUpdateCommandFunc,
	}

	cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the updated member.")

	return cc
}

// NewMemberListCommand returns the cobra command for "member list".
func NewMemberListCommand() *cobra.Command {
	cc := &cobra.Command{
		Use:   "list",
		Short: "Lists all members in the cluster",
		Long: `When --write-out is set to simple, this command prints out comma-separated member lists for each endpoint.
The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs.
`,

		Run: memberListCommandFunc,
	}

	return cc
}

// memberAddCommandFunc executes the "member add" command.
func memberAddCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member name not provided."))
	}
	if len(args) > 1 {
		errorstring := "too many arguments"
		for _, v := range args {
			if strings.HasPrefix(strings.ToLower(v), "http"){
				errorstring += ", did you mean \"--peer-urls " + v + "\""
			}
		}
		ExitWithError(ExitBadArgs, fmt.Errorf(errorstring))
	}
	newMemberName := args[0]

	if len(memberPeerURLs) == 0 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided."))
	}

	urls := strings.Split(memberPeerURLs, ",")
	ctx, cancel := commandCtx(cmd)
	cli := mustClientFromCmd(cmd)
	resp, err := cli.MemberAdd(ctx, urls)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}
	newID := resp.Member.ID

	display.MemberAdd(*resp)

	if _, ok := (display).(*simplePrinter); ok {
		ctx, cancel = commandCtx(cmd)
		listResp, err := cli.MemberList(ctx)
		// get latest member list; if there's failover new member might have outdated list
		for {
			if err != nil {
				ExitWithError(ExitError, err)
			}
			if listResp.Header.MemberId == resp.Header.MemberId {
				break
			}
			// quorum get to sync cluster list
			gresp, gerr := cli.Get(ctx, "_")
			if gerr != nil {
				ExitWithError(ExitError, err)
			}
			resp.Header.MemberId = gresp.Header.MemberId
			listResp, err = cli.MemberList(ctx)
		}
		cancel()

		conf := []string{}
		for _, memb := range listResp.Members {
			for _, u := range memb.PeerURLs {
				n := memb.Name
				if memb.ID == newID {
					n = newMemberName
				}
				conf = append(conf, fmt.Sprintf("%s=%s", n, u))
			}
		}

		fmt.Print("\n")
		fmt.Printf("ETCD_NAME=%q\n", newMemberName)
		fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ","))
		fmt.Printf("ETCD_INITIAL_ADVERTISE_PEER_URLS=%q\n", memberPeerURLs)
		fmt.Printf("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n")
	}
}

// memberRemoveCommandFunc executes the "member remove" command.
func memberRemoveCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided"))
	}

	id, err := strconv.ParseUint(args[0], 16, 64)
	if err != nil {
		ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err))
	}

	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).MemberRemove(ctx, id)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}
	display.MemberRemove(id, *resp)
}

// memberUpdateCommandFunc executes the "member update" command.
func memberUpdateCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided"))
	}

	id, err := strconv.ParseUint(args[0], 16, 64)
	if err != nil {
		ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err))
	}

	if len(memberPeerURLs) == 0 {
		ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided."))
	}

	urls := strings.Split(memberPeerURLs, ",")

	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).MemberUpdate(ctx, id, urls)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}

	display.MemberUpdate(id, *resp)
}

// memberListCommandFunc executes the "member list" command.
func memberListCommandFunc(cmd *cobra.Command, args []string) {
	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).MemberList(ctx)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}

	display.MemberList(*resp)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"regexp"
)

func main() {
	flag.Parse()
	filenames := flag.Args()
	fset := token.NewFileSet()
	pkgMap, firstErr := parser.ParseFiles(fset, filenames, 0)
	if firstErr != nil {
		fmt.Fprintf(os.Stderr, "Error while parsing: %v\n", firstErr)
	}

	v := NewNodeChecker(fset)
	v.InterfaceName = regexp.MustCompile("I[A-Z][A-Za-z]+")

	for _, pkg := range pkgMap {
		ast.Walk(v, pkg)
	}
}

type NodeChecker struct {
	fset          *token.FileSet
	InterfaceName *regexp.Regexp
}

func NewNodeChecker(fset *token.FileSet) *NodeChecker {
	return &NodeChecker{
		fset: fset,
	}
}

func (v *NodeChecker) Visit(node ast.Node) (w ast.Visitor) {
	switch n := node.(type) {
	case *ast.TypeSpec:
		v.checkTypeName(n)
	}
	return v
}

// report displays a message about a particular position in the fileset.
func (v *NodeChecker) report(pos token.Pos, format string, args ...interface{}) {
	position := v.fset.Position(pos)
	allArgs := make([]interface{}, len(args)+3)

	allArgs[0] = position.Filename
	allArgs[1] = position.Line
	allArgs[2] = position.Column
	copy(allArgs[3:], args)

	fmt.Fprintf(
		os.Stderr,
		"%s:%d:%d: " + format,
		allArgs...)
}

func (v *NodeChecker) checkTypeName(typeSpec *ast.TypeSpec) {
	name := typeSpec.Name.Name
	switch t := typeSpec.Type.(type) {
	case *ast.InterfaceType:
		if !v.InterfaceName.MatchString(name) {
			v.report(typeSpec.Name.NamePos, "Bad name for interface %q\n", name)
		}
	}
}
<commit_msg>gofmt run.<commit_after>package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"regexp"
)

func main() {
	flag.Parse()
	filenames := flag.Args()
	fset := token.NewFileSet()
	pkgMap, firstErr := parser.ParseFiles(fset, filenames, 0)
	if firstErr != nil {
		fmt.Fprintf(os.Stderr, "Error while parsing: %v\n", firstErr)
	}

	v := NewNodeChecker(fset)
	v.InterfaceName = regexp.MustCompile("I[A-Z][A-Za-z]+")

	for _, pkg := range pkgMap {
		ast.Walk(v, pkg)
	}
}

type NodeChecker struct {
	fset          *token.FileSet
	InterfaceName *regexp.Regexp
}

func NewNodeChecker(fset *token.FileSet) *NodeChecker {
	return &NodeChecker{
		fset: fset,
	}
}

func (v *NodeChecker) Visit(node ast.Node) (w ast.Visitor) {
	switch n := node.(type) {
	case *ast.TypeSpec:
		v.checkTypeName(n)
	}
	return v
}

// report displays a message about a particular position in the fileset.
func (v *NodeChecker) report(pos token.Pos, format string, args ...interface{}) {
	position := v.fset.Position(pos)
	allArgs := make([]interface{}, len(args)+3)

	allArgs[0] = position.Filename
	allArgs[1] = position.Line
	allArgs[2] = position.Column
	copy(allArgs[3:], args)

	fmt.Fprintf(
		os.Stderr,
		"%s:%d:%d: "+format,
		allArgs...)
}

func (v *NodeChecker) checkTypeName(typeSpec *ast.TypeSpec) {
	name := typeSpec.Name.Name
	switch t := typeSpec.Type.(type) {
	case *ast.InterfaceType:
		if !v.InterfaceName.MatchString(name) {
			v.report(typeSpec.Name.NamePos, "Bad name for interface %q\n", name)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package help

import (
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/labels"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/plugins"
)

const pluginName = "help"

var (
	helpRe                     = regexp.MustCompile(`(?mi)^/help\s*$`)
	helpRemoveRe               = regexp.MustCompile(`(?mi)^/remove-help\s*$`)
	helpGoodFirstIssueRe       = regexp.MustCompile(`(?mi)^/good-first-issue\s*$`)
	helpGoodFirstIssueRemoveRe = regexp.MustCompile(`(?mi)^/remove-good-first-issue\s*$`)
	helpGuidelinesURL          = "https://git.k8s.io/community/contributors/devel/help-wanted.md"
	helpMsgPruneMatch          = "This request has been marked as needing help from a contributor."
	helpMsg                    = `
	This request has been marked as needing help from a contributor.

Please ensure the request meets the requirements listed [here](` + helpGuidelinesURL + `).

If this request no longer meets these requirements, the label can be removed
by commenting with the ` + "`/remove-help`" + ` command.
`
	goodFirstIssueMsgPruneMatch = "This request has been marked as suitable for new contributors."
	goodFirstIssueMsg           = `
	This request has been marked as suitable for new contributors.

Please ensure the request meets the requirements listed [here](` + helpGuidelinesURL + "#good-first-issue" + `).

If this request no longer meets these requirements, the label can be removed
by commenting with the ` + "`/remove-good-first-issue`" + ` command.
`
)

func init() {
	plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment, helpProvider)
}

func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) {
	// The Config field is omitted because this plugin is not configurable.
	pluginHelp := &pluginhelp.PluginHelp{
		Description: "The help plugin provides commands that add or remove the '" + labels.Help + "' and the '" + labels.GoodFirstIssue + "' labels from issues.",
	}
	pluginHelp.AddCommand(pluginhelp.Command{
		Usage:       "/[remove-](help|good-first-issue)",
		Description: "Applies or removes the '" + labels.Help + "' and '" + labels.GoodFirstIssue + "' labels to an issue.",
		Featured:    false,
		WhoCanUse:   "Anyone can trigger this command on a PR.",
		Examples:    []string{"/help", "/remove-help", "/good-first-issue", "/remove-good-first-issue"},
	})
	return pluginHelp, nil
}

type githubClient interface {
	BotName() (string, error)
	CreateComment(owner, repo string, number int, comment string) error
	AddLabel(owner, repo string, number int, label string) error
	RemoveLabel(owner, repo string, number int, label string) error
	GetIssueLabels(org, repo string, number int) ([]github.Label, error)
}

type commentPruner interface {
	PruneComments(shouldPrune func(github.IssueComment) bool)
}

func handleGenericComment(pc plugins.Agent, e github.GenericCommentEvent) error {
	cp, err := pc.CommentPruner()
	if err != nil {
		return err
	}
	return handle(pc.GitHubClient, pc.Logger, cp, &e)
}

func handle(gc githubClient, log *logrus.Entry, cp commentPruner, e *github.GenericCommentEvent) error {
	// Only consider open issues and new comments.
	if e.IsPR || e.IssueState != "open" || e.Action != github.GenericCommentActionCreated {
		return nil
	}

	org := e.Repo.Owner.Login
	repo := e.Repo.Name
	commentAuthor := e.User.Login

	// Determine if the issue has the help and the good-first-issue label
	issueLabels, err := gc.GetIssueLabels(org, repo, e.Number)
	if err != nil {
		log.WithError(err).Errorf("Failed to get issue labels.")
	}
	hasHelp := github.HasLabel(labels.Help, issueLabels)
	hasGoodFirstIssue := github.HasLabel(labels.GoodFirstIssue, issueLabels)

	// If PR has help label and we're asking for it to be removed, remove label
	if hasHelp && helpRemoveRe.MatchString(e.Body) {
		if err := gc.RemoveLabel(org, repo, e.Number, labels.Help); err != nil {
			log.WithError(err).Errorf("Github failed to remove the following label: %s", labels.Help)
		}

		botName, err := gc.BotName()
		if err != nil {
			log.WithError(err).Errorf("Failed to get bot name.")
		}
		cp.PruneComments(shouldPrune(log, botName, helpMsgPruneMatch))

		// if it has the good-first-issue label, remove it too
		if hasGoodFirstIssue {
			if err := gc.RemoveLabel(org, repo, e.Number, labels.GoodFirstIssue); err != nil {
				log.WithError(err).Errorf("Github failed to remove the following label: %s", labels.GoodFirstIssue)
			}
			cp.PruneComments(shouldPrune(log, botName, goodFirstIssueMsgPruneMatch))
		}

		return nil
	}

	// If PR does not have the good-first-issue label and we are asking for it to be added,
	// add both the good-first-issue and help labels
	if !hasGoodFirstIssue && helpGoodFirstIssueRe.MatchString(e.Body) {
		if err := gc.CreateComment(org, repo, e.Number, plugins.FormatResponseRaw(e.Body, e.IssueHTMLURL, commentAuthor, goodFirstIssueMsg)); err != nil {
			log.WithError(err).Errorf("Failed to create comment \"%s\".", goodFirstIssueMsg)
		}

		if err := gc.AddLabel(org, repo, e.Number, labels.GoodFirstIssue); err != nil {
			log.WithError(err).Errorf("Github failed to add the following label: %s", labels.GoodFirstIssue)
		}

		if !hasHelp {
			if err := gc.AddLabel(org, repo, e.Number, labels.Help); err != nil {
				log.WithError(err).Errorf("Github failed to add the following label: %s", labels.Help)
			}
		}

		return nil
	}

	// If PR does not have the help label and we're asking it to be added,
	// add the label
	if !hasHelp && helpRe.MatchString(e.Body) {
		if err := gc.CreateComment(org, repo, e.Number, plugins.FormatResponseRaw(e.Body, e.IssueHTMLURL, commentAuthor, helpMsg)); err != nil {
			log.WithError(err).Errorf("Failed to create comment \"%s\".", helpMsg)
		}
		if err := gc.AddLabel(org, repo, e.Number, labels.Help); err != nil {
			log.WithError(err).Errorf("Github failed to add the following label: %s", labels.Help)
		}

		return nil
	}

	// If PR has good-first-issue label and we are asking for it to be removed,
	// remove just the good-first-issue label
	if hasGoodFirstIssue && helpGoodFirstIssueRemoveRe.MatchString(e.Body) {
		if err := gc.RemoveLabel(org, repo, e.Number, labels.GoodFirstIssue); err != nil {
			log.WithError(err).Errorf("Github failed to remove the following label: %s", labels.GoodFirstIssue)
		}

		botName, err := gc.BotName()
		if err != nil {
			log.WithError(err).Errorf("Failed to get bot name.")
		}
		cp.PruneComments(shouldPrune(log, botName, goodFirstIssueMsgPruneMatch))

		return nil
	}

	return nil
}

// shouldPrune finds comments left by this plugin.
func shouldPrune(log *logrus.Entry, botName, msgPruneMatch string) func(github.IssueComment) bool {
	return func(comment github.IssueComment) bool {
		if comment.User.Login != botName {
			return false
		}
		return strings.Contains(comment.Body, msgPruneMatch)
	}
}
<commit_msg>Updating URL<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package help

import (
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/labels"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/plugins"
)

const pluginName = "help"

var (
	helpRe                     = regexp.MustCompile(`(?mi)^/help\s*$`)
	helpRemoveRe               = regexp.MustCompile(`(?mi)^/remove-help\s*$`)
	helpGoodFirstIssueRe       = regexp.MustCompile(`(?mi)^/good-first-issue\s*$`)
	helpGoodFirstIssueRemoveRe = regexp.MustCompile(`(?mi)^/remove-good-first-issue\s*$`)
	helpGuidelinesURL          = "https://git.k8s.io/community/contributors/guide/help-wanted.md"
	helpMsgPruneMatch          = "This request has been marked as needing help from a contributor."
	helpMsg                    = `
	This request has been marked as needing help from a contributor.

Please ensure the request meets the requirements listed [here](` + helpGuidelinesURL + `).

If this request no longer meets these requirements, the label can be removed
by commenting with the ` + "`/remove-help`" + ` command.
`
	goodFirstIssueMsgPruneMatch = "This request has been marked as suitable for new contributors."
	goodFirstIssueMsg           = `
	This request has been marked as suitable for new contributors.

Please ensure the request meets the requirements listed [here](` + helpGuidelinesURL + "#good-first-issue" + `).

If this request no longer meets these requirements, the label can be removed
by commenting with the ` + "`/remove-good-first-issue`" + ` command.
`
)

func init() {
	plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment, helpProvider)
}

func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) {
	// The Config field is omitted because this plugin is not configurable.
	pluginHelp := &pluginhelp.PluginHelp{
		Description: "The help plugin provides commands that add or remove the '" + labels.Help + "' and the '" + labels.GoodFirstIssue + "' labels from issues.",
	}
	pluginHelp.AddCommand(pluginhelp.Command{
		Usage:       "/[remove-](help|good-first-issue)",
		Description: "Applies or removes the '" + labels.Help + "' and '" + labels.GoodFirstIssue + "' labels to an issue.",
		Featured:    false,
		WhoCanUse:   "Anyone can trigger this command on a PR.",
		Examples:    []string{"/help", "/remove-help", "/good-first-issue", "/remove-good-first-issue"},
	})
	return pluginHelp, nil
}

type githubClient interface {
	BotName() (string, error)
	CreateComment(owner, repo string, number int, comment string) error
	AddLabel(owner, repo string, number int, label string) error
	RemoveLabel(owner, repo string, number int, label string) error
	GetIssueLabels(org, repo string, number int) ([]github.Label, error)
}

type commentPruner interface {
	PruneComments(shouldPrune func(github.IssueComment) bool)
}

func handleGenericComment(pc plugins.Agent, e github.GenericCommentEvent) error {
	cp, err := pc.CommentPruner()
	if err != nil {
		return err
	}
	return handle(pc.GitHubClient, pc.Logger, cp, &e)
}

func handle(gc githubClient, log *logrus.Entry, cp commentPruner, e *github.GenericCommentEvent) error {
	// Only consider open issues and new comments.
	if e.IsPR || e.IssueState != "open" || e.Action != github.GenericCommentActionCreated {
		return nil
	}

	org := e.Repo.Owner.Login
	repo := e.Repo.Name
	commentAuthor := e.User.Login

	// Determine if the issue has the help and the good-first-issue label
	issueLabels, err := gc.GetIssueLabels(org, repo, e.Number)
	if err != nil {
		log.WithError(err).Errorf("Failed to get issue labels.")
	}
	hasHelp := github.HasLabel(labels.Help, issueLabels)
	hasGoodFirstIssue := github.HasLabel(labels.GoodFirstIssue, issueLabels)

	// If PR has help label and we're asking for it to be removed, remove label
	if hasHelp && helpRemoveRe.MatchString(e.Body) {
		if err := gc.RemoveLabel(org, repo, e.Number, labels.Help); err != nil {
			log.WithError(err).Errorf("Github failed to remove the following label: %s", labels.Help)
		}

		botName, err := gc.BotName()
		if err != nil {
			log.WithError(err).Errorf("Failed to get bot name.")
		}
		cp.PruneComments(shouldPrune(log, botName, helpMsgPruneMatch))

		// if it has the good-first-issue label, remove it too
		if hasGoodFirstIssue {
			if err := gc.RemoveLabel(org, repo, e.Number, labels.GoodFirstIssue); err != nil {
				log.WithError(err).Errorf("Github failed to remove the following label: %s", labels.GoodFirstIssue)
			}
			cp.PruneComments(shouldPrune(log, botName, goodFirstIssueMsgPruneMatch))
		}

		return nil
	}

	// If PR does not have the good-first-issue label and we are asking for it to be added,
	// add both the good-first-issue and help labels
	if !hasGoodFirstIssue && helpGoodFirstIssueRe.MatchString(e.Body) {
		if err := gc.CreateComment(org, repo, e.Number, plugins.FormatResponseRaw(e.Body, e.IssueHTMLURL, commentAuthor, goodFirstIssueMsg)); err != nil {
			log.WithError(err).Errorf("Failed to create comment \"%s\".", goodFirstIssueMsg)
		}

		if err := gc.AddLabel(org, repo, e.Number, labels.GoodFirstIssue); err != nil {
			log.WithError(err).Errorf("Github failed to add the following label: %s", labels.GoodFirstIssue)
		}

		if !hasHelp {
			if err := gc.AddLabel(org, repo, e.Number, labels.Help); err != nil {
				log.WithError(err).Errorf("Github failed to add the following label: %s", labels.Help)
			}
		}

		return nil
	}

	// If PR does not have the help label and we're asking it to be added,
	// add the label
	if !hasHelp && helpRe.MatchString(e.Body) {
		if err := gc.CreateComment(org, repo, e.Number, plugins.FormatResponseRaw(e.Body, e.IssueHTMLURL, commentAuthor, helpMsg)); err != nil {
			log.WithError(err).Errorf("Failed to create comment \"%s\".", helpMsg)
		}
		if err := gc.AddLabel(org, repo, e.Number, labels.Help); err != nil {
			log.WithError(err).Errorf("Github failed to add the following label: %s", labels.Help)
		}

		return nil
	}

	// If PR has good-first-issue label and we are asking for it to be removed,
	// remove just the good-first-issue label
	if hasGoodFirstIssue && helpGoodFirstIssueRemoveRe.MatchString(e.Body) {
		if err := gc.RemoveLabel(org, repo, e.Number, labels.GoodFirstIssue); err != nil {
			log.WithError(err).Errorf("Github failed to remove the following label: %s", labels.GoodFirstIssue)
		}

		botName, err := gc.BotName()
		if err != nil {
			log.WithError(err).Errorf("Failed to get bot name.")
		}
		cp.PruneComments(shouldPrune(log, botName, goodFirstIssueMsgPruneMatch))

		return nil
	}

	return nil
}

// shouldPrune finds comments left by this plugin.
func shouldPrune(log *logrus.Entry, botName, msgPruneMatch string) func(github.IssueComment) bool {
	return func(comment github.IssueComment) bool {
		if comment.User.Login != botName {
			return false
		}
		return strings.Contains(comment.Body, msgPruneMatch)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Peter Mrekaj. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.txt file.

package ptypes

import (
	"math"
	"testing"
)

func TestIntWeight(t *testing.T) {
	for _, test := range []struct {
		in   uint64
		want int
	}{
		{0, 0},
		{1, 1},
		{2, 1},
		{3, 2},
		{4, 1},
		{5, 2},
		{6, 2},
		{7, 3},
		{8, 1},
		{9, 2},
		{math.MaxUint64, 64},
	} {
		if got := IntWeight(test.in); got != test.want {
			t.Errorf("IntWeight(%d) = %d; want %d", test.in, got, test.want)
		}
	}
}

func BenchmarkIntWeight(b *testing.B) {
	for i := 0; i < b.N; i++ {
		IntWeight(uint64(i))
	}
}

func TestClosestInt(t *testing.T) {
	for _, test := range []struct {
		in   uint64
		want uint64
		ok   bool
	}{
		{0x0000000000000000, 0x0, false},
		{0xffffffffffffffff, 0x0, false},
		{0x01, 0x02, true},
		{0x03, 0x05, true},
		{0x04, 0x02, true},
		{0x05, 0x06, true},
		{0x06, 0x05, true},
		{0x07, 0x0b, true},
		{0x08, 0x04, true},
		{0x09, 0x0a, true},
		{0x0a, 0x09, true},
		{0x0b, 0x0d, true},
		{0x0c, 0x0a, true},
		{0x0d, 0x0e, true},
		{0x0e, 0x0d, true},
		{0x0f, 0x17, true},
	} {
		if got, ok := ClosestInt(test.in); got != test.want || ok != test.ok {
			t.Errorf("ClosestInt(%#x) = %#x, %t; want %#x, %t", test.in, got, ok, test.want, test.ok)
		}
	}
}

func BenchmarkClosestInt(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ClosestInt(uint64(i))
	}
}
<commit_msg>Add test case to TestClosestInt for number 2<commit_after>// Copyright (c) 2015, Peter Mrekaj. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.txt file.

package ptypes

import (
	"math"
	"testing"
)

func TestIntWeight(t *testing.T) {
	for _, test := range []struct {
		in   uint64
		want int
	}{
		{0, 0},
		{1, 1},
		{2, 1},
		{3, 2},
		{4, 1},
		{5, 2},
		{6, 2},
		{7, 3},
		{8, 1},
		{9, 2},
		{math.MaxUint64, 64},
	} {
		if got := IntWeight(test.in); got != test.want {
			t.Errorf("IntWeight(%d) = %d; want %d", test.in, got, test.want)
		}
	}
}

func BenchmarkIntWeight(b *testing.B) {
	for i := 0; i < b.N; i++ {
		IntWeight(uint64(i))
	}
}

func TestClosestInt(t *testing.T) {
	for _, test := range []struct {
		in   uint64
		want uint64
		ok   bool
	}{
		{0x0000000000000000, 0x0, false},
		{0xffffffffffffffff, 0x0, false},
		{0x01, 0x02, true},
		{0x02, 0x01, true},
		{0x03, 0x05, true},
		{0x04, 0x02, true},
		{0x05, 0x06, true},
		{0x06, 0x05, true},
		{0x07, 0x0b, true},
		{0x08, 0x04, true},
		{0x09, 0x0a, true},
		{0x0a, 0x09, true},
		{0x0b, 0x0d, true},
		{0x0c, 0x0a, true},
		{0x0d, 0x0e, true},
		{0x0e, 0x0d, true},
		{0x0f, 0x17, true},
	} {
		if got, ok := ClosestInt(test.in); got != test.want || ok != test.ok {
			t.Errorf("ClosestInt(%#x) = %#x, %t; want %#x, %t", test.in, got, ok, test.want, test.ok)
		}
	}
}

func BenchmarkClosestInt(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ClosestInt(uint64(i))
	}
}
<|endoftext|>


<commit_before>package event

/*
import (
	"errors"
	"github.com/stretchr/testify/assert"
	"testing"
)

type strStruct struct{}

func (s strStruct) String() string {
	return "STRING"
}

func Test_deStruct_string_interface(t *testing.T) {
	str := &strStruct{}

	fields := map[string]interface{}{}
	strCopy := deStruct(str, "parent", fields)

	assert.Equal(t, "STRING", strCopy)
	assert.Equal(t, "STRING", fields["parent"])
}

////////////////////////////////////////

type errStruct struct{}

func (e errStruct) Error() string {
	return "ERROR"
}

func Test_deStruct_error(t *testing.T) {
	err := &errStruct{}

	fields := map[string]interface{}{}
	errCopy := deStruct(err, "parent", fields)

	assert.Equal(t, "ERROR", errCopy)
	assert.Equal(t, "ERROR", fields["parent"])
}

////////////////////////////////////////

type errStructWithExports struct {
	Attribute bool
}

func (e errStructWithExports) Error() string {
	return "ERROR"
}

// Check that if we have a struct with exported attributes, and that satisfies the error interface, that we expose the attributes, and then the error message as an `Error` attribute
func Test_deStruct_errorWithExports(t *testing.T) {
	err := &errStructWithExports{true}

	fields := map[string]interface{}{}
	errCopy := deStruct(err, "parent", fields)

	errCopyMap, ok := errCopy.(map[string]interface{})
	if assert.True(t, ok) {
		assert.Equal(t, "ERROR", errCopyMap["Error"])
	}

	assert.Equal(t, "ERROR", fields["parent.Error"])
}

////////////////////////////////////////

// Check that if we have a struct where the Error() is on the pointer, that we still work

func Test_deStruct_errorNonPointer(t *testing.T) {
	err := errors.New("ERROR")

	fields := map[string]interface{}{}
	errCopy := deStruct(err, "parent", fields)

	errCopyMap, ok := errCopy.(map[string]interface{})
	if assert.True(t, ok) {
		assert.Equal(t, "ERROR", errCopyMap["Error"])
	}

	assert.Equal(t, "ERROR", fields["parent.Error"])
}
*/
<commit_msg>remove event_test from d52204e<commit_after><|endoftext|>


<commit_before>// +build linux

package qemu

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
	hasVsock   bool
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	qemuPidFile string
	qemuLogFile *QemuLogFile
	cpus        int
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath(QEMU_SYSTEM_EXE)
	if err != nil {
		return nil
	}

	var hasVsock bool
	_, err = exec.Command("/sbin/modprobe", "vhost_vsock").Output()
	if err == nil {
		hasVsock = true
	}

	return &QemuDriver{
		executable: cmd,
		hasVsock:   hasVsock,
	}
}

func (qd *QemuDriver) Name() string {
	return "qemu"
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	if _, err := os.Stat(QemuLogDir); os.IsNotExist(err) {
		os.Mkdir(QemuLogDir, 0755)
	}

	logFile := filepath.Join(QemuLogDir, homeDir[strings.Index(homeDir, "vm-"):len(homeDir)-1]+".log")
	if _, err := os.Create(logFile); err != nil {
		glog.Errorf("create qemu log file failed: %v", err)
	}
	qemuLogFile := &QemuLogFile{
		Name:   logFile,
		Offset: 0,
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: filepath.Join(homeDir, QmpSockName),
		qemuPidFile: filepath.Join(homeDir, QemuPidFile),
		qemuLogFile: qemuLogFile,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var log QemuLogFile
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case float64:
			proc, err = os.FindProcess(int(p.(float64)))
			if err != nil {
				return nil, err
			}
			// test if process has already exited
			if err = proc.Signal(syscall.Signal(0)); err != nil {
				return nil, fmt.Errorf("signal 0 on Qemu process(%d) failed: %v", int(p.(float64)), err)
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	l, ok := persisted["log"]
	if !ok {
		return nil, errors.New("cannot read the qemu log filename info from persist info")
	}
	if bytes, err := json.Marshal(l); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	} else if err = json.Unmarshal(bytes, &log); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		qemuLogFile: &log,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"log":        *qc.qemuLogFile,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(ctx, qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.qmp <- &QmpQuit{}
	qc.wdt <- "quit"
	<-qc.waitQmp
	qc.qemuLogFile.Close()
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, pause bool) error {
	commands := make([]*QmpCommand, 1)

	if pause {
		commands[0] = &QmpCommand{
			Execute: "stop",
		}
	} else {
		commands[0] = &QmpCommand{
			Execute: "cont",
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.DiskDescriptor, result chan<- hypervisor.VmEvent) {
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId
	readonly := blockInfo.ReadOnly

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(ctx, qc, filename, format, id, readonly, result)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.DiskDescriptor, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(ctx, qc, id, callback, result)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo, result chan<- hypervisor.VmEvent) {
	var (
		fd       int = -1
		err      error
		waitChan chan hypervisor.VmEvent = make(chan hypervisor.VmEvent, 1)
	)

	if ctx.Boot.EnableVhostUser {
		err = GetVhostUserPort(host.Device, host.Bridge, ctx.HomeDir, host.Options)
	} else {
		fd, err = GetTapFd(host.Device, host.Bridge, host.Options)
	}

	if err != nil {
		glog.Errorf("fail to create nic for sandbox: %v, %v", ctx.Id, err)
		result <- &hypervisor.DeviceFailed{
			Session: nil,
		}
		return
	}

	go func() {
		// close tap file if necessary
		ev, ok := <-waitChan
		syscall.Close(fd)
		if !ok {
			close(result)
		} else {
			result <- ev
		}
	}()
	newNetworkAddSession(ctx, qc, fd, host, guest, waitChan)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	newNetworkDelSession(ctx, qc, n.NewName, callback, result)
}

func (qc *QemuContext) SetCpus(ctx *hypervisor.VmContext, cpus int) error {
	currcpus := qc.cpus

	if cpus < currcpus {
		return fmt.Errorf("can't reduce cpus number from %d to %d", currcpus, cpus)
	} else if cpus == currcpus {
		return nil
	}

	commands := make([]*QmpCommand, cpus-currcpus)
	for id := currcpus; id < cpus; id++ {
		commands[id-currcpus] = &QmpCommand{
			Execute: "cpu-add",
			Arguments: map[string]interface{}{
				"id": id,
			},
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			if err == nil {
				qc.cpus = cpus
			}
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int) error {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}
	return <-result
}

func (qc *QemuContext) Save(ctx *hypervisor.VmContext, path string) error {
	commands := make([]*QmpCommand, 2)

	commands[0] = &QmpCommand{
		Execute: "migrate-set-capabilities",
		Arguments: map[string]interface{}{
			"capabilities": []map[string]interface{}{
				{
					"capability": "bypass-shared-memory",
					"state":      true,
				},
			},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "migrate",
		Arguments: map[string]interface{}{
			"uri": fmt.Sprintf("exec:cat>%s", path),
		},
	}
	if !ctx.Boot.BootToBeTemplate {
		commands = commands[1:]
	}

	result := make(chan error, 1)
	// TODO: use query-migrate to query until completed
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}

	return <-result
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuDriver) SupportVmSocket() bool {
	return qc.hasVsock
}
<commit_msg>qemu: only close tap fd on addnic failure<commit_after>// +build linux

package qemu

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
	hasVsock   bool
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	qemuPidFile string
	qemuLogFile *QemuLogFile
	cpus        int
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath(QEMU_SYSTEM_EXE)
	if err != nil {
		return nil
	}

	var hasVsock bool
	_, err = exec.Command("/sbin/modprobe", "vhost_vsock").Output()
	if err == nil {
		hasVsock = true
	}

	return &QemuDriver{
		executable: cmd,
		hasVsock:   hasVsock,
	}
}

func (qd *QemuDriver) Name() string {
	return "qemu"
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	if _, err := os.Stat(QemuLogDir); os.IsNotExist(err) {
		os.Mkdir(QemuLogDir, 0755)
	}

	logFile := filepath.Join(QemuLogDir, homeDir[strings.Index(homeDir, "vm-"):len(homeDir)-1]+".log")
	if _, err := os.Create(logFile); err != nil {
		glog.Errorf("create qemu log file failed: %v", err)
	}
	qemuLogFile := &QemuLogFile{
		Name:   logFile,
		Offset: 0,
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: filepath.Join(homeDir, QmpSockName),
		qemuPidFile: filepath.Join(homeDir, QemuPidFile),
		qemuLogFile: qemuLogFile,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var log QemuLogFile
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case float64:
			proc, err = os.FindProcess(int(p.(float64)))
			if err != nil {
				return nil, err
			}
			// test if process has already exited
			if err = proc.Signal(syscall.Signal(0)); err != nil {
				return nil, fmt.Errorf("signal 0 on Qemu process(%d) failed: %v", int(p.(float64)), err)
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	l, ok := persisted["log"]
	if !ok {
		return nil, errors.New("cannot read the qemu log filename info from persist info")
	}
	if bytes, err := json.Marshal(l); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	} else if err = json.Unmarshal(bytes, &log); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		qemuLogFile: &log,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"log":        *qc.qemuLogFile,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(ctx, qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.qmp <- &QmpQuit{}
	qc.wdt <- "quit"
	<-qc.waitQmp
	qc.qemuLogFile.Close()
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, pause bool) error {
	commands := make([]*QmpCommand, 1)

	if pause {
		commands[0] = &QmpCommand{
			Execute: "stop",
		}
	} else {
		commands[0] = &QmpCommand{
			Execute: "cont",
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.DiskDescriptor, result chan<- hypervisor.VmEvent) {
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId
	readonly := blockInfo.ReadOnly

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(ctx, qc, filename, format, id, readonly, result)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.DiskDescriptor, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(ctx, qc, id, callback, result)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo, result chan<- hypervisor.VmEvent) {
	var (
		fd       int = -1
		err      error
		waitChan chan hypervisor.VmEvent = make(chan hypervisor.VmEvent, 1)
	)

	if ctx.Boot.EnableVhostUser {
		err = GetVhostUserPort(host.Device, host.Bridge, ctx.HomeDir, host.Options)
	} else {
		fd, err = GetTapFd(host.Device, host.Bridge, host.Options)
	}

	if err != nil {
		glog.Errorf("fail to create nic for sandbox: %v, %v", ctx.Id, err)
		result <- &hypervisor.DeviceFailed{
			Session: nil,
		}
		return
	}

	go func() {
		// close tap file if necessary
		ev, ok := <-waitChan
		if !ok {
			syscall.Close(fd)
			close(result)
		} else {
			if _, ok := ev.(*hypervisor.DeviceFailed); ok {
				syscall.Close(fd)
			}
			result <- ev
		}
	}()
	newNetworkAddSession(ctx, qc, fd, host, guest, waitChan)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	newNetworkDelSession(ctx, qc, n.NewName, callback, result)
}

func (qc *QemuContext) SetCpus(ctx *hypervisor.VmContext, cpus int) error {
	currcpus := qc.cpus

	if cpus < currcpus {
		return fmt.Errorf("can't reduce cpus number from %d to %d", currcpus, cpus)
	} else if cpus == currcpus {
		return nil
	}

	commands := make([]*QmpCommand, cpus-currcpus)
	for id := currcpus; id < cpus; id++ {
		commands[id-currcpus] = &QmpCommand{
			Execute: "cpu-add",
			Arguments: map[string]interface{}{
				"id": id,
			},
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			if err == nil {
				qc.cpus = cpus
			}
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int) error {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}
	return <-result
}

func (qc *QemuContext) Save(ctx *hypervisor.VmContext, path string) error {
	commands := make([]*QmpCommand, 2)

	commands[0] = &QmpCommand{
		Execute: "migrate-set-capabilities",
		Arguments: map[string]interface{}{
			"capabilities": []map[string]interface{}{
				{
					"capability": "bypass-shared-memory",
					"state":      true,
				},
			},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "migrate",
		Arguments: map[string]interface{}{
			"uri": fmt.Sprintf("exec:cat>%s", path),
		},
	}
	if !ctx.Boot.BootToBeTemplate {
		commands = commands[1:]
	}

	result := make(chan error, 1)
	// TODO: use query-migrate to query until completed
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}

	return <-result
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuDriver) SupportVmSocket() bool {
	return qc.hasVsock
}
<|endoftext|>


<commit_before>package b2

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
)

func Test_createB2_Success(t *testing.T) {
	s, c := setupRequest(200, `{"accountId":"1","authorizationToken":"1","apiUrl":"/","downloadUrl":"/"}`)
	defer s.Close()

	client := &client{Protocol: "http", Client: c}

	b, err := createB2("1", "1", client)
	if err != nil {
		t.Fatalf("Expected no error, instead got %s", err)
	}

	if b.AccountID != "1" {
		t.Errorf(`Expected AccountID to be "1", instead got %s`, b.AccountID)
	}
	if b.AuthorizationToken != "1" {
		t.Errorf(`Expected AuthorizationToken to be "1", instead got %s`, b.AuthorizationToken)
	}
	if b.ApiUrl != "/" {
		t.Errorf(`Expected AccountID to be "/", instead got %s`, b.ApiUrl)
	}
	if b.DownloadUrl != "/" {
		t.Errorf(`Expected AccountID to be "/", instead got %s`, b.DownloadUrl)
	}
}

func Test_createB2_HasAuth(t *testing.T) {
	reqChan := make(chan *http.Request, 1)
	s, c := setupMockJsonServer(200, "", reqChan)
	defer s.Close()

	client := &client{Protocol: "http", Client: c}

	createB2("1", "1", client)

	// get the request that the mock server received
	req := <-reqChan

	username, password, ok := req.BasicAuth()
	if !ok {
		t.Fatal("Expected ok to be true, instead got false")
	}
	if username != "1" {
		t.Errorf(`Expected username to be "1", instead got %s`, username)
	}
	if password != "1" {
		t.Errorf(`Expected password to be "1", instead got %s`, password)
	}
}

func Test_MakeB2_Errors(t *testing.T) {
	codes, bodies := errorResponses()
	for i := range codes {
		s, c := setupRequest(codes[i], bodies[i])

		client := &client{Protocol: "http", Client: c}
		b, err := createB2("1", "1", client)
		testErrorResponse(err, codes[i], t)
		if b != nil {
			t.Errorf("Expected b to be empty, instead got %+v", b)
		}

		s.Close()
	}
}

func Test_parseCreateB2Response(t *testing.T) {
	t.Skip()
}

func Test_B2_CreateRequest(t *testing.T) {
	t.Skip()
}

func Test_replaceProtocol(t *testing.T) {
	b2s := []B2{
		B2{client: &client{Protocol: "https"}},
		B2{client: &client{Protocol: "http"}},
		B2{client: &client{Protocol: "kittens"}},
	}
	urls := []string{
		"http://localhost", "https://localhost", "http://localhost", "kittens://localhost",
		"https://www.backblaze.com/", "https://www.backblaze.com/",
		"http://www.backblaze.com/", "kittens://www.backblaze.com/",
		"non/url/",
	}

	for i, b := range b2s {
		index, i2, index2 := i+1, i+4, i+5 // make offsets
		// localhost
		url, err := b.replaceProtocol(urls[i])
		if err != nil {
			t.Errorf("Expected no error, instead got %+v", err)
		}
		if url != urls[index] {
			t.Errorf("Expected url to be %s, instead got %s", urls[index], url)
		}

		// www.backblaze.com
		url, err = b.replaceProtocol(urls[i2])
		if err != nil {
			t.Errorf("Expected no error, instead got %+v", err)
		}
		if url != urls[index2] {
			t.Errorf("Expected url to be %s, instead got %s", urls[index2], url)
		}
	}

	url, err := b2s[0].replaceProtocol(urls[len(urls)-1])
	if err == nil {
		t.Errorf("Expected error, instead got nil, url returned was %s", url)
	}
}

func Test_GetBzInfoHeaders(t *testing.T) {
	headers := map[string][]string{
		"Content-Type":      []string{"kittens"},
		"X-Bz-Info-kittens": []string{"yes"},
		"X-Bz-Info-thing":   []string{"one"},
	}
	resp := &http.Response{Header: headers}

	bzHeaders := GetBzInfoHeaders(resp)

	if len(bzHeaders) != 2 {
		t.Fatalf("Expected length of headers to be 2, instead got %d", len(bzHeaders))
	}
	if h, ok := bzHeaders["Content-Type"]; ok {
		t.Errorf("Expected no Content-Type, instead recieved %s", h)
	}
	if h := bzHeaders["kittens"]; h != "yes" {
		t.Errorf(`Expected kittens to be "yes", instead got %s`, h)
	}
	if h := bzHeaders["thing"]; h != "one" {
		t.Errorf(`Expected thing to be "one", instead got %s`, h)
	}
}

func setupRequest(code int, body string) (*httptest.Server, http.Client) {
	return setupMockJsonServer(code, body, nil)
}

func setupMockJsonServer(code int, body string, reqChan chan<- *http.Request) (*httptest.Server, http.Client) {
	headers := map[string]string{"Content-Type": "application/json"}
	return setupMockServer(code, body, headers, reqChan)
}

func setupMockServer(code int, body string, headers map[string]string, reqChan chan<- *http.Request) (*httptest.Server, http.Client) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if reqChan != nil {
			reqChan <- r
		}

		for k, v := range headers {
			w.Header().Set(k, v)
		}

		w.WriteHeader(code)
		fmt.Fprintln(w, body)
	}))

	tr := &http.Transport{
		Proxy: func(req *http.Request) (*url.URL, error) {
			return url.Parse(server.URL)
		},
	}

	return server, http.Client{Transport: tr}
}

func errorResponses() ([]int, []string) {
	codes := []int{400, 401}
	bodies := []string{
		`{"status":400,"code":"nope","message":"nope nope"}`,
		`{"status":401,"code":"nope","message":"nope nope"}`,
	}
	return codes, bodies
}

func testErrorResponse(err error, code int, t *testing.T) {
	if err == nil {
		t.Error("Expected error, no error received")
	} else if err.Error() != fmt.Sprintf("Status: %d, Code: nope, Message: nope nope", code) {
		t.Errorf(`Expected "Status: %d, Code: nope, Message: nope nope", instead got %s`, code, err)
	}
}

func makeTestB2(c http.Client) *B2 {
	return &B2{
		AccountID:          "id",
		AuthorizationToken: "token",
		ApiUrl:             "https://api900.backblaze.com",
		DownloadUrl:        "https://f900.backblaze.com",
		client:             &client{Protocol: "http", Client: c},
	}
}
<commit_msg>Add test for B2 CreateRequest<commit_after>package b2

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
)

func Test_createB2_Success(t *testing.T) {
	s, c := setupRequest(200, `{"accountId":"1","authorizationToken":"1","apiUrl":"/","downloadUrl":"/"}`)
	defer s.Close()

	client := &client{Protocol: "http", Client: c}

	b, err := createB2("1", "1", client)
	if err != nil {
		t.Fatalf("Expected no error, instead got %s", err)
	}

	if b.AccountID != "1" {
		t.Errorf(`Expected AccountID to be "1", instead got %s`, b.AccountID)
	}
	if b.AuthorizationToken != "1" {
		t.Errorf(`Expected AuthorizationToken to be "1", instead got %s`, b.AuthorizationToken)
	}
	if b.ApiUrl != "/" {
		t.Errorf(`Expected AccountID to be "/", instead got %s`, b.ApiUrl)
	}
	if b.DownloadUrl != "/" {
		t.Errorf(`Expected AccountID to be "/", instead got %s`, b.DownloadUrl)
	}
}

func Test_createB2_HasAuth(t *testing.T) {
	reqChan := make(chan *http.Request, 1)
	s, c := setupMockJsonServer(200, "", reqChan)
	defer s.Close()

	client := &client{Protocol: "http", Client: c}

	createB2("1", "1", client)

	// get the request that the mock server received
	req := <-reqChan

	username, password, ok := req.BasicAuth()
	if !ok {
		t.Fatal("Expected ok to be true, instead got false")
	}
	if username != "1" {
		t.Errorf(`Expected username to be "1", instead got %s`, username)
	}
	if password != "1" {
		t.Errorf(`Expected password to be "1", instead got %s`, password)
	}
}

func Test_MakeB2_Errors(t *testing.T) {
	codes, bodies := errorResponses()
	for i := range codes {
		s, c := setupRequest(codes[i], bodies[i])

		client := &client{Protocol: "http", Client: c}
		b, err := createB2("1", "1", client)
		testErrorResponse(err, codes[i], t)
		if b != nil {
			t.Errorf("Expected b to be empty, instead got %+v", b)
		}

		s.Close()
	}
}

func Test_parseCreateB2Response(t *testing.T) {
	t.Skip()
}

func Test_B2_CreateRequest(t *testing.T) {
	b2 := &B2{client: &client{Protocol: "https"}} // set client protocol to default

	methods := []string{"GET", "POST", "KITTENS", "POST"}
	urls := []string{"http://example.com", "kittens://example.com", "aoeu://example.com",
		"invalid-url"}
	reqBody := struct {
		a int `json:"a"`
	}{a: 1}

	for i := 0; i < 3; i++ {
		req, err := b2.CreateRequest(methods[i], urls[i], reqBody)
		if err != nil {
			t.Fatalf("Expected err to be nil, instead got %+v", err)
		}
		if req.URL.Scheme != "https" {
			t.Errorf(`Expected url protocol to be "https", instead got %s`, req.URL.Scheme)
		}
		if req.Body == nil {
			t.Error("Expected req.Body to not be nil")
		}
	}
	for i := 3; i < 4; i++ {
		req, err := b2.CreateRequest(methods[i], urls[i], reqBody)
		if req != nil {
			t.Errorf("Expected req to be nil, instead got %+v", req)
		}
		if err == nil {
			t.Fatal("Expected err to exist")
		}
	}
}

func Test_replaceProtocol(t *testing.T) {
	b2s := []B2{
		B2{client: &client{Protocol: "https"}},
		B2{client: &client{Protocol: "http"}},
		B2{client: &client{Protocol: "kittens"}},
	}
	urls := []string{
		"http://localhost", "https://localhost", "http://localhost", "kittens://localhost",
		"https://www.backblaze.com/", "https://www.backblaze.com/",
		"http://www.backblaze.com/", "kittens://www.backblaze.com/",
		"non/url/",
	}

	for i, b := range b2s {
		index, i2, index2 := i+1, i+4, i+5 // make offsets
		// localhost
		url, err := b.replaceProtocol(urls[i])
		if err != nil {
			t.Errorf("Expected no error, instead got %+v", err)
		}
		if url != urls[index] {
			t.Errorf("Expected url to be %s, instead got %s", urls[index], url)
		}

		// www.backblaze.com
		url, err = b.replaceProtocol(urls[i2])
		if err != nil {
			t.Errorf("Expected no error, instead got %+v", err)
		}
		if url != urls[index2] {
			t.Errorf("Expected url to be %s, instead got %s", urls[index2], url)
		}
	}

	url, err := b2s[0].replaceProtocol(urls[len(urls)-1])
	if err == nil {
		t.Errorf("Expected error, instead got nil, url returned was %s", url)
	}
}

func Test_GetBzInfoHeaders(t *testing.T) {
	headers := map[string][]string{
		"Content-Type":      []string{"kittens"},
		"X-Bz-Info-kittens": []string{"yes"},
		"X-Bz-Info-thing":   []string{"one"},
	}
	resp := &http.Response{Header: headers}

	bzHeaders := GetBzInfoHeaders(resp)

	if len(bzHeaders) != 2 {
		t.Fatalf("Expected length of headers to be 2, instead got %d", len(bzHeaders))
	}
	if h, ok := bzHeaders["Content-Type"]; ok {
		t.Errorf("Expected no Content-Type, instead recieved %s", h)
	}
	if h := bzHeaders["kittens"]; h != "yes" {
		t.Errorf(`Expected kittens to be "yes", instead got %s`, h)
	}
	if h := bzHeaders["thing"]; h != "one" {
		t.Errorf(`Expected thing to be "one", instead got %s`, h)
	}
}

func setupRequest(code int, body string) (*httptest.Server, http.Client) {
	return setupMockJsonServer(code, body, nil)
}

func setupMockJsonServer(code int, body string, reqChan chan<- *http.Request) (*httptest.Server, http.Client) {
	headers := map[string]string{"Content-Type": "application/json"}
	return setupMockServer(code, body, headers, reqChan)
}

func setupMockServer(code int, body string, headers map[string]string, reqChan chan<- *http.Request) (*httptest.Server, http.Client) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if reqChan != nil {
			reqChan <- r
		}

		for k, v := range headers {
			w.Header().Set(k, v)
		}

		w.WriteHeader(code)
		fmt.Fprintln(w, body)
	}))

	tr := &http.Transport{
		Proxy: func(req *http.Request) (*url.URL, error) {
			return url.Parse(server.URL)
		},
	}

	return server, http.Client{Transport: tr}
}

func errorResponses() ([]int, []string) {
	codes := []int{400, 401}
	bodies := []string{
		`{"status":400,"code":"nope","message":"nope nope"}`,
		`{"status":401,"code":"nope","message":"nope nope"}`,
	}
	return codes, bodies
}

func testErrorResponse(err error, code int, t *testing.T) {
	if err == nil {
		t.Error("Expected error, no error received")
	} else if err.Error() != fmt.Sprintf("Status: %d, Code: nope, Message: nope nope", code) {
		t.Errorf(`Expected "Status: %d, Code: nope, Message: nope nope", instead got %s`, code, err)
	}
}

func makeTestB2(c http.Client) *B2 {
	return &B2{
		AccountID:          "id",
		AuthorizationToken: "token",
		ApiUrl:             "https://api900.backblaze.com",
		DownloadUrl:        "https://f900.backblaze.com",
		client:             &client{Protocol: "http", Client: c},
	}
}
<|endoftext|>