<commit_before>package clw11

/*
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif

extern void programCallback(cl_program program, void *user_data);

void callProgramCallback(cl_program program, void *user_data)
{
	programCallback(program, user_data);
}
*/
import "C"
import "unsafe"

type (
	Program          C.cl_program
	ProgramInfo      C.cl_program_info
	ProgramBuildInfo C.cl_program_build_info
)

const (
	ProgramReferenceCount = ProgramInfo(C.CL_PROGRAM_REFERENCE_COUNT)
	ProgramContext        = ProgramInfo(C.CL_PROGRAM_CONTEXT)
	ProgramNumDevices     = ProgramInfo(C.CL_PROGRAM_NUM_DEVICES)
	ProgramDevices        = ProgramInfo(C.CL_PROGRAM_DEVICES)
	ProgramSource         = ProgramInfo(C.CL_PROGRAM_SOURCE)
	ProgramBinarySizes    = ProgramInfo(C.CL_PROGRAM_BINARY_SIZES)
	ProgramBinaries       = ProgramInfo(C.CL_PROGRAM_BINARIES)
)

const (
	ProgramBuildStatus  = ProgramBuildInfo(C.CL_PROGRAM_BUILD_STATUS)
	ProgramBuildOptions = ProgramBuildInfo(C.CL_PROGRAM_BUILD_OPTIONS)
	ProgramBuildLog     = ProgramBuildInfo(C.CL_PROGRAM_BUILD_LOG)
)

func CreateProgramWithSource(context Context, sources [][]byte) (Program, error) {

	count := len(sources)
	strings := make([]unsafe.Pointer, count)
	lengths := make([]C.size_t, count)
	for i := range sources {
		strings[i] = unsafe.Pointer(&sources[i][0])
		lengths[i] = C.size_t(len(sources[i]))
	}

	var err C.cl_int
	program := C.clCreateProgramWithSource(context, C.cl_uint(count), (**C.char)(unsafe.Pointer(&strings[0])),
		&lengths[0], &err)

	return Program(program), toError(err)
}

func BuildProgram(program Program, devices []DeviceID, options string, callback ProgramCallbackFunc,
	userData interface{}) error {

	cOptions := C.CString(options)
	defer C.free(unsafe.Pointer(cOptions))

	key := programCallbacks.add(callback, userData)

	err := toError(C.clBuildProgram(program, C.cl_uint(len(devices)), (*C.cl_device_id)(&devices[0]), cOptions,
		(*[0]byte)(C.callProgramCallback), unsafe.Pointer(key)))

	if err != nil {
		// If the C side setting of the callback failed the get callback will
		// remove the callback from the map.
		programCallbacks.get(key)
	}

	return err
}

func GetProgramInfo(program Program, param_name ProgramInfo, param_value_size Size, param_value unsafe.Pointer,
	param_value_size_ret *Size) error {

	return toError(C.clGetProgramInfo(program, C.cl_program_info(param_name), C.size_t(param_value_size), param_value,
		(*C.size_t)(param_value_size_ret)))
}

func GetProgramBuildInfo(program Program, device DeviceID, param_name ProgramBuildInfo, param_value_size Size,
	param_value unsafe.Pointer, param_value_size_ret *Size) error {

	return toError(C.clGetProgramBuildInfo(program, device, C.cl_program_build_info(param_name),
		C.size_t(param_value_size), param_value, (*C.size_t)(param_value_size_ret)))
}
<commit_msg>Added remaining program wrappers.<commit_after>package clw11

/*
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif

extern void programCallback(cl_program program, void *user_data);

void callProgramCallback(cl_program program, void *user_data)
{
	programCallback(program, user_data);
}
*/
import "C"
import "unsafe"

type (
	Program          C.cl_program
	ProgramInfo      C.cl_program_info
	ProgramBuildInfo C.cl_program_build_info
)

const (
	ProgramReferenceCount = ProgramInfo(C.CL_PROGRAM_REFERENCE_COUNT)
	ProgramContext        = ProgramInfo(C.CL_PROGRAM_CONTEXT)
	ProgramNumDevices     = ProgramInfo(C.CL_PROGRAM_NUM_DEVICES)
	ProgramDevices        = ProgramInfo(C.CL_PROGRAM_DEVICES)
	ProgramSource         = ProgramInfo(C.CL_PROGRAM_SOURCE)
	ProgramBinarySizes    = ProgramInfo(C.CL_PROGRAM_BINARY_SIZES)
	ProgramBinaries       = ProgramInfo(C.CL_PROGRAM_BINARIES)
)

const (
	ProgramBuildStatus  = ProgramBuildInfo(C.CL_PROGRAM_BUILD_STATUS)
	ProgramBuildOptions = ProgramBuildInfo(C.CL_PROGRAM_BUILD_OPTIONS)
	ProgramBuildLog     = ProgramBuildInfo(C.CL_PROGRAM_BUILD_LOG)
)

// Creates a program object for a context, and loads the source code specified
// by the text strings in the strings array into the program object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateProgramWithSource.html
func CreateProgramWithSource(context Context, sources [][]byte) (Program, error) {

	count := len(sources)
	strings := make([]unsafe.Pointer, count)
	lengths := make([]C.size_t, count)
	for i := range sources {
		strings[i] = unsafe.Pointer(&sources[i][0])
		lengths[i] = C.size_t(len(sources[i]))
	}

	var err C.cl_int
	program := C.clCreateProgramWithSource(context, C.cl_uint(count), (**C.char)(unsafe.Pointer(&strings[0])),
		&lengths[0], &err)

	return Program(program), toError(err)
}

// Creates a program object for a context, and loads the binary bits specified
// by binary into the program object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateProgramWithBinary.html
func CreateProgramWithBinary(context Context, devices []DeviceID, binaries [][]byte,
	binary_status []error) (Program, error) {

	num_devices := len(devices)
	lengths := make([]C.size_t, num_devices)
	cBinaries := make([]*C.uchar, num_devices)
	errors := make([]C.cl_int, num_devices)
	for i := range devices {
		lengths[i] = C.size_t(len(binaries[i]))
		cBinaries[i] = (*C.uchar)(&binaries[i][0])
	}

	var err C.cl_int
	program := C.clCreateProgramWithBinary(context, C.cl_uint(num_devices), (*C.cl_device_id)(&devices[0]),
		(*C.size_t)(&lengths[0]), (**C.uchar)(&cBinaries[0]), (*C.cl_int)(&errors[0]), &err)

	for i := range binary_status {
		binary_status[i] = toError(errors[i])
	}

	return Program(program), toError(err)
}

// Increments the program reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clRetainProgram.html
func RetainProgram(program Program) error {
	return toError(C.clRetainProgram(program))
}

// Decrements the program reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clReleaseProgram.html
func ReleaseProgram(program Program) error {
	return toError(C.clReleaseProgram(program))
}

// Allows the implementation to release the resources allocated by the OpenCL
// compiler.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clUnloadCompiler.html
func UnloadCompiler() error {
	return toError(C.clUnloadCompiler())
}

// Builds (compiles and links) a program executable from the program source or
// binary.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clBuildProgram.html
func BuildProgram(program Program, devices []DeviceID, options string, callback ProgramCallbackFunc,
	userData interface{}) error {

	cOptions := C.CString(options)
	defer C.free(unsafe.Pointer(cOptions))

	key := programCallbacks.add(callback, userData)

	err := toError(C.clBuildProgram(program, C.cl_uint(len(devices)), (*C.cl_device_id)(&devices[0]), cOptions,
		(*[0]byte)(C.callProgramCallback), unsafe.Pointer(key)))

	if err != nil {
		// If the C side setting of the callback failed the get callback will
		// remove the callback from the map.
		programCallbacks.get(key)
	}

	return err
}

// Returns information about the program object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clGetProgramInfo.html
func GetProgramInfo(program Program, param_name ProgramInfo, param_value_size Size, param_value unsafe.Pointer,
	param_value_size_ret *Size) error {

	return toError(C.clGetProgramInfo(program, C.cl_program_info(param_name), C.size_t(param_value_size), param_value,
		(*C.size_t)(param_value_size_ret)))
}

// Returns build information for each device in the program object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clGetProgramBuildInfo.html
func GetProgramBuildInfo(program Program, device DeviceID, param_name ProgramBuildInfo, param_value_size Size,
	param_value unsafe.Pointer, param_value_size_ret *Size) error {

	return toError(C.clGetProgramBuildInfo(program, device, C.cl_program_build_info(param_name),
		C.size_t(param_value_size), param_value, (*C.size_t)(param_value_size_ret)))
}
<|endoftext|>


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

package vulncheck

import (
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/vuln/client"
	"golang.org/x/vuln/osv"
)

// Config is used for configuring vulncheck algorithms.
type Config struct {
	// ImportsOnly instructs vulncheck to analyze import chains only.
	// Otherwise, call chains are analyzed too.
	ImportsOnly bool

	// Client is used for querying data from a vulnerability database.
	Client client.Client

	// SourceGoVersion is Go version used to build Source inputs passed
	// to vulncheck. If not provided, the current underlying Go version
	// is used to detect vulnerabilities in Go standard library.
	SourceGoVersion string
}

// Package is a Go package for vulncheck analysis. It is a version of
// packages.Package trimmed down to reduce memory consumption.
type Package struct {
	Name      string
	PkgPath   string
	Imports   []*Package
	Pkg       *types.Package
	Fset      *token.FileSet
	Syntax    []*ast.File
	TypesInfo *types.Info
	Module    *Module
}

// Module is a Go module for vulncheck analysis.
type Module struct {
	Path    string
	Version string
	Dir     string
	Replace *Module
}

// Convert transforms a slice of packages.Package to
// a slice of corresponding vulncheck.Package.
func Convert(pkgs []*packages.Package) []*Package {
	convertMod := newModuleConverter()
	ps := make(map[*packages.Package]*Package)
	var pkg func(*packages.Package) *Package
	pkg = func(p *packages.Package) *Package {
		if vp, ok := ps[p]; ok {
			return vp
		}

		vp := &Package{
			Name:      p.Name,
			PkgPath:   p.PkgPath,
			Pkg:       p.Types,
			Fset:      p.Fset,
			Syntax:    p.Syntax,
			TypesInfo: p.TypesInfo,
			Module:    convertMod(p.Module),
		}
		ps[p] = vp

		for _, i := range p.Imports {
			vp.Imports = append(vp.Imports, pkg(i))
		}
		return vp
	}

	var vpkgs []*Package
	for _, p := range pkgs {
		vpkgs = append(vpkgs, pkg(p))
	}
	return vpkgs
}

// Result contains information on how known vulnerabilities are reachable
// in the call graph, package imports graph, and module requires graph of
// the user code.
type Result struct {
	// Calls is a call graph whose roots are program entry functions and
	// methods, and sinks are known vulnerable symbols. It is empty when
	// Config.ImportsOnly is true or when no vulnerable symbols are reachable
	// via the program call graph.
	Calls *CallGraph

	// Imports is a package dependency graph whose roots are entry user packages
	// and sinks are packages with some known vulnerable symbols. It is empty
	// when no packages with vulnerabilities are imported in the program.
	Imports *ImportGraph

	// Requires is a module dependency graph whose roots are entry user modules
	// and sinks are modules with some vulnerable packages. It is empty when no
	// modules with vulnerabilities are required by the program. If used, the
	// standard library is modeled as an artificial "stdlib" module whose version
	// is the Go version used to build the code under analysis.
	Requires *RequireGraph

	// Vulns contains information on detected vulnerabilities and their place in
	// the above graphs. Only vulnerabilities whose symbols are reachable in Calls,
	// or whose packages are imported in Imports, or whose modules are required in
	// Requires, have an entry in Vulns.
	Vulns []*Vuln

	// Modules are the modules that comprise the user code.
	Modules []*Module
}

// Vuln provides information on how a vulnerability is affecting user code by
// connecting it to the Result.{Calls,Imports,Requires} graphs. Vulnerabilities
// detected in Go binaries do not appear in the Result graphs.
type Vuln struct {
	// OSV contains information on the detected vulnerability in the shared
	// vulnerability format.
	//
	// OSV, Symbol, PkgPath, and ModPath identify a vulnerability.
	//
	// Note that *osv.Entry may describe multiple symbols from multiple
	// packages.
	OSV *osv.Entry

	// Symbol is the name of the detected vulnerable function or method.
	Symbol string

	// PkgPath is the package path of the detected Symbol.
	PkgPath string

	// ModPath is the module path corresponding to PkgPath.
	ModPath string

	// CallSink is the ID of the FuncNode in Result.Calls corresponding to
	// Symbol.
	//
	// When analyzing binaries, Symbol is not reachable, or Config.ImportsOnly
	// is true, CallSink will be unavailable and set to 0.
	CallSink int

	// ImportSink is the ID of the PkgNode in Result.Imports corresponding to
	// PkgPath.
	//
	// When analyzing binaries or PkgPath is not imported, ImportSink will be
	// unavailable and set to 0.
	ImportSink int

	// RequireSink is the ID of the ModNode in Result.Requires corresponding to
	// ModPath.
	//
	// When analyzing binaries, RequireSink will be unavailable and set to 0.
	RequireSink int
}

// CallGraph is a slice of a full program call graph whose sinks are vulnerable
// functions and sources are entry points of user packages.
//
// CallGraph is directed from vulnerable functions towards program entry
// functions (see FuncNode) for a more efficient traversal of the slice
// related to a particular vulnerability.
type CallGraph struct {
	// Functions contains all call graph nodes as a map: FuncNode.ID -> FuncNode.
	Functions map[int]*FuncNode

	// Entries are IDs of a subset of Functions representing vulncheck entry points.
	Entries []int
}

// A FuncNode describes a function in the call graph.
type FuncNode struct {
	// ID is the id used to identify the FuncNode in CallGraph.
	ID int

	// Name is the name of the function.
	Name string

	// RecvType is the receiver object type of this function, if any.
	RecvType string

	// PkgPath is the import path of the package containing the function.
	PkgPath string

	// Position describes the position of the function in the file.
	Pos *token.Position

	// CallSites is a set of call sites where this function is called.
	CallSites []*CallSite
}

func (fn *FuncNode) String() string {
	if fn.RecvType == "" {
		return fmt.Sprintf("%s.%s", fn.PkgPath, fn.Name)
	}
	return fmt.Sprintf("%s.%s", fn.RecvType, fn.Name)
}

// A CallSite describes a function call.
type CallSite struct {
	// Parent is ID of the enclosing function where the call is made.
	Parent int

	// Name stands for the name of the function (variable) being called.
	Name string

	// RecvType is the full path of the receiver object type, if any.
	RecvType string

	// Position describes the position of the function in the file.
	Pos *token.Position

	// Resolved indicates if the called function can be statically resolved.
	Resolved bool
}

// RequireGraph is a slice of a full program module requires graph whose sinks
// are modules with known vulnerabilities and sources are modules of user entry
// packages.
//
// RequireGraph is directed from a vulnerable module towards the program entry
// modules (see ModNode) for a more efficient traversal of the slice related
// to a particular vulnerability.
type RequireGraph struct {
	// Modules contains all module nodes as a map: module node id -> module node.
	Modules map[int]*ModNode

	// Entries are IDs of a subset of Modules representing modules of vulncheck entry points.
	Entries []int
}

// A ModNode describes a module in the requires graph.
type ModNode struct {
	// ID is the id used to identify the ModNode in CallGraph.
	ID int

	// Path is the module path.
	Path string

	// Version is the module version.
	Version string

	// Replace is the ID of the replacement module node.
	// A zero value means there is no replacement.
	Replace int

	// RequiredBy contains IDs of the modules requiring this module.
	RequiredBy []int
}

// ImportGraph is a slice of a full program package import graph whose sinks are
// packages with some known vulnerabilities and sources are user specified
// packages.
//
// ImportGraph is directed from a vulnerable package towards the program entry
// packages (see PkgNode) for a more efficient traversal of the slice related
// to a particular vulnerability.
type ImportGraph struct {
	// Packages contains all package nodes as a map: package node id -> package node.
	Packages map[int]*PkgNode

	// Entries are IDs of a subset of Packages representing packages of vulncheck entry points.
	Entries []int
}

// A PkgNode describes a package in the import graph.
type PkgNode struct {
	// ID is the id used to identify the PkgNode in ImportGraph.
	ID int

	// Name is the package identifier as it appears in the source code.
	Name string

	// Path is the package path.
	Path string

	// Module holds ID of the corresponding module (node) in the Requires graph.
	Module int

	// ImportedBy contains IDs of packages directly importing this package.
	ImportedBy []int

	// pkg is used for connecting package node to module and call graph nodes.
	pkg *Package
}

// moduleVulnerabilities is an internal structure for
// holding and querying vulnerabilities provided by a
// vulnerability database client.
type moduleVulnerabilities []modVulns

// modVulns groups vulnerabilities per module.
type modVulns struct {
	mod   *Module
	vulns []*osv.Entry
}

func (mv moduleVulnerabilities) filter(os, arch string) moduleVulnerabilities {
	var filteredMod moduleVulnerabilities
	for _, mod := range mv {
		module := mod.mod
		modVersion := module.Version
		if module.Replace != nil {
			modVersion = module.Replace.Version
		}
		// TODO(https://golang.org/issues/49264): if modVersion == "", try vcs?
		var filteredVulns []*osv.Entry
		for _, v := range mod.vulns {
			var filteredAffected []osv.Affected
			for _, a := range v.Affected {
				// A module version is affected if
				//  - it is included in one of the affected version ranges
				//  - and module version is not ""
				//  The latter means the module version is not available, so
				//  we don't want to spam users with potential false alarms.
				//  TODO: issue warning for "" cases above?
				affected := modVersion != "" && a.Ranges.AffectsSemver(modVersion) && matchesPlatform(os, arch, a.EcosystemSpecific)
				if affected {
					filteredAffected = append(filteredAffected, a)
				}
			}
			if len(filteredAffected) == 0 {
				continue
			}
			// save the non-empty vulnerability with only
			// affected symbols.
			newV := *v
			newV.Affected = filteredAffected
			filteredVulns = append(filteredVulns, &newV)
		}
		filteredMod = append(filteredMod, modVulns{
			mod:   module,
			vulns: filteredVulns,
		})
	}
	return filteredMod
}

func matchesPlatform(os, arch string, e osv.EcosystemSpecific) bool {
	matchesOS := len(e.GOOS) == 0
	matchesArch := len(e.GOARCH) == 0
	for _, o := range e.GOOS {
		if os == o {
			matchesOS = true
			break
		}
	}
	for _, a := range e.GOARCH {
		if arch == a {
			matchesArch = true
			break
		}
	}
	return matchesOS && matchesArch
}

// vulnsForPackage returns the vulnerabilities for the module which is the most
// specific prefix of importPath, or nil if there is no matching module with
// vulnerabilities.
func (mv moduleVulnerabilities) vulnsForPackage(importPath string) []*osv.Entry {
	isStd := isStdPackage(importPath)
	var mostSpecificMod *modVulns
	for _, mod := range mv {
		md := mod
		if isStd && mod.mod == stdlibModule {
			// standard library packages do not have an associated module,
			// so we relate them to the artificial stdlib module.
			mostSpecificMod = &md
		} else if strings.HasPrefix(importPath, md.mod.Path) {
			if mostSpecificMod == nil || len(mostSpecificMod.mod.Path) < len(md.mod.Path) {
				mostSpecificMod = &md
			}
		}
	}

	if mostSpecificMod == nil {
		return nil
	}

	if mostSpecificMod.mod.Replace != nil {
		// standard libraries do not have a module nor replace module
		importPath = fmt.Sprintf("%s%s", mostSpecificMod.mod.Replace.Path, strings.TrimPrefix(importPath, mostSpecificMod.mod.Path))
	}
	vulns := mostSpecificMod.vulns
	packageVulns := []*osv.Entry{}
	for _, v := range vulns {
		for _, a := range v.Affected {
			if a.Package.Name == importPath {
				packageVulns = append(packageVulns, v)
				break
			}
		}
	}
	return packageVulns
}

// vulnsForSymbol returns vulnerabilities for `symbol` in `mv.VulnsForPackage(importPath)`.
func (mv moduleVulnerabilities) vulnsForSymbol(importPath, symbol string) []*osv.Entry {
	vulns := mv.vulnsForPackage(importPath)
	if vulns == nil {
		return nil
	}

	symbolVulns := []*osv.Entry{}
	for _, v := range vulns {
	vulnLoop:
		for _, a := range v.Affected {
			if a.Package.Name != importPath {
				continue
			}
			if len(a.EcosystemSpecific.Symbols) == 0 {
				symbolVulns = append(symbolVulns, v)
				continue vulnLoop
			}
			for _, s := range a.EcosystemSpecific.Symbols {
				if s == symbol {
					symbolVulns = append(symbolVulns, v)
					continue vulnLoop
				}
			}
		}
	}
	return symbolVulns
}

func newModuleConverter() func(m *packages.Module) *Module {
	pmap := map[*packages.Module]*Module{}
	var convert func(m *packages.Module) *Module
	convert = func(m *packages.Module) *Module {
		if m == nil {
			return nil
		}
		if vm, ok := pmap[m]; ok {
			return vm
		}
		vm := &Module{
			Path:    m.Path,
			Version: m.Version,
			Dir:     m.Dir,
			Replace: convert(m.Replace),
		}
		pmap[m] = vm
		return vm
	}
	return convert
}
<commit_msg>vulncheck: add ResultVersion<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vulncheck

import (
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/vuln/client"
	"golang.org/x/vuln/osv"
)

// ResultVersion should change when the results of this package change
// for any input. It is intended to be used to cache results.
// The field should begin with a date in YYYY-MM-DD format.
// Experimental versions of the package should follow that with
// a brief description. E.g. "2022-08-03 CHA only".
const ResultVersion = "2022-07-21"

// Config is used for configuring vulncheck algorithms.
type Config struct {
	// ImportsOnly instructs vulncheck to analyze import chains only.
	// Otherwise, call chains are analyzed too.
	ImportsOnly bool

	// Client is used for querying data from a vulnerability database.
	Client client.Client

	// SourceGoVersion is Go version used to build Source inputs passed
	// to vulncheck. If not provided, the current underlying Go version
	// is used to detect vulnerabilities in Go standard library.
	SourceGoVersion string
}

// Package is a Go package for vulncheck analysis. It is a version of
// packages.Package trimmed down to reduce memory consumption.
type Package struct {
	Name      string
	PkgPath   string
	Imports   []*Package
	Pkg       *types.Package
	Fset      *token.FileSet
	Syntax    []*ast.File
	TypesInfo *types.Info
	Module    *Module
}

// Module is a Go module for vulncheck analysis.
type Module struct {
	Path    string
	Version string
	Dir     string
	Replace *Module
}

// Convert transforms a slice of packages.Package to
// a slice of corresponding vulncheck.Package.
func Convert(pkgs []*packages.Package) []*Package {
	convertMod := newModuleConverter()
	ps := make(map[*packages.Package]*Package)
	var pkg func(*packages.Package) *Package
	pkg = func(p *packages.Package) *Package {
		if vp, ok := ps[p]; ok {
			return vp
		}

		vp := &Package{
			Name:      p.Name,
			PkgPath:   p.PkgPath,
			Pkg:       p.Types,
			Fset:      p.Fset,
			Syntax:    p.Syntax,
			TypesInfo: p.TypesInfo,
			Module:    convertMod(p.Module),
		}
		ps[p] = vp

		for _, i := range p.Imports {
			vp.Imports = append(vp.Imports, pkg(i))
		}
		return vp
	}

	var vpkgs []*Package
	for _, p := range pkgs {
		vpkgs = append(vpkgs, pkg(p))
	}
	return vpkgs
}

// Result contains information on how known vulnerabilities are reachable
// in the call graph, package imports graph, and module requires graph of
// the user code.
type Result struct {
	// Calls is a call graph whose roots are program entry functions and
	// methods, and sinks are known vulnerable symbols. It is empty when
	// Config.ImportsOnly is true or when no vulnerable symbols are reachable
	// via the program call graph.
	Calls *CallGraph

	// Imports is a package dependency graph whose roots are entry user packages
	// and sinks are packages with some known vulnerable symbols. It is empty
	// when no packages with vulnerabilities are imported in the program.
	Imports *ImportGraph

	// Requires is a module dependency graph whose roots are entry user modules
	// and sinks are modules with some vulnerable packages. It is empty when no
	// modules with vulnerabilities are required by the program. If used, the
	// standard library is modeled as an artificial "stdlib" module whose version
	// is the Go version used to build the code under analysis.
	Requires *RequireGraph

	// Vulns contains information on detected vulnerabilities and their place in
	// the above graphs. Only vulnerabilities whose symbols are reachable in Calls,
	// or whose packages are imported in Imports, or whose modules are required in
	// Requires, have an entry in Vulns.
	Vulns []*Vuln

	// Modules are the modules that comprise the user code.
	Modules []*Module
}

// Vuln provides information on how a vulnerability is affecting user code by
// connecting it to the Result.{Calls,Imports,Requires} graphs. Vulnerabilities
// detected in Go binaries do not appear in the Result graphs.
type Vuln struct {
	// OSV contains information on the detected vulnerability in the shared
	// vulnerability format.
	//
	// OSV, Symbol, PkgPath, and ModPath identify a vulnerability.
	//
	// Note that *osv.Entry may describe multiple symbols from multiple
	// packages.
	OSV *osv.Entry

	// Symbol is the name of the detected vulnerable function or method.
	Symbol string

	// PkgPath is the package path of the detected Symbol.
	PkgPath string

	// ModPath is the module path corresponding to PkgPath.
	ModPath string

	// CallSink is the ID of the FuncNode in Result.Calls corresponding to
	// Symbol.
	//
	// When analyzing binaries, Symbol is not reachable, or Config.ImportsOnly
	// is true, CallSink will be unavailable and set to 0.
	CallSink int

	// ImportSink is the ID of the PkgNode in Result.Imports corresponding to
	// PkgPath.
	//
	// When analyzing binaries or PkgPath is not imported, ImportSink will be
	// unavailable and set to 0.
	ImportSink int

	// RequireSink is the ID of the ModNode in Result.Requires corresponding to
	// ModPath.
	//
	// When analyzing binaries, RequireSink will be unavailable and set to 0.
	RequireSink int
}

// CallGraph is a slice of a full program call graph whose sinks are vulnerable
// functions and sources are entry points of user packages.
//
// CallGraph is directed from vulnerable functions towards program entry
// functions (see FuncNode) for a more efficient traversal of the slice
// related to a particular vulnerability.
type CallGraph struct {
	// Functions contains all call graph nodes as a map: FuncNode.ID -> FuncNode.
	Functions map[int]*FuncNode

	// Entries are IDs of a subset of Functions representing vulncheck entry points.
	Entries []int
}

// A FuncNode describes a function in the call graph.
type FuncNode struct {
	// ID is the id used to identify the FuncNode in CallGraph.
	ID int

	// Name is the name of the function.
	Name string

	// RecvType is the receiver object type of this function, if any.
	RecvType string

	// PkgPath is the import path of the package containing the function.
	PkgPath string

	// Position describes the position of the function in the file.
	Pos *token.Position

	// CallSites is a set of call sites where this function is called.
	CallSites []*CallSite
}

func (fn *FuncNode) String() string {
	if fn.RecvType == "" {
		return fmt.Sprintf("%s.%s", fn.PkgPath, fn.Name)
	}
	return fmt.Sprintf("%s.%s", fn.RecvType, fn.Name)
}

// A CallSite describes a function call.
type CallSite struct {
	// Parent is ID of the enclosing function where the call is made.
	Parent int

	// Name stands for the name of the function (variable) being called.
	Name string

	// RecvType is the full path of the receiver object type, if any.
	RecvType string

	// Position describes the position of the function in the file.
	Pos *token.Position

	// Resolved indicates if the called function can be statically resolved.
	Resolved bool
}

// RequireGraph is a slice of a full program module requires graph whose sinks
// are modules with known vulnerabilities and sources are modules of user entry
// packages.
//
// RequireGraph is directed from a vulnerable module towards the program entry
// modules (see ModNode) for a more efficient traversal of the slice related
// to a particular vulnerability.
type RequireGraph struct {
	// Modules contains all module nodes as a map: module node id -> module node.
	Modules map[int]*ModNode

	// Entries are IDs of a subset of Modules representing modules of vulncheck entry points.
	Entries []int
}

// A ModNode describes a module in the requires graph.
type ModNode struct {
	// ID is the id used to identify the ModNode in CallGraph.
	ID int

	// Path is the module path.
	Path string

	// Version is the module version.
	Version string

	// Replace is the ID of the replacement module node.
	// A zero value means there is no replacement.
	Replace int

	// RequiredBy contains IDs of the modules requiring this module.
	RequiredBy []int
}

// ImportGraph is a slice of a full program package import graph whose sinks are
// packages with some known vulnerabilities and sources are user specified
// packages.
//
// ImportGraph is directed from a vulnerable package towards the program entry
// packages (see PkgNode) for a more efficient traversal of the slice related
// to a particular vulnerability.
type ImportGraph struct {
	// Packages contains all package nodes as a map: package node id -> package node.
	Packages map[int]*PkgNode

	// Entries are IDs of a subset of Packages representing packages of vulncheck entry points.
	Entries []int
}

// A PkgNode describes a package in the import graph.
type PkgNode struct {
	// ID is the id used to identify the PkgNode in ImportGraph.
	ID int

	// Name is the package identifier as it appears in the source code.
	Name string

	// Path is the package path.
	Path string

	// Module holds ID of the corresponding module (node) in the Requires graph.
	Module int

	// ImportedBy contains IDs of packages directly importing this package.
	ImportedBy []int

	// pkg is used for connecting package node to module and call graph nodes.
	pkg *Package
}

// moduleVulnerabilities is an internal structure for
// holding and querying vulnerabilities provided by a
// vulnerability database client.
type moduleVulnerabilities []modVulns

// modVulns groups vulnerabilities per module.
type modVulns struct {
	mod   *Module
	vulns []*osv.Entry
}

func (mv moduleVulnerabilities) filter(os, arch string) moduleVulnerabilities {
	var filteredMod moduleVulnerabilities
	for _, mod := range mv {
		module := mod.mod
		modVersion := module.Version
		if module.Replace != nil {
			modVersion = module.Replace.Version
		}
		// TODO(https://golang.org/issues/49264): if modVersion == "", try vcs?
		var filteredVulns []*osv.Entry
		for _, v := range mod.vulns {
			var filteredAffected []osv.Affected
			for _, a := range v.Affected {
				// A module version is affected if
				//  - it is included in one of the affected version ranges
				//  - and module version is not ""
				//  The latter means the module version is not available, so
				//  we don't want to spam users with potential false alarms.
				//  TODO: issue warning for "" cases above?
				affected := modVersion != "" && a.Ranges.AffectsSemver(modVersion) && matchesPlatform(os, arch, a.EcosystemSpecific)
				if affected {
					filteredAffected = append(filteredAffected, a)
				}
			}
			if len(filteredAffected) == 0 {
				continue
			}
			// save the non-empty vulnerability with only
			// affected symbols.
			newV := *v
			newV.Affected = filteredAffected
			filteredVulns = append(filteredVulns, &newV)
		}
		filteredMod = append(filteredMod, modVulns{
			mod:   module,
			vulns: filteredVulns,
		})
	}
	return filteredMod
}

func matchesPlatform(os, arch string, e osv.EcosystemSpecific) bool {
	matchesOS := len(e.GOOS) == 0
	matchesArch := len(e.GOARCH) == 0
	for _, o := range e.GOOS {
		if os == o {
			matchesOS = true
			break
		}
	}
	for _, a := range e.GOARCH {
		if arch == a {
			matchesArch = true
			break
		}
	}
	return matchesOS && matchesArch
}

// vulnsForPackage returns the vulnerabilities for the module which is the most
// specific prefix of importPath, or nil if there is no matching module with
// vulnerabilities.
func (mv moduleVulnerabilities) vulnsForPackage(importPath string) []*osv.Entry {
	isStd := isStdPackage(importPath)
	var mostSpecificMod *modVulns
	for _, mod := range mv {
		md := mod
		if isStd && mod.mod == stdlibModule {
			// standard library packages do not have an associated module,
			// so we relate them to the artificial stdlib module.
			mostSpecificMod = &md
		} else if strings.HasPrefix(importPath, md.mod.Path) {
			if mostSpecificMod == nil || len(mostSpecificMod.mod.Path) < len(md.mod.Path) {
				mostSpecificMod = &md
			}
		}
	}

	if mostSpecificMod == nil {
		return nil
	}

	if mostSpecificMod.mod.Replace != nil {
		// standard libraries do not have a module nor replace module
		importPath = fmt.Sprintf("%s%s", mostSpecificMod.mod.Replace.Path, strings.TrimPrefix(importPath, mostSpecificMod.mod.Path))
	}
	vulns := mostSpecificMod.vulns
	packageVulns := []*osv.Entry{}
	for _, v := range vulns {
		for _, a := range v.Affected {
			if a.Package.Name == importPath {
				packageVulns = append(packageVulns, v)
				break
			}
		}
	}
	return packageVulns
}

// vulnsForSymbol returns vulnerabilities for `symbol` in `mv.VulnsForPackage(importPath)`.
func (mv moduleVulnerabilities) vulnsForSymbol(importPath, symbol string) []*osv.Entry {
	vulns := mv.vulnsForPackage(importPath)
	if vulns == nil {
		return nil
	}

	symbolVulns := []*osv.Entry{}
	for _, v := range vulns {
	vulnLoop:
		for _, a := range v.Affected {
			if a.Package.Name != importPath {
				continue
			}
			if len(a.EcosystemSpecific.Symbols) == 0 {
				symbolVulns = append(symbolVulns, v)
				continue vulnLoop
			}
			for _, s := range a.EcosystemSpecific.Symbols {
				if s == symbol {
					symbolVulns = append(symbolVulns, v)
					continue vulnLoop
				}
			}
		}
	}
	return symbolVulns
}

func newModuleConverter() func(m *packages.Module) *Module {
	pmap := map[*packages.Module]*Module{}
	var convert func(m *packages.Module) *Module
	convert = func(m *packages.Module) *Module {
		if m == nil {
			return nil
		}
		if vm, ok := pmap[m]; ok {
			return vm
		}
		vm := &Module{
			Path:    m.Path,
			Version: m.Version,
			Dir:     m.Dir,
			Replace: convert(m.Replace),
		}
		pmap[m] = vm
		return vm
	}
	return convert
}
<|endoftext|>


<commit_before>package decimal_test

import (
	"math"
	"math/big"
	"strconv"
	"strings"
	"sync"
	"testing"

	"github.com/ericlagergren/decimal"
	"github.com/ericlagergren/decimal/internal/test"
)

func TestBig_Abs(t *testing.T)        { test.Abs.Test(t) }
func TestBig_Add(t *testing.T)        { test.Add.Test(t) }
func TestBig_Class(t *testing.T)      { test.Class.Test(t) }
func TestBig_Cmp(t *testing.T)        { test.Cmp.Test(t) }
func TestBig_FMA(t *testing.T)        { test.FMA.Test(t) }
func TestBig_Mul(t *testing.T)        { test.Mul.Test(t) }
func TestBig_Neg(t *testing.T)        { test.Neg.Test(t) }
func TestBig_Quantize(t *testing.T)   { test.Quant.Test(t) }
func TestBig_Quo(t *testing.T)        { test.Quo.Test(t) }
func TestBig_QuoInt(t *testing.T)     { test.QuoInt.Test(t) }
func TestBig_Rat(t *testing.T)        { test.CTR.Test(t) }
func TestBig_Reduce(t *testing.T)     { test.Reduce.Test(t) }
func TestBig_Rem(t *testing.T)        { test.Rem.Test(t) }
func TestBig_RoundToInt(t *testing.T) { test.RoundToInt.Test(t) }
func TestBig_SetString(t *testing.T)  { test.CTS.Test(t) /* Same as CFS */ }
func TestBig_Sign(t *testing.T)       { test.Sign.Test(t) }
func TestBig_SignBit(t *testing.T)    { test.Signbit.Test(t) }
func TestBig_String(t *testing.T)     { test.CTS.Test(t) }
func TestBig_Sub(t *testing.T)        { test.Sub.Test(t) }

func TestBig_Float(t *testing.T) {
	for i, test := range [...]string{
		"42", "3.14156", "23423141234", ".44444", "1e+1222", "12e-444", "0",
	} {
		flt, ok := new(big.Float).SetString(test)
		if !ok {
			t.Fatal("!ok")
		}
		fv := new(big.Float).SetPrec(flt.Prec())
		xf := new(decimal.Big).SetFloat(flt).Float(fv)
		if xf.String() != flt.String() {
			t.Fatalf("#%d: wanted %f, got %f", i, flt, xf)
		}
	}
}

func TestBig_Int(t *testing.T) {
	for i, test := range [...]string{
		"1.234", "4.567", "11111111111111111111111111111111111.2",
		"1234234.2321", "121111111111", "44444444.241", "1241.1",
		"4", "5123", "1.2345123134123414123123213", "0.11", ".1",
	} {
		a, ok := new(decimal.Big).SetString(test)
		if !ok {
			t.Fatalf("#%d: !ok", i)
		}
		iv := test
		switch x := strings.IndexByte(test, '.'); {
		case x > 0:
			iv = test[:x]
		case x == 0:
			iv = "0"
		}
		n := a.Int(nil)
		if n.String() != iv {
			t.Fatalf("#%d: wanted %q, got %q", i, iv, n.String())
		}
	}
}

func TestBig_Int64(t *testing.T) {
	for i, test := range [...]string{
		"1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
		"100", "200", "300", "400", "500", "600", "700", "800", "900",
		"1000", "2000", "4000", "5000", "6000", "7000", "8000", "9000",
		"1000000", "2000000", "-12", "-500", "-13123213", "12.000000",
	} {
		a, ok := new(decimal.Big).SetString(test)
		if !ok {
			t.Fatalf("#%d: !ok", i)
		}
		iv := test
		switch x := strings.IndexByte(test, '.'); {
		case x > 0:
			iv = test[:x]
		case x == 0:
			iv = "0"
		}
		n, ok := a.Int64()
		if !ok {
			t.Fatal("!ok")
		}
		if ns := strconv.FormatInt(n, 10); ns != iv {
			t.Fatalf("#%d: wanted %q, got %q", i, iv, ns)
		}
	}
}

func TestBig_IsInt(t *testing.T) {
	for i, test := range [...]string{
		"1.087581170583171279366331325163992810993060588169144153517806339238748036659594606503711549623097075801903290898984816913699837852618679612062658508694865627080580343806827457751585727929883451128788810220782555198023845932678964045544369555311671308165766927777574386318610481491980102511680466744045522904137471213980283536704254600843996379022514957521",
		"0 int",
		"-0 int",
		"1 int",
		"-1 int",
		"0.0120",
		"444.000 int",
		"10.000 int",
		"1.0001e+33333 int",
		"0.5",
		"0.011",
		"1.23",
		"1.23e1",
		"1.23e2 int",
		"0.000000001e+8",
		"0.000000001e+9 int",
		"1.2345e200 int",
		"Inf",
		"+Inf",
		"-Inf",
		"-inf",
	} {
		s := strings.TrimSuffix(test, " int")
		x, ok := new(decimal.Big).SetString(s)
		if !ok {
			t.Fatal("TestBig_IsInt !ok")
		}
		want := s != test
		if got := x.IsInt(); got != want {
			t.Fatalf("#%d: (%q).IsInt() == %t", i, s, got)
		}
	}
}

// func TestBig_Format(t *testing.T) {
// 	tests := [...]struct {
// 		format string
// 		a      string
// 		b      string
// 	}{
// 		0: {format: "%e", a: "1.234", b: "1.234"},
// 		1: {format: "%s", a: "1.2134124124", b: "1.2134124124"},
// 		2: {format: "%e", a: "1.00003e-12", b: "1.00003e-12"},
// 		3: {format: "%E", a: "1.000003E-12", b: "1.000003E-12"},
// 	}
// 	for i, v := range tests {
// 		x, ok := new(decimal.Big).SetString(v.a)
// 		if !ok {
// 			t.Fatal("invalid SetString")
// 		}
// 		if fs := fmt.Sprintf(v.format, x); fs != v.b {
// 			t.Fatalf("#%d: wanted %q, got %q:", i, v.b, fs)
// 		}
// 	}
// }

func TestParallel(t *testing.T) {
	x := decimal.New(4, 0)
	y := decimal.New(3, 0)
	var wg sync.WaitGroup
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			m := new(decimal.Big)
			m.Add(x, y)
			m.Mul(m, y)
			m.Quo(m, x)
			m.Sub(m, y)
			m.FMA(m, x, y)
			wg.Done()
		}()
	}
	wg.Wait()
}

func TestBig_Prec(t *testing.T) {
	// confirmed to work inside internal/arith/intlen_test.go
}

func TestBig_Round(t *testing.T) {
	for i, test := range [...]struct {
		v   string
		to  int
		res string
	}{
		0: {"5.5", 1, "6"},
		1: {"1.234", 2, "1.2"},
		2: {"1", 1, "1"},
		3: {"9.876", 0, "9.876"},
		4: {"5.65", 2, "5.6"},
		5: {"5.0002", 2, "5"},
		6: {"0.000158674", 6, "0.000158674"},
		7: {"1.58089722856961873690377135139876745465351534188711107066818e+12288", 50, "1.5808972285696187369037713513987674546535153418871e+12288"},
	} {
		bd, _ := new(decimal.Big).SetString(test.v)
		r, _ := new(decimal.Big).SetString(test.res)
		if bd.Round(test.to).Cmp(r) != 0 {
			t.Fatalf(`#%d:
wanted: %q
got   : %q
`, i, test.res, bd)
		}
	}
}

func TestBig_Scan(t *testing.T) {
	// TODO(eric): write this test
}

func TestBig_SetFloat64(t *testing.T) {
	if testing.Short() {
		return
	}

	const eps = 1e-15
	z := decimal.WithPrecision(17)
	for x := uint32(0); x != math.MaxUint32; x++ {
		f := float64(math.Float32frombits(x))
		zf, _ := z.SetFloat64(f).Float64()
		if math.Float64bits(zf) != math.Float64bits(f) {
			if isSpecial(f) || isSpecial(zf) || math.Abs(zf-f) > eps {
				t.Fatalf(`#%d:
wanted: %g
got   : %g
`, x, f, zf)
			}
		}
	}
}

func isSpecial(f float64) bool { return math.IsInf(f, 0) || math.IsNaN(f) }
<commit_msg>add some autogenerated tests<commit_after>package decimal_test

import (
	"math"
	"math/big"
	"math/rand"
	"strconv"
	"strings"
	"sync"
	"testing"

	"github.com/ericlagergren/decimal"
	"github.com/ericlagergren/decimal/internal/test"
)

func TestBig_Abs(t *testing.T)        { test.Abs.Test(t) }
func TestBig_Add(t *testing.T)        { test.Add.Test(t) }
func TestBig_Class(t *testing.T)      { test.Class.Test(t) }
func TestBig_Cmp(t *testing.T)        { test.Cmp.Test(t) }
func TestBig_FMA(t *testing.T)        { test.FMA.Test(t) }
func TestBig_Mul(t *testing.T)        { test.Mul.Test(t) }
func TestBig_Neg(t *testing.T)        { test.Neg.Test(t) }
func TestBig_Quantize(t *testing.T)   { test.Quant.Test(t) }
func TestBig_Quo(t *testing.T)        { test.Quo.Test(t) }
func TestBig_QuoInt(t *testing.T)     { test.QuoInt.Test(t) }
func TestBig_Rat(t *testing.T)        { test.CTR.Test(t) }
func TestBig_Reduce(t *testing.T)     { test.Reduce.Test(t) }
func TestBig_Rem(t *testing.T)        { test.Rem.Test(t) }
func TestBig_RoundToInt(t *testing.T) { test.RoundToInt.Test(t) }
func TestBig_SetString(t *testing.T)  { test.CTS.Test(t) /* Same as CFS */ }
func TestBig_Sign(t *testing.T)       { test.Sign.Test(t) }
func TestBig_SignBit(t *testing.T)    { test.Signbit.Test(t) }
func TestBig_String(t *testing.T)     { test.CTS.Test(t) }
func TestBig_Sub(t *testing.T)        { test.Sub.Test(t) }

var rnd = rand.New(rand.NewSource(0))

func rndn(min, max int) int {
	return rnd.Intn(max-min) + min
}

func randDec() string {
	b := make([]byte, rndn(5, 50))
	for i := range b {
		b[i] = '0' + byte(rndn(0, 10))
	}
	if rnd.Intn(10) != 0 {
		b[rndn(2, len(b))] = '.'
	}
	if b[0] == '0' {
		if b[1] == '0' && b[2] != '.' {
			b = b[1:]
		}
		b[0] = '-'
	}
	return string(b)
}

var randDecs = func() (a [5000]string) {
	for i := range a {
		a[i] = randDec()
	}
	return a
}()

func TestBig_Float(t *testing.T) {
	for i, test := range randDecs {
		flt, ok := new(big.Float).SetString(test)
		if !ok {
			t.Fatal("!ok")
		}
		fv := new(big.Float).SetPrec(flt.Prec())
		xf := new(decimal.Big).SetFloat(flt).Float(fv)
		if xf.String() != flt.String() {
			t.Fatalf("#%d: wanted %f, got %f", i, flt, xf)
		}
	}
}

func TestBig_Int(t *testing.T) {
	for i, test := range randDecs {
		a, ok := new(decimal.Big).SetString(test)
		if !ok {
			t.Fatalf("#%d: !ok", i)
		}
		iv := test
		switch x := strings.IndexByte(test, '.'); {
		case x > 0:
			iv = test[:x]
		case x == 0:
			iv = "0"
		}
		b, ok := new(big.Int).SetString(iv, 10)
		if !ok {
			t.Fatal("!ok")
		}
		if n := a.Int(nil); n.Cmp(b) != 0 {
			t.Fatalf("#%d: wanted %q, got %q", i, b, n)
		}
	}
}

func TestBig_Int64(t *testing.T) {
	for i, test := range randDecs {
		a, ok := new(decimal.Big).SetString(test)
		if !ok {
			t.Fatalf("#%d: !ok", i)
		}
		iv := test
		switch x := strings.IndexByte(test, '.'); {
		case x > 0:
			iv = test[:x]
		case x == 0:
			iv = "0"
		}
		n, ok := a.Int64()
		gv, err := strconv.ParseInt(iv, 10, 64)
		if (err == nil) != ok {
			t.Fatalf("#%d: wanted %t, got %t", i, err == nil, ok)
		}
		if ok && (n != gv) {
			t.Fatalf("#%d: wanted %d, got %d", i, gv, n)
		}
	}
}

func TestBig_Uint64(t *testing.T) {
	for i, test := range randDecs {
		a, ok := new(decimal.Big).SetString(test)
		if !ok {
			t.Fatalf("#%d: !ok", i)
		}
		iv := test
		switch x := strings.IndexByte(test, '.'); {
		case x > 0:
			iv = test[:x]
		case x == 0:
			iv = "0"
		}
		n, ok := a.Uint64()
		if _, err := strconv.ParseUint(iv, 10, 64); (err == nil) != ok {
			t.Fatalf("#%d: wanted %t, got %t", i, err == nil, ok)
		}
		if !ok {
			continue
		}
		if ns := strconv.FormatUint(n, 10); ns != iv {
			t.Fatalf("#%d: wanted %q, got %q", i, iv, ns)
		}
	}
}

func TestBig_IsInt(t *testing.T) {
	allZeros := func(s string) bool {
		for _, c := range s {
			if c != '0' {
				return false
			}
		}
		return true
	}
	for i, test := range randDecs {
		x, ok := new(decimal.Big).SetString(test)
		if !ok {
			t.Fatal("TestBig_IsInt !ok")
		}
		j := strings.IndexByte(test, '.')
		if got := x.IsInt(); got != (j < 0 || allZeros(test[j+1:])) {
			t.Fatalf("#%d: (%q).IsInt() == %t", i, test, got)
		}
	}
}

// func TestBig_Format(t *testing.T) {
// 	tests := [...]struct {
// 		format string
// 		a      string
// 		b      string
// 	}{
// 		0: {format: "%e", a: "1.234", b: "1.234"},
// 		1: {format: "%s", a: "1.2134124124", b: "1.2134124124"},
// 		2: {format: "%e", a: "1.00003e-12", b: "1.00003e-12"},
// 		3: {format: "%E", a: "1.000003E-12", b: "1.000003E-12"},
// 	}
// 	for i, v := range tests {
// 		x, ok := new(decimal.Big).SetString(v.a)
// 		if !ok {
// 			t.Fatal("invalid SetString")
// 		}
// 		if fs := fmt.Sprintf(v.format, x); fs != v.b {
// 			t.Fatalf("#%d: wanted %q, got %q:", i, v.b, fs)
// 		}
// 	}
// }

func TestParallel(t *testing.T) {
	x := decimal.New(4, 0)
	y := decimal.New(3, 0)
	var wg sync.WaitGroup
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			m := new(decimal.Big)
			m.Add(x, y)
			m.Mul(m, y)
			m.Quo(m, x)
			m.Sub(m, y)
			m.FMA(m, x, y)
			wg.Done()
		}()
	}
	wg.Wait()
}

func TestBig_Prec(t *testing.T) {
	// confirmed to work inside internal/arith/intlen_test.go
}

func TestBig_Round(t *testing.T) {
	for i, test := range [...]struct {
		v   string
		to  int
		res string
	}{
		0: {"5.5", 1, "6"},
		1: {"1.234", 2, "1.2"},
		2: {"1", 1, "1"},
		3: {"9.876", 0, "9.876"},
		4: {"5.65", 2, "5.6"},
		5: {"5.0002", 2, "5"},
		6: {"0.000158674", 6, "0.000158674"},
		7: {"1.58089722856961873690377135139876745465351534188711107066818e+12288", 50, "1.5808972285696187369037713513987674546535153418871e+12288"},
	} {
		bd, _ := new(decimal.Big).SetString(test.v)
		r, _ := new(decimal.Big).SetString(test.res)
		if bd.Round(test.to).Cmp(r) != 0 {
			t.Fatalf(`#%d:
wanted: %q
got   : %q
`, i, test.res, bd)
		}
	}
}

func TestBig_Scan(t *testing.T) {
	// TODO(eric): write this test
}

func TestBig_SetFloat64(t *testing.T) {
	if testing.Short() {
		return
	}

	const eps = 1e-15
	z := decimal.WithPrecision(17)
	for x := uint32(0); x != math.MaxUint32; x++ {
		f := float64(math.Float32frombits(x))
		zf, _ := z.SetFloat64(f).Float64()
		if math.Float64bits(zf) != math.Float64bits(f) {
			if isSpecial(f) || isSpecial(zf) || math.Abs(zf-f) > eps {
				t.Fatalf(`#%d:
wanted: %g
got   : %g
`, x, f, zf)
			}
		}
	}
}

func isSpecial(f float64) bool { return math.IsInf(f, 0) || math.IsNaN(f) }
<|endoftext|>


<commit_before>package main

import prompt "github.com/segmentio/go-prompt"
import "fmt"

var (
	commands = []string{"source", "create", "update", "delete"}
)

type interactivePrompt struct {
	fileStore filer
	session   launcher
}

func (p *interactivePrompt) listCommands() {
	printLogo()
	i := prompt.Choose("Please select from the following commands", commands)
	switch commands[i] {
	case "source":
		p.source()
	case "create":
		p.create()
	case "update":
		p.update()
	case "delete":
		fmt.Println("hello world delete")
	}
}

func (p *interactivePrompt) source() {
	var pass string
	var envs map[string]string
	files, err := p.fileStore.listFiles()
	printError(err)
	i := prompt.Choose("Select from the following enviroments", files)
	envFile, err := p.fileStore.getFile(files[i])
	printError(err)
	if envFile.fileContent.Encrypted {
		pass = prompt.PasswordMasked("File is encrypted, please enter the passpharse")
	}
	envs, err = envFile.getContent(pass)
	printError(err)
	p.session.launch(envs)
}

func (p *interactivePrompt) create() {
	var pass string
	var done bool
	envs := make(map[string]string)
	fileName := prompt.StringRequired("Enter a name for this container")
	encrypted := prompt.Confirm("Would you like to encrypt this container?(Yes,y/No,n)")
	if encrypted {
		pass = prompt.PasswordMasked("Enter a passpharse")
	}
	envFile, err := p.fileStore.newFile(fileName+".json", encrypted)
	printError(err)
	for !done {
		key := prompt.StringRequired("Enter a key")
		value := prompt.StringRequired("Enter a value")
		envs[key] = value
		done = prompt.Confirm("stop adding enviroment variables?(Yes,y/No,n)")
	}
	printError(envFile.setContent(envs, pass))
	printError(envFile.save())
}

func (p *interactivePrompt) update() {
	var pass string
	var done bool
	envs := make(map[string]string)
	files, err := p.fileStore.listFiles()
	printError(err)
	i := prompt.Choose("Pick a container to update", files)
	envFile, err := p.fileStore.getFile(files[i])
	printError(err)
	if envFile.fileContent.Encrypted {
		pass = prompt.PasswordMasked("File is encrypted, please enter the passpharse")
	}
	envs, err = envFile.getContent(pass)
	printError(err)
	for !done {
		for k, v := range envs {
			fmt.Println(k + "=" + v)
		}
		key := prompt.StringRequired("Enter a key")
		value := prompt.StringRequired("Enter a value")
		envs[key] = value
		done = prompt.Confirm("stop adding/updating variables enviroment variables?(Yes,y/No,n)")
	}
	printError(envFile.setContent(envs, pass))
	printError(envFile.save())
}
<commit_msg>added delete file<commit_after>package main

import prompt "github.com/segmentio/go-prompt"
import "fmt"

var (
	commands = []string{"source", "create", "update", "delete"}
)

type interactivePrompt struct {
	fileStore filer
	session   launcher
}

func (p *interactivePrompt) listCommands() {
	printLogo()
	i := prompt.Choose("Please select from the following commands", commands)
	switch commands[i] {
	case "source":
		p.source()
	case "create":
		p.create()
	case "update":
		p.update()
	case "delete":
		p.delete()
	}
}

func (p *interactivePrompt) source() {
	var pass string
	var envs map[string]string
	files, err := p.fileStore.listFiles()
	printError(err)
	i := prompt.Choose("Select from the following enviroments", files)
	envFile, err := p.fileStore.getFile(files[i])
	printError(err)
	if envFile.fileContent.Encrypted {
		pass = prompt.PasswordMasked("File is encrypted, please enter the passpharse")
	}
	envs, err = envFile.getContent(pass)
	printError(err)
	p.session.launch(envs)
}

func (p *interactivePrompt) create() {
	var pass string
	var done bool
	envs := make(map[string]string)
	fileName := prompt.StringRequired("Enter a name for this container")
	encrypted := prompt.Confirm("Would you like to encrypt this container?(Yes,y/No,n)")
	if encrypted {
		pass = prompt.PasswordMasked("Enter a passpharse")
	}
	envFile, err := p.fileStore.newFile(fileName+".json", encrypted)
	printError(err)
	for !done {
		key := prompt.StringRequired("Enter a key")
		value := prompt.StringRequired("Enter a value")
		envs[key] = value
		done = prompt.Confirm("stop adding enviroment variables?(Yes,y/No,n)")
	}
	printError(envFile.setContent(envs, pass))
	printError(envFile.save())
}

func (p *interactivePrompt) update() {
	var pass string
	var done bool
	envs := make(map[string]string)
	files, err := p.fileStore.listFiles()
	printError(err)
	i := prompt.Choose("Pick a container to update", files)
	envFile, err := p.fileStore.getFile(files[i])
	printError(err)
	if envFile.fileContent.Encrypted {
		pass = prompt.PasswordMasked("File is encrypted, please enter the passpharse")
	}
	envs, err = envFile.getContent(pass)
	printError(err)
	for !done {
		for k, v := range envs {
			fmt.Println(k + "=" + v)
		}
		key := prompt.StringRequired("Enter a key")
		value := prompt.StringRequired("Enter a value")
		envs[key] = value
		done = prompt.Confirm("stop adding/updating variables enviroment variables?(Yes,y/No,n)")
	}
	printError(envFile.setContent(envs, pass))
	printError(envFile.save())
}

func (p *interactivePrompt) delete() {
	files, err := p.fileStore.listFiles()
	printError(err)
	i := prompt.Choose("Pick a container to delete", files)
	printError(p.fileStore.deleteFile(files[i]))
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"net/http"
)

var sqlString string = "gimvic:GimVicServer@/gimvic"

func main() {
	http.HandleFunc("/chooserOptions", chooserOptions)
	http.ListenAndServe(":8080", nil)
}

func chooserOptions(w http.ResponseWriter, r *http.Request) {
	response := ChooserOptionsResponse{}
	con, err := sql.Open("mysql", sqlString)
	check(err)
	defer con.Close()

	//fill main classes
	rows, err := con.Query("select class from classes where main=1;")
	check(err)
	for rows.Next() {
		var temp string
		rows.Scan(&temp)
		response.MainClasses = append(response.MainClasses, temp)
	}

	//fill additional classes
	rows, err = con.Query("select class from classes where main=0;")
	check(err)
	for rows.Next() {
		var temp string
		rows.Scan(&temp)
		response.AdditionalClasses = append(response.AdditionalClasses, temp)
	}

	responseStr, err := json.Marshal(response)
	check(err)
	fmt.Fprint(w, string(responseStr))
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

type ChooserOptionsResponse struct {
	MainClasses       []string
	AdditionalClasses []string
	ValidUntil        string
}

type ScheduleResponse struct {
}

type Day struct {
	Lessons    []Lesson
	SnackLines []string
	LunchLines []string
}

type Lesson struct {
	Subject        string
	Teacher        string
	Classroom      string
	Class          string
	Note           string
	Lesson         int
	Day            int
	IsSubstitution bool
}
<commit_msg>finished schedule builder<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"net/http"
	"net/url"
)

var sqlString string = "gimvic:GimVicServer@/gimvic"

func main() {
	http.HandleFunc("/chooserOptions", chooserOptions)
	http.HandleFunc("/data", data)
	http.ListenAndServe(":8080", nil)
}

func data(w http.ResponseWriter, r *http.Request) {
	queries := parseUrl(r)

	result := DataResponse{}
	if queries["type"][0] == "hybrid" {
		result.Days = pureScedule(queries)
	}

	jsonStr, err := json.Marshal(result)
	check(err)
	fmt.Fprint(w, string(jsonStr))
}

func pureScedule(queries map[string][]string) [5]Day {
	var days [5]Day
	con, err := sql.Open("mysql", sqlString)
	check(err)
	defer con.Close()

	where := ""
	for _, class := range queries["classes"] {
		if where != "" {
			where += " or "
		}
		where += "class='" + class + "'"
	}
	rows, err := con.Query("select class, teacher, subject, classroom, day, lesson from schedule where " + where + ";")
	check(err)
	var class, teacher, subject, classroom string
	var day, lesson int
	for rows.Next() {
		rows.Scan(&class, &teacher, &subject, &classroom, &day, &lesson)
		fmt.Println(lesson - 1)
		days[day-1].Lessons[lesson-1].Classes = append(days[day-1].Lessons[lesson-1].Classes, class)
		days[day-1].Lessons[lesson-1].Teachers = append(days[day-1].Lessons[lesson-1].Teachers, teacher)
		days[day-1].Lessons[lesson-1].Subjects = append(days[day-1].Lessons[lesson-1].Subjects, subject)
		days[day-1].Lessons[lesson-1].Classrooms = append(days[day-1].Lessons[lesson-1].Classrooms, classroom)
	}

	return days
}
func chooserOptions(w http.ResponseWriter, r *http.Request) {
	response := ChooserOptionsResponse{}
	con, err := sql.Open("mysql", sqlString)
	check(err)
	defer con.Close()

	//fill main classes
	rows, err := con.Query("select class from classes where main=1;")
	check(err)
	for rows.Next() {
		var temp string
		rows.Scan(&temp)
		response.MainClasses = append(response.MainClasses, temp)
	}

	//fill additional classes
	rows, err = con.Query("select class from classes where main=0;")
	check(err)
	for rows.Next() {
		var temp string
		rows.Scan(&temp)
		response.AdditionalClasses = append(response.AdditionalClasses, temp)
	}

	responseStr, err := json.Marshal(response)
	check(err)
	fmt.Fprint(w, string(responseStr))
}

func parseUrl(r *http.Request) map[string][]string {
	str := r.URL.String()
	u, err := url.Parse(str)
	check(err)
	m, err := url.ParseQuery(u.RawQuery)
	check(err)
	return m
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

type ChooserOptionsResponse struct {
	MainClasses       []string `json:"mainClasses,omitempty"`
	AdditionalClasses []string `json:"additionalClasses,omitempty"`
	ValidUntil        string   `json:"validUntil,omitempty"`
}

type DataResponse struct {
	Days [5]Day `json:"days,omitempty"`
	Hash string `json:"hash,omitempty"`
}

type Day struct {
	Lessons    [8]Lesson `json:"lessons,omitempty"`
	SnackLines []string  `json:"snackLines,omitempty"`
	LunchLines []string  `json:"lunchLines,omitempty"`
}

type Lesson struct {
	Subjects       []string `json:"subjects,omitempty"`
	Teachers       []string `json:"teachers,omitempty"`
	Classrooms     []string `json:"classrooms,omitempty"`
	Classes        []string `json:"classes,omitempty"`
	Note           string   `json:"note,omitempty"`
	Lesson         int      `json:"lesson,omitempty"`
	Day            int      `json:"day,omitempty"`
	IsSubstitution bool     `json:"substitution"`
}
<|endoftext|>


<commit_before>package wbrules

import (
	"fmt"
	wbgo "github.com/contactless/wbgo"
	"github.com/stretchr/objx"
	"io/ioutil"
	"path"
	"strconv"
	"testing"
)

const (
	SAMPLE_CLIENT_ID = "11111111"
)

type EditorSuite struct {
	wbgo.Suite
	*wbgo.FakeMQTTFixture
	client      wbgo.MQTTClient
	rpc         *wbgo.MQTTRPCServer
	scriptDir   string
	rmScriptDir func()
	id          uint64
}

func (s *EditorSuite) SetupTest() {
	s.Suite.SetupTest()
	s.id = 1
	s.scriptDir, s.rmScriptDir = wbgo.SetupTempDir(s.T())
	s.addSampleFiles()
	s.FakeMQTTFixture = wbgo.NewFakeMQTTFixture(s.T())
	s.rpc = wbgo.NewMQTTRPCServer("wbrules", s.Broker.MakeClient("wbrules"))
	s.rpc.Register(NewEditor(s.scriptDir))
	s.client = s.Broker.MakeClient("tst")
	s.client.Start()
	s.rpc.Start()
	s.Verify(
		"Subscribe -- wbrules: /rpc/v1/wbrules/+/+/+",
		"wbrules -> /rpc/v1/wbrules/Editor/List: [1] (QoS 1, retained)",
	)
}

func (s *EditorSuite) TearDownTest() {
	s.rmScriptDir()
	s.rpc.Stop()
	s.Suite.TearDownTest()
}

func (s *EditorSuite) writeScript(filename, content string) string {
	fullPath := path.Join(s.scriptDir, filename)
	if err := ioutil.WriteFile(fullPath, []byte(content), 0777); err != nil {
		s.Require().Fail("failed to write file", "%s: %s", fullPath, err)
	}
	return fullPath
}

func (s *EditorSuite) addSampleFiles() {
	s.writeScript("sample1.js", "// sample1")
	s.writeScript("sample2.js", "// sample2")
}

func (s *EditorSuite) verifyRpcRaw(subtopic string, param objx.Map, expectedResponse objx.Map) {
	replyId := strconv.FormatUint(s.id, 10)
	request := objx.Map{
		"id":     replyId,
		"params": []objx.Map{param},
	}
	s.id++
	topic := fmt.Sprintf("/rpc/v1/wbrules/Editor/%s/%s", subtopic, SAMPLE_CLIENT_ID)
	payload := request.MustJSON()
	s.client.Publish(wbgo.MQTTMessage{topic, payload, 1, false})
	resp := expectedResponse.Copy()
	resp["id"] = replyId
	s.Verify(
		fmt.Sprintf("tst -> %s: [%s] (QoS 1)", topic, payload),
		fmt.Sprintf("wbrules -> %s/reply: [%s] (QoS 1)", topic, resp.MustJSON()),
	)
}

func (s *EditorSuite) verifyRpc(subtopic string, param objx.Map, expectedResult interface{}) {
	s.verifyRpcRaw(subtopic, param, objx.Map{"result": expectedResult})
}

func (s *EditorSuite) verifyRpcError(subtopic string, param objx.Map, code int, typ string, msg string) {
	s.verifyRpc(
		subtopic,
		param,
		objx.Map{
			"error": objx.Map{
				"errorMessage": msg,
				"code":         code,
				"data":         typ,
			},
		},
	)
}

func (s *EditorSuite) TestListFiles() {
	s.verifyRpc("List", objx.Map{"path": "/"}, []string{
		"sample1.js",
		"sample2.js",
	})
}

func TestEditorSuite(t *testing.T) {
	wbgo.RunSuites(t, new(EditorSuite))
}

// TBD: make sure "../.." paths don't work
// TBD: list dirs
// TBD: only show .js files and dirs
// TBD: use verifyMessages()-style formatting for Recorder.Verify() / Recorder.VerifyUnordered()
//      and update tests that use them
// TBD: look for safe path handling for Go
<commit_msg>editor: updated tests for proper RPC params.<commit_after>package wbrules

import (
	"fmt"
	wbgo "github.com/contactless/wbgo"
	"github.com/stretchr/objx"
	"io/ioutil"
	"path"
	"strconv"
	"testing"
)

const (
	SAMPLE_CLIENT_ID = "11111111"
)

type EditorSuite struct {
	wbgo.Suite
	*wbgo.FakeMQTTFixture
	client      wbgo.MQTTClient
	rpc         *wbgo.MQTTRPCServer
	scriptDir   string
	rmScriptDir func()
	id          uint64
}

func (s *EditorSuite) SetupTest() {
	s.Suite.SetupTest()
	s.id = 1
	s.scriptDir, s.rmScriptDir = wbgo.SetupTempDir(s.T())
	s.addSampleFiles()
	s.FakeMQTTFixture = wbgo.NewFakeMQTTFixture(s.T())
	s.rpc = wbgo.NewMQTTRPCServer("wbrules", s.Broker.MakeClient("wbrules"))
	s.rpc.Register(NewEditor(s.scriptDir))
	s.client = s.Broker.MakeClient("tst")
	s.client.Start()
	s.rpc.Start()
	s.Verify(
		"Subscribe -- wbrules: /rpc/v1/wbrules/+/+/+",
		"wbrules -> /rpc/v1/wbrules/Editor/List: [1] (QoS 1, retained)",
	)
}

func (s *EditorSuite) TearDownTest() {
	s.rmScriptDir()
	s.rpc.Stop()
	s.Suite.TearDownTest()
}

func (s *EditorSuite) writeScript(filename, content string) string {
	fullPath := path.Join(s.scriptDir, filename)
	if err := ioutil.WriteFile(fullPath, []byte(content), 0777); err != nil {
		s.Require().Fail("failed to write file", "%s: %s", fullPath, err)
	}
	return fullPath
}

func (s *EditorSuite) addSampleFiles() {
	s.writeScript("sample1.js", "// sample1")
	s.writeScript("sample2.js", "// sample2")
}

func (s *EditorSuite) verifyRpcRaw(subtopic string, params objx.Map, expectedResponse objx.Map) {
	replyId := strconv.FormatUint(s.id, 10)
	request := objx.Map{
		"id":     replyId,
		"params": params,
	}
	s.id++
	topic := fmt.Sprintf("/rpc/v1/wbrules/Editor/%s/%s", subtopic, SAMPLE_CLIENT_ID)
	payload := request.MustJSON()
	s.client.Publish(wbgo.MQTTMessage{topic, payload, 1, false})
	resp := expectedResponse.Copy()
	resp["id"] = replyId
	s.Verify(
		fmt.Sprintf("tst -> %s: [%s] (QoS 1)", topic, payload),
		fmt.Sprintf("wbrules -> %s/reply: [%s] (QoS 1)", topic, resp.MustJSON()),
	)
}

func (s *EditorSuite) verifyRpc(subtopic string, param objx.Map, expectedResult interface{}) {
	s.verifyRpcRaw(subtopic, param, objx.Map{"result": expectedResult})
}

func (s *EditorSuite) verifyRpcError(subtopic string, param objx.Map, code int, typ string, msg string) {
	s.verifyRpc(
		subtopic,
		param,
		objx.Map{
			"error": objx.Map{
				"errorMessage": msg,
				"code":         code,
				"data":         typ,
			},
		},
	)
}

func (s *EditorSuite) TestListFiles() {
	s.verifyRpc("List", objx.Map{"path": "/"}, []string{
		"sample1.js",
		"sample2.js",
	})
}

func TestEditorSuite(t *testing.T) {
	wbgo.RunSuites(t, new(EditorSuite))
}

// TBD: make sure "../.." paths don't work
// TBD: list dirs
// TBD: only show .js files and dirs
// TBD: use verifyMessages()-style formatting for Recorder.Verify() / Recorder.VerifyUnordered()
//      and update tests that use them
// TBD: look for safe path handling for Go
<|endoftext|>


<commit_before>package log_test

import (
	"errors"
	"testing"

	"github.com/apex/log"
	"github.com/apex/log/handlers/memory"
	"github.com/stretchr/testify/assert"
)

type Pet struct {
	Name string
	Age  int
}

func (p *Pet) Fields() log.Fields {
	return log.Fields{
		"name": p.Name,
		"age":  p.Age,
	}
}

func TestInfo(t *testing.T) {
	h := memory.New()
	log.SetHandler(h)

	log.Infof("logged in %s", "Tobi")

	e := h.Entries[0]
	assert.Equal(t, e.Message, "logged in Tobi")
	assert.Equal(t, e.Level, log.InfoLevel)
}

func TestFielder(t *testing.T) {
	h := memory.New()
	log.SetHandler(h)

	pet := &Pet{"Tobi", 3}
	log.WithFields(pet).Info("add pet")

	e := h.Entries[0]
	assert.Equal(t, log.Fields{"name": "Tobi", "age": 3}, e.Fields)
}

// Unstructured logging is supported, but not recommended since it is hard to query.
func Example_unstructured() {
	log.Infof("%s logged in", "Tobi")
}

// Structured logging is supported with fields, and is recommended over the formatted message variants.
func Example_structured() {
	log.WithField("user", "Tobo").Info("logged in")
}

// Errors are passed to WithError(), populating the "error" field.
func Example_errors() {
	err := errors.New("boom")
	log.WithError(err).Error("upload failed")
}

// Multiple fields can be set, via chaining, or WithFields().
func Example_multipleFields() {
	log.WithFields(log.Fields{
		"user": "Tobi",
		"file": "sloth.png",
		"type": "image/png",
	}).Info("upload")
}

// Trace can be used to simplify logging of start and completion events,
// for example an upload which may fail.
func Example_trace() (err error) {
	defer log.Trace("upload").Stop(&err)
	return nil
}
<commit_msg>fix Example_trace(), should not return anything<commit_after>package log_test

import (
	"errors"
	"testing"

	"github.com/apex/log"
	"github.com/apex/log/handlers/memory"
	"github.com/stretchr/testify/assert"
)

type Pet struct {
	Name string
	Age  int
}

func (p *Pet) Fields() log.Fields {
	return log.Fields{
		"name": p.Name,
		"age":  p.Age,
	}
}

func TestInfo(t *testing.T) {
	h := memory.New()
	log.SetHandler(h)

	log.Infof("logged in %s", "Tobi")

	e := h.Entries[0]
	assert.Equal(t, e.Message, "logged in Tobi")
	assert.Equal(t, e.Level, log.InfoLevel)
}

func TestFielder(t *testing.T) {
	h := memory.New()
	log.SetHandler(h)

	pet := &Pet{"Tobi", 3}
	log.WithFields(pet).Info("add pet")

	e := h.Entries[0]
	assert.Equal(t, log.Fields{"name": "Tobi", "age": 3}, e.Fields)
}

// Unstructured logging is supported, but not recommended since it is hard to query.
func Example_unstructured() {
	log.Infof("%s logged in", "Tobi")
}

// Structured logging is supported with fields, and is recommended over the formatted message variants.
func Example_structured() {
	log.WithField("user", "Tobo").Info("logged in")
}

// Errors are passed to WithError(), populating the "error" field.
func Example_errors() {
	err := errors.New("boom")
	log.WithError(err).Error("upload failed")
}

// Multiple fields can be set, via chaining, or WithFields().
func Example_multipleFields() {
	log.WithFields(log.Fields{
		"user": "Tobi",
		"file": "sloth.png",
		"type": "image/png",
	}).Info("upload")
}

// Trace can be used to simplify logging of start and completion events,
// for example an upload which may fail.
func Example_trace() {
	fn := func() (err error) {
		defer log.Trace("upload").Stop(&err)
		return
	}

	fn()
	return
}
<|endoftext|>


<commit_before>package http2

import (
	"github.com/summerwind/h2spec/config"
	"github.com/summerwind/h2spec/spec"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"
)

func RequestPseudoHeaderFields() *spec.TestGroup {
	tg := NewTestGroup("8.1.2.3", "Request Pseudo-Header Fields")

	// The ":path" pseudo-header field includes the path and query
	// parts of the target URI (the "path-absolute" production and
	// optionally a '?' character followed by the "query" production
	// (see Sections 3.3 and 3.4 of [RFC3986]). A request in asterisk
	// form includes the value '*' for the ":path" pseudo-header field.
	//
	// This pseudo-header field MUST NOT be empty for "http" or "https"
	// URIs; "http" or "https" URIs that do not contain a path
	// component MUST include a value of '/'.
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with empty \":path\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers[2].Value = ""

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame that omits \":method\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = []hpack.HeaderField{
				headers[1], // :scheme
				headers[2], // :path
				headers[3], // :authority
			}

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers[1:]),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame that omits \":scheme\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = []hpack.HeaderField{
				headers[0], // :method
				headers[2], // :path
				headers[3], // :authority
			}

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame that omits \":path\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = []hpack.HeaderField{
				headers[0], // :method
				headers[1], // :scheme
				headers[3], // :authority
			}

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with duplicated \":method\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = append(headers, spec.HeaderField(":method", headers[0].Value))

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with duplicated \":scheme\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = append(headers, spec.HeaderField(":scheme", headers[1].Value))

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with duplicated \":method\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = append(headers, spec.HeaderField(":method", headers[2].Value))

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	return tg
}
<commit_msg>Test checking duplicated :method header is duplicated and :path one is missing<commit_after>package http2

import (
	"github.com/summerwind/h2spec/config"
	"github.com/summerwind/h2spec/spec"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"
)

func RequestPseudoHeaderFields() *spec.TestGroup {
	tg := NewTestGroup("8.1.2.3", "Request Pseudo-Header Fields")

	// The ":path" pseudo-header field includes the path and query
	// parts of the target URI (the "path-absolute" production and
	// optionally a '?' character followed by the "query" production
	// (see Sections 3.3 and 3.4 of [RFC3986]). A request in asterisk
	// form includes the value '*' for the ":path" pseudo-header field.
	//
	// This pseudo-header field MUST NOT be empty for "http" or "https"
	// URIs; "http" or "https" URIs that do not contain a path
	// component MUST include a value of '/'.
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with empty \":path\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers[2].Value = ""

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame that omits \":method\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = []hpack.HeaderField{
				headers[1], // :scheme
				headers[2], // :path
				headers[3], // :authority
			}

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers[1:]),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame that omits \":scheme\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = []hpack.HeaderField{
				headers[0], // :method
				headers[2], // :path
				headers[3], // :authority
			}

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame that omits \":path\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = []hpack.HeaderField{
				headers[0], // :method
				headers[1], // :scheme
				headers[3], // :authority
			}

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with duplicated \":method\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = append(headers, spec.HeaderField(":method", headers[0].Value))

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with duplicated \":scheme\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = append(headers, spec.HeaderField(":scheme", headers[1].Value))

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	// All HTTP/2 requests MUST include exactly one valid value for
	// the ":method", ":scheme", and ":path" pseudo-header fields,
	// unless it is a CONNECT request (Section 8.3).
	tg.AddTestCase(&spec.TestCase{
		Desc:        "Sends a HEADERS frame with duplicated \":path\" pseudo-header field",
		Requirement: "The endpoint MUST respond with a stream error of type PROTOCOL_ERROR.",
		Run: func(c *config.Config, conn *spec.Conn) error {
			var streamID uint32 = 1

			err := conn.Handshake()
			if err != nil {
				return err
			}

			headers := spec.CommonHeaders(c)
			headers = append(headers, spec.HeaderField(":path", headers[2].Value))

			hp := http2.HeadersFrameParam{
				StreamID:      streamID,
				EndStream:     true,
				EndHeaders:    true,
				BlockFragment: conn.EncodeHeaders(headers),
			}

			conn.WriteHeaders(hp)

			return spec.VerifyStreamError(conn, http2.ErrCodeProtocol)
		},
	})

	return tg
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru-admin authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/tsuru/tsuru/cmd"
	"io"
	"launchpad.net/gnuflag"
	"net/http"
	"strings"
)

type platformAdd struct {
	name       string
	dockerfile string
	fs         *gnuflag.FlagSet
}

func (p *platformAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-add",
		Usage:   "platform-add <platform name> [--dockerfile/-d Dockerfile]",
		Desc:    "Add new platform to tsuru.",
		MinArgs: 1,
	}
}

func (p *platformAdd) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
	body := fmt.Sprintf("name=%s&dockerfile=%s", name, p.dockerfile)
	url, err := cmd.GetURL("/platforms")
	request, err := http.NewRequest("POST", url, strings.NewReader(body))
	if err != nil {
		return err
	}
	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	var buf bytes.Buffer
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(io.MultiWriter(&buf, context.Stdout), response.Body) {
	}
	if strings.HasSuffix(buf.String(), "\nOK!\n") {
		fmt.Fprintf(context.Stdout, "Platform successfully added!\n")
		return nil
	}
	return errors.New("Failed to add new platform.\n")
}

func (p *platformAdd) Flags() *gnuflag.FlagSet {
	message := "The dockerfile url to create a platform"
	if p.fs == nil {
		p.fs = gnuflag.NewFlagSet("platform-add", gnuflag.ExitOnError)
		p.fs.StringVar(&p.dockerfile, "dockerfile", "", message)
		p.fs.StringVar(&p.dockerfile, "d", "", message)
	}
	return p.fs
}

type platformUpdate struct {
	name        string
	dockerfile  string
	forceUpdate bool
	fs          *gnuflag.FlagSet
}

func (p *platformUpdate) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-update",
		Usage:   "platform-update <platform name> [--dockerfile/-d Dockerfile]",
		Desc:    "Update a platform to tsuru.",
		MinArgs: 1,
	}
}

func (p *platformUpdate) Flags() *gnuflag.FlagSet {
	dockerfileMessage := "The dockerfile url to update a platform"
	if p.fs == nil {
		p.fs = gnuflag.NewFlagSet("platform-update", gnuflag.ExitOnError)
		p.fs.StringVar(&p.dockerfile, "dockerfile", "", dockerfileMessage)
		p.fs.StringVar(&p.dockerfile, "d", "", dockerfileMessage)
	}
	return p.fs
}

func (p *platformUpdate) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
	body := fmt.Sprintf("a=1&dockerfile=%s", p.dockerfile)
	url, err := cmd.GetURL("/platforms/" + name)
	request, err := http.NewRequest("PUT", url, strings.NewReader(body))
	if err != nil {
		return err
	}
	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	var buf bytes.Buffer
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(io.MultiWriter(&buf, context.Stdout), response.Body) {
	}
	if strings.HasSuffix(buf.String(), "\nOK!\n") {
		fmt.Fprintf(context.Stdout, "Platform successfully updated!\n")
		return nil
	}
	return errors.New("Failed to update platform!\n")
}

type platformRemove struct {
	name string
}

func (p *platformRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-remove",
		Usage:   "platform-remove <platform name>",
		Desc:    "Remove a platform from tsuru.",
		MinArgs: 1,
	}
}

func (p *platformRemove) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
	url, err := cmd.GetURL("/platforms/" + name)
	if err != nil {
		return err
	}
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, "Platform successfully removed!\n")
	return nil
}
<commit_msg>add failed message to platform-remove cmd<commit_after>// Copyright 2014 tsuru-admin authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/tsuru/tsuru/cmd"
	"io"
	"launchpad.net/gnuflag"
	"net/http"
	"strings"
)

type platformAdd struct {
	name       string
	dockerfile string
	fs         *gnuflag.FlagSet
}

func (p *platformAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-add",
		Usage:   "platform-add <platform name> [--dockerfile/-d Dockerfile]",
		Desc:    "Add new platform to tsuru.",
		MinArgs: 1,
	}
}

func (p *platformAdd) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
	body := fmt.Sprintf("name=%s&dockerfile=%s", name, p.dockerfile)
	url, err := cmd.GetURL("/platforms")
	request, err := http.NewRequest("POST", url, strings.NewReader(body))
	if err != nil {
		return err
	}
	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	var buf bytes.Buffer
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(io.MultiWriter(&buf, context.Stdout), response.Body) {
	}
	if strings.HasSuffix(buf.String(), "\nOK!\n") {
		fmt.Fprintf(context.Stdout, "Platform successfully added!\n")
		return nil
	}
	return errors.New("Failed to add new platform.\n")
}

func (p *platformAdd) Flags() *gnuflag.FlagSet {
	message := "The dockerfile url to create a platform"
	if p.fs == nil {
		p.fs = gnuflag.NewFlagSet("platform-add", gnuflag.ExitOnError)
		p.fs.StringVar(&p.dockerfile, "dockerfile", "", message)
		p.fs.StringVar(&p.dockerfile, "d", "", message)
	}
	return p.fs
}

type platformUpdate struct {
	name        string
	dockerfile  string
	forceUpdate bool
	fs          *gnuflag.FlagSet
}

func (p *platformUpdate) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-update",
		Usage:   "platform-update <platform name> [--dockerfile/-d Dockerfile]",
		Desc:    "Update a platform to tsuru.",
		MinArgs: 1,
	}
}

func (p *platformUpdate) Flags() *gnuflag.FlagSet {
	dockerfileMessage := "The dockerfile url to update a platform"
	if p.fs == nil {
		p.fs = gnuflag.NewFlagSet("platform-update", gnuflag.ExitOnError)
		p.fs.StringVar(&p.dockerfile, "dockerfile", "", dockerfileMessage)
		p.fs.StringVar(&p.dockerfile, "d", "", dockerfileMessage)
	}
	return p.fs
}

func (p *platformUpdate) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
	body := fmt.Sprintf("a=1&dockerfile=%s", p.dockerfile)
	url, err := cmd.GetURL("/platforms/" + name)
	request, err := http.NewRequest("PUT", url, strings.NewReader(body))
	if err != nil {
		return err
	}
	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	var buf bytes.Buffer
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(io.MultiWriter(&buf, context.Stdout), response.Body) {
	}
	if strings.HasSuffix(buf.String(), "\nOK!\n") {
		fmt.Fprintf(context.Stdout, "Platform successfully updated!\n")
		return nil
	}
	return errors.New("Failed to update platform!\n")
}

type platformRemove struct {
	name string
}

func (p *platformRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-remove",
		Usage:   "platform-remove <platform name>",
		Desc:    "Remove a platform from tsuru.",
		MinArgs: 1,
	}
}

func (p *platformRemove) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
	url, err := cmd.GetURL("/platforms/" + name)
	if err != nil {
		return err
	}
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		fmt.Fprintf(context.Stdout, "Failed to remove platform!\n")
		return err
	}
	fmt.Fprintf(context.Stdout, "Platform successfully removed!\n")
	return nil
}
<|endoftext|>