<commit_before>package events

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"strconv"

	"code.cloudfoundry.org/bbs/models"
	"github.com/gogo/protobuf/proto"
	"github.com/vito/go-sse/sse"
)

var ErrUnrecognizedEventType = errors.New("unrecognized event type")

var ErrSourceClosed = errors.New("source closed")

var ErrNoData = errors.New("event with no data")

type invalidPayloadError struct {
	payloadType string
	protoErr    error
}

func NewInvalidPayloadError(payloadType string, protoErr error) error {
	return invalidPayloadError{payloadType: payloadType, protoErr: protoErr}
}

func (e invalidPayloadError) Error() string {
	return fmt.Sprintf("invalid protobuf payload of type %s: %s", e.payloadType, e.protoErr.Error())
}

type rawEventSourceError struct {
	rawError error
}

func NewRawEventSourceError(rawError error) error {
	return rawEventSourceError{rawError: rawError}
}

func (e rawEventSourceError) Error() string {
	return fmt.Sprintf("raw event source error: %s", e.rawError.Error())
}

type closeError struct {
	err error
}

func NewCloseError(err error) error {
	return closeError{err: err}
}

func (e closeError) Error() string {
	return fmt.Sprintf("error closing raw source: %s", e.err.Error())
}

func NewEventFromModelEvent(eventID int, event models.Event) (sse.Event, error) {
	payload, err := proto.Marshal(event)
	if err != nil {
		return sse.Event{}, err
	}

	encodedPayload := base64.StdEncoding.EncodeToString(payload)
	return sse.Event{
		ID:   strconv.Itoa(eventID),
		Name: string(event.EventType()),
		Data: []byte(encodedPayload),
	}, nil
}

//go:generate counterfeiter -o eventfakes/fake_event_source.go . EventSource

// EventSource provides sequential access to a stream of events.
type EventSource interface {
	// Next reads the next event from the source. If the connection is lost, it
	// automatically reconnects.
	//
	// If the end of the stream is reached cleanly (which should actually never
	// happen), io.EOF is returned. If called after or during Close,
	// ErrSourceClosed is returned.
	Next() (models.Event, error)

	// Close releases the underlying response, interrupts any in-flight Next, and
	// prevents further calls to Next.
	Close() error
}

//go:generate counterfeiter -o eventfakes/fake_raw_event_source.go . RawEventSource

type RawEventSource interface {
	Next() (sse.Event, error)
	Close() error
}

type eventSource struct {
	rawEventSource RawEventSource
}

func NewEventSource(raw RawEventSource) EventSource {
	return &eventSource{
		rawEventSource: raw,
	}
}

func (e *eventSource) Next() (models.Event, error) {
	rawEvent, err := e.rawEventSource.Next()
	if err != nil {
		switch err {
		case io.EOF:
			return nil, err

		case sse.ErrSourceClosed:
			return nil, ErrSourceClosed

		default:
			return nil, NewRawEventSourceError(err)
		}
	}

	return parseRawEvent(rawEvent)
}

func (e *eventSource) Close() error {
	err := e.rawEventSource.Close()
	if err != nil {
		return NewCloseError(err)
	}

	return nil
}

func parseRawEvent(rawEvent sse.Event) (models.Event, error) {
	data, err := base64.StdEncoding.DecodeString(string(rawEvent.Data))
	if len(data) == 0 {
		return nil, NewInvalidPayloadError(rawEvent.Name, ErrNoData)
	} else if err != nil {
		return nil, NewInvalidPayloadError(rawEvent.Name, err)
	}

	switch rawEvent.Name {
	case models.EventTypeDesiredLRPCreated:
		event := new(models.DesiredLRPCreatedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeDesiredLRPChanged:
		event := new(models.DesiredLRPChangedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeDesiredLRPRemoved:
		event := new(models.DesiredLRPRemovedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPCreated:
		event := new(models.ActualLRPCreatedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPChanged:
		event := new(models.ActualLRPChangedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPRemoved:
		event := new(models.ActualLRPRemovedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPCrashed:
		event := new(models.ActualLRPCrashedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil
	}

	return nil, ErrUnrecognizedEventType
}
<commit_msg>reformat error variable declarations<commit_after>package events

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"strconv"

	"code.cloudfoundry.org/bbs/models"
	"github.com/gogo/protobuf/proto"
	"github.com/vito/go-sse/sse"
)

var (
	ErrUnrecognizedEventType = errors.New("unrecognized event type")
	ErrSourceClosed          = errors.New("source closed")
	ErrNoData                = errors.New("event with no data")
)

type invalidPayloadError struct {
	payloadType string
	protoErr    error
}

func NewInvalidPayloadError(payloadType string, protoErr error) error {
	return invalidPayloadError{payloadType: payloadType, protoErr: protoErr}
}

func (e invalidPayloadError) Error() string {
	return fmt.Sprintf("invalid protobuf payload of type %s: %s", e.payloadType, e.protoErr.Error())
}

type rawEventSourceError struct {
	rawError error
}

func NewRawEventSourceError(rawError error) error {
	return rawEventSourceError{rawError: rawError}
}

func (e rawEventSourceError) Error() string {
	return fmt.Sprintf("raw event source error: %s", e.rawError.Error())
}

type closeError struct {
	err error
}

func NewCloseError(err error) error {
	return closeError{err: err}
}

func (e closeError) Error() string {
	return fmt.Sprintf("error closing raw source: %s", e.err.Error())
}

func NewEventFromModelEvent(eventID int, event models.Event) (sse.Event, error) {
	payload, err := proto.Marshal(event)
	if err != nil {
		return sse.Event{}, err
	}

	encodedPayload := base64.StdEncoding.EncodeToString(payload)
	return sse.Event{
		ID:   strconv.Itoa(eventID),
		Name: string(event.EventType()),
		Data: []byte(encodedPayload),
	}, nil
}

//go:generate counterfeiter -o eventfakes/fake_event_source.go . EventSource

// EventSource provides sequential access to a stream of events.
type EventSource interface {
	// Next reads the next event from the source. If the connection is lost, it
	// automatically reconnects.
	//
	// If the end of the stream is reached cleanly (which should actually never
	// happen), io.EOF is returned. If called after or during Close,
	// ErrSourceClosed is returned.
	Next() (models.Event, error)

	// Close releases the underlying response, interrupts any in-flight Next, and
	// prevents further calls to Next.
	Close() error
}

//go:generate counterfeiter -o eventfakes/fake_raw_event_source.go . RawEventSource

type RawEventSource interface {
	Next() (sse.Event, error)
	Close() error
}

type eventSource struct {
	rawEventSource RawEventSource
}

func NewEventSource(raw RawEventSource) EventSource {
	return &eventSource{
		rawEventSource: raw,
	}
}

func (e *eventSource) Next() (models.Event, error) {
	rawEvent, err := e.rawEventSource.Next()
	if err != nil {
		switch err {
		case io.EOF:
			return nil, err

		case sse.ErrSourceClosed:
			return nil, ErrSourceClosed

		default:
			return nil, NewRawEventSourceError(err)
		}
	}

	return parseRawEvent(rawEvent)
}

func (e *eventSource) Close() error {
	err := e.rawEventSource.Close()
	if err != nil {
		return NewCloseError(err)
	}

	return nil
}

func parseRawEvent(rawEvent sse.Event) (models.Event, error) {
	data, err := base64.StdEncoding.DecodeString(string(rawEvent.Data))
	if len(data) == 0 {
		return nil, NewInvalidPayloadError(rawEvent.Name, ErrNoData)
	} else if err != nil {
		return nil, NewInvalidPayloadError(rawEvent.Name, err)
	}

	switch rawEvent.Name {
	case models.EventTypeDesiredLRPCreated:
		event := new(models.DesiredLRPCreatedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeDesiredLRPChanged:
		event := new(models.DesiredLRPChangedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeDesiredLRPRemoved:
		event := new(models.DesiredLRPRemovedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPCreated:
		event := new(models.ActualLRPCreatedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPChanged:
		event := new(models.ActualLRPChangedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPRemoved:
		event := new(models.ActualLRPRemovedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil

	case models.EventTypeActualLRPCrashed:
		event := new(models.ActualLRPCrashedEvent)
		err := proto.Unmarshal(data, event)
		if err != nil {
			return nil, NewInvalidPayloadError(rawEvent.Name, err)
		}

		return event, nil
	}

	return nil, ErrUnrecognizedEventType
}
<|endoftext|>


<commit_before>package cellnet

import (
	"reflect"
)

type DecodePacketHandler struct {
	BaseEventHandler
}

func (self *DecodePacketHandler) Call(ev *SessionEvent) {

	// 系统消息不做处理
	if !ev.IsSystemEvent() {

		meta := MessageMetaByID(ev.MsgID)

		if meta.Codec == nil {
			return
		}

		// 创建消息
		ev.Msg = reflect.New(meta.Type).Interface()

		// 解析消息
		meta.Codec.Decode(ev.Data, ev.Msg)
	}

}

func NewDecodePacketHandler() EventHandler {

	return &DecodePacketHandler{}
}
<commit_msg>decode报错<commit_after>package cellnet

import (
	"reflect"
)

type DecodePacketHandler struct {
	BaseEventHandler
}

func (self *DecodePacketHandler) Call(ev *SessionEvent) {

	// 系统消息不做处理
	if !ev.IsSystemEvent() {

		meta := MessageMetaByID(ev.MsgID)

		if meta.Codec == nil {
			return
		}

		// 创建消息
		ev.Msg = reflect.New(meta.Type).Interface()

		// 解析消息
		err := meta.Codec.Decode(ev.Data, ev.Msg)
		if err != nil {
			log.Errorln(err)
		}
	}

}

func NewDecodePacketHandler() EventHandler {

	return &DecodePacketHandler{}
}
<|endoftext|>


<commit_before>package logging

import (
	"fmt"
	"log"
	"os"
)

// Logger struct for logging
type Logger struct {
	tag string
}

// GetLogger get the logger
func GetLogger(tag string) *Logger {
	return &Logger{tag: tag}
}

// global log level
var logLv = INFO
var lgr = log.New(os.Stderr, "", log.LstdFlags)

// SetLogLevel congigure log settings
func SetLogLevel(lv level) {
	if logLv != lv {
		logLv = lv
		if logLv <= DEBUG {
			lgr.SetFlags(log.LstdFlags | log.Lshortfile)
		} else {
			lgr.SetFlags(log.LstdFlags)
		}
	}
}

func (logger *Logger) message(lv level, message string) string {
	return lv.String() + " <" + logger.tag + "> " + message
}

func (logger *Logger) log(lv level, message string, args ...interface{}) {
	if lv >= logLv {
		// caller -> Infof() -> log()
		const depth = 3
		lgr.Output(depth, fmt.Sprintf(logger.message(lv, message), args...))
	}
}

// Criticalf critical log
func (logger *Logger) Criticalf(m string, args ...interface{}) {
	logger.log(CRITICAL, m, args...)
}

// Errorf error log
func (logger *Logger) Errorf(m string, args ...interface{}) {
	logger.log(ERROR, m, args...)
}

// Warningf warning log
func (logger *Logger) Warningf(m string, args ...interface{}) {
	logger.log(WARNING, m, args...)
}

// Infof info log
func (logger *Logger) Infof(m string, args ...interface{}) {
	logger.log(INFO, m, args...)
}

// Debugf debug log
func (logger *Logger) Debugf(m string, args ...interface{}) {
	logger.log(DEBUG, m, args...)
}

// Tracef trace log for debugging details
func (logger *Logger) Tracef(m string, args ...interface{}) {
	logger.log(TRACE, m, args...)
}
<commit_msg>fix linting issue: return value is not checked<commit_after>package logging

import (
	"fmt"
	"log"
	"os"
)

// Logger struct for logging
type Logger struct {
	tag string
}

// GetLogger get the logger
func GetLogger(tag string) *Logger {
	return &Logger{tag: tag}
}

// global log level
var logLv = INFO
var lgr = log.New(os.Stderr, "", log.LstdFlags)

// SetLogLevel congigure log settings
func SetLogLevel(lv level) {
	if logLv != lv {
		logLv = lv
		if logLv <= DEBUG {
			lgr.SetFlags(log.LstdFlags | log.Lshortfile)
		} else {
			lgr.SetFlags(log.LstdFlags)
		}
	}
}

func (logger *Logger) message(lv level, message string) string {
	return lv.String() + " <" + logger.tag + "> " + message
}

func (logger *Logger) log(lv level, message string, args ...interface{}) {
	if lv >= logLv {
		// caller -> Infof() -> log()
		const depth = 3
		_ = lgr.Output(depth, fmt.Sprintf(logger.message(lv, message), args...))
	}
}

// Criticalf critical log
func (logger *Logger) Criticalf(m string, args ...interface{}) {
	logger.log(CRITICAL, m, args...)
}

// Errorf error log
func (logger *Logger) Errorf(m string, args ...interface{}) {
	logger.log(ERROR, m, args...)
}

// Warningf warning log
func (logger *Logger) Warningf(m string, args ...interface{}) {
	logger.log(WARNING, m, args...)
}

// Infof info log
func (logger *Logger) Infof(m string, args ...interface{}) {
	logger.log(INFO, m, args...)
}

// Debugf debug log
func (logger *Logger) Debugf(m string, args ...interface{}) {
	logger.log(DEBUG, m, args...)
}

// Tracef trace log for debugging details
func (logger *Logger) Tracef(m string, args ...interface{}) {
	logger.log(TRACE, m, args...)
}
<|endoftext|>


<commit_before>package controller

import (
	"errors"
	"gosaic/environment"
	"gosaic/model"
	"gosaic/service"
	"gosaic/util"
	"log"
	"os"
	"os/signal"
	"syscall"

	"gopkg.in/cheggaaa/pb.v1"
)

func PartialAspect(env environment.Environment, macroId int64) error {
	aspectService, err := env.AspectService()
	if err != nil {
		env.Printf("Error getting aspect service: %s\n", err.Error())
		return err
	}

	macroService, err := env.MacroService()
	if err != nil {
		env.Printf("Error getting macro service: %s\n", err.Error())
		return err
	}

	macroPartialService, err := env.MacroPartialService()
	if err != nil {
		env.Printf("Error getting macro partial service: %s\n", err.Error())
		return err
	}

	gidxPartialService, err := env.GidxPartialService()
	if err != nil {
		env.Printf("Error getting gidx partial service: %s\n", err.Error())
		return err
	}

	macro, err := macroService.Get(macroId)
	if err != nil {
		env.Printf("Error getting macro %d: %s\n", macroId, err.Error())
		return err
	}

	aspectIds, err := macroPartialService.AspectIds(macro.Id)
	if err != nil {
		env.Printf("Error getting aspect ids: %s\n", err.Error())
		return err
	}

	if len(aspectIds) == 0 {
		return nil
	}

	aspects, err := aspectService.FindIn(aspectIds)
	if err != nil {
		env.Printf("Error getting aspects: %s\n", err.Error())
		return err
	}

	err = createMissingGidxIndexes(env.Log(), gidxPartialService, aspects, env.Workers())
	if err != nil {
		env.Printf("Error creating index aspects: %s\n", err.Error())
		return err
	}

	return nil
}

func createMissingGidxIndexes(l *log.Logger, gidxPartialService service.GidxPartialService, aspects []*model.Aspect, workers int) error {
	count, err := gidxPartialService.CountMissing(aspects)
	if err != nil {
		return err
	}

	if count == 0 {
		return nil
	}

	l.Printf("Building %d indexed image partials...\n", count)
	bar := pb.StartNew(int(count))
	batchSize := workers * 4

	cancel := false
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		l.Println("Caught sigterm signal. Please wait to stop cleanly...")
		cancel = true
	}()

	for _, aspect := range aspects {
		for {
			if cancel {
				return errors.New("Cancelled")
			}

			gidxs, err := gidxPartialService.FindMissing(aspect, "gidx.id ASC", batchSize, 0)
			if err != nil {
				return err
			}

			if len(gidxs) == 0 {
				break
			}

			gidxPartials := buildGidxPartials(l, gidxs, aspect, workers, bar)

			if len(gidxPartials) > 0 {
				_, err := gidxPartialService.BulkInsert(gidxPartials)
				if err != nil {
					return err
				}
			}
		}
	}

	bar.Finish()

	return nil
}

func buildGidxPartials(l *log.Logger, gidxs []*model.Gidx, aspect *model.Aspect, workers int, bar *pb.ProgressBar) []*model.GidxPartial {
	gidxPartials := make([]*model.GidxPartial, len(gidxs))
	add := make(chan *model.GidxPartial)
	errs := make(chan error)
	sem := make(chan bool, workers)

	go func(gps []*model.GidxPartial) {
		idx := 0
		for i := 0; i < len(gps); i++ {
			select {
			case gp := <-add:
				gps[idx] = gp
				idx++
			case err := <-errs:
				l.Printf("Error building index partial: %s\n", err.Error())
			}
			bar.Increment()
			<-sem
		}
	}(gidxPartials)

	for _, gidx := range gidxs {
		sem <- true
		go func(g *model.Gidx) {
			gp, err := buildGidxPartial(g, aspect)
			if err != nil {
				errs <- err
				return
			}
			add <- gp
		}(gidx)
	}

	for i := 0; i < cap(sem); i++ {
		sem <- true
	}

	close(add)
	close(errs)
	close(sem)

	return gidxPartials
}

func buildGidxPartial(gidx *model.Gidx, aspect *model.Aspect) (*model.GidxPartial, error) {
	p := model.GidxPartial{
		GidxId:   gidx.Id,
		AspectId: aspect.Id,
	}

	pixels, err := util.GetAspectLab(gidx, aspect)
	if err != nil {
		return nil, err
	}
	p.Pixels = pixels

	err = p.EncodePixels()
	if err != nil {
		return nil, err
	}

	return &p, nil
}
<commit_msg>remove signal message<commit_after>package controller

import (
	"errors"
	"gosaic/environment"
	"gosaic/model"
	"gosaic/service"
	"gosaic/util"
	"log"
	"os"
	"os/signal"
	"syscall"

	"gopkg.in/cheggaaa/pb.v1"
)

func PartialAspect(env environment.Environment, macroId int64) error {
	aspectService, err := env.AspectService()
	if err != nil {
		env.Printf("Error getting aspect service: %s\n", err.Error())
		return err
	}

	macroService, err := env.MacroService()
	if err != nil {
		env.Printf("Error getting macro service: %s\n", err.Error())
		return err
	}

	macroPartialService, err := env.MacroPartialService()
	if err != nil {
		env.Printf("Error getting macro partial service: %s\n", err.Error())
		return err
	}

	gidxPartialService, err := env.GidxPartialService()
	if err != nil {
		env.Printf("Error getting gidx partial service: %s\n", err.Error())
		return err
	}

	macro, err := macroService.Get(macroId)
	if err != nil {
		env.Printf("Error getting macro %d: %s\n", macroId, err.Error())
		return err
	}

	aspectIds, err := macroPartialService.AspectIds(macro.Id)
	if err != nil {
		env.Printf("Error getting aspect ids: %s\n", err.Error())
		return err
	}

	if len(aspectIds) == 0 {
		return nil
	}

	aspects, err := aspectService.FindIn(aspectIds)
	if err != nil {
		env.Printf("Error getting aspects: %s\n", err.Error())
		return err
	}

	err = createMissingGidxIndexes(env.Log(), gidxPartialService, aspects, env.Workers())
	if err != nil {
		env.Printf("Error creating index aspects: %s\n", err.Error())
		return err
	}

	return nil
}

func createMissingGidxIndexes(l *log.Logger, gidxPartialService service.GidxPartialService, aspects []*model.Aspect, workers int) error {
	count, err := gidxPartialService.CountMissing(aspects)
	if err != nil {
		return err
	}

	if count == 0 {
		return nil
	}

	l.Printf("Building %d indexed image partials...\n", count)
	bar := pb.StartNew(int(count))
	batchSize := workers * 4

	cancel := false
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		cancel = true
	}()

	for _, aspect := range aspects {
		for {
			if cancel {
				return errors.New("Cancelled")
			}

			gidxs, err := gidxPartialService.FindMissing(aspect, "gidx.id ASC", batchSize, 0)
			if err != nil {
				return err
			}

			if len(gidxs) == 0 {
				break
			}

			gidxPartials := buildGidxPartials(l, gidxs, aspect, workers, bar)

			if len(gidxPartials) > 0 {
				_, err := gidxPartialService.BulkInsert(gidxPartials)
				if err != nil {
					return err
				}
			}
		}
	}

	bar.Finish()

	return nil
}

func buildGidxPartials(l *log.Logger, gidxs []*model.Gidx, aspect *model.Aspect, workers int, bar *pb.ProgressBar) []*model.GidxPartial {
	gidxPartials := make([]*model.GidxPartial, len(gidxs))
	add := make(chan *model.GidxPartial)
	errs := make(chan error)
	sem := make(chan bool, workers)

	go func(gps []*model.GidxPartial) {
		idx := 0
		for i := 0; i < len(gps); i++ {
			select {
			case gp := <-add:
				gps[idx] = gp
				idx++
			case err := <-errs:
				l.Printf("Error building index partial: %s\n", err.Error())
			}
			bar.Increment()
			<-sem
		}
	}(gidxPartials)

	for _, gidx := range gidxs {
		sem <- true
		go func(g *model.Gidx) {
			gp, err := buildGidxPartial(g, aspect)
			if err != nil {
				errs <- err
				return
			}
			add <- gp
		}(gidx)
	}

	for i := 0; i < cap(sem); i++ {
		sem <- true
	}

	close(add)
	close(errs)
	close(sem)

	return gidxPartials
}

func buildGidxPartial(gidx *model.Gidx, aspect *model.Aspect) (*model.GidxPartial, error) {
	p := model.GidxPartial{
		GidxId:   gidx.Id,
		AspectId: aspect.Id,
	}

	pixels, err := util.GetAspectLab(gidx, aspect)
	if err != nil {
		return nil, err
	}
	p.Pixels = pixels

	err = p.EncodePixels()
	if err != nil {
		return nil, err
	}

	return &p, nil
}
<|endoftext|>


<commit_before>package lang

import (
    . "jvmgo/any"
    "jvmgo/rtda"
    rtc "jvmgo/rtda/class"
)

func init() {
    _class(desiredAssertionStatus0, "desiredAssertionStatus0",  "(Ljava/lang/Class;)Z")
    _class(getClassLoader0,         "getClassLoader0",          "()Ljava/lang/ClassLoader;")
    _class(getName0,                "getName0",                 "()Ljava/lang/String;")
    _class(getPrimitiveClass,       "getPrimitiveClass",        "(Ljava/lang/String;)Ljava/lang/Class;")
}

func _class(method Any, name, desc string) {
    rtc.RegisterNativeMethod("java/lang/Class", name, desc, method)
}

func desiredAssertionStatus0(frame *rtda.Frame) {
    // todo
    stack := frame.OperandStack()
    _ = stack.PopRef() // this
    stack.PushBoolean(false)
}

func getClassLoader0(frame *rtda.Frame) {
    // todo
    stack := frame.OperandStack()
    _ = stack.PopRef() // this
    stack.PushRef(nil)
}

func getName0(frame *rtda.Frame) {
    panic("getName0")
}

//static native Class<?> getPrimitiveClass(String name);
//getPrimitiveClass(Ljava/lang/String;)Ljava/lang/Class;
func getPrimitiveClass(frame *rtda.Frame) {
    // todo
    panic("getPrimitiveClass")
}
<commit_msg>hack java.lang.Class.getPrimitiveClass()<commit_after>package lang

import (
    . "jvmgo/any"
    "jvmgo/rtda"
    rtc "jvmgo/rtda/class"
)

func init() {
    _class(desiredAssertionStatus0, "desiredAssertionStatus0",  "(Ljava/lang/Class;)Z")
    _class(getClassLoader0,         "getClassLoader0",          "()Ljava/lang/ClassLoader;")
    _class(getName0,                "getName0",                 "()Ljava/lang/String;")
    _class(getPrimitiveClass,       "getPrimitiveClass",        "(Ljava/lang/String;)Ljava/lang/Class;")
}

func _class(method Any, name, desc string) {
    rtc.RegisterNativeMethod("java/lang/Class", name, desc, method)
}

func desiredAssertionStatus0(frame *rtda.Frame) {
    // todo
    stack := frame.OperandStack()
    _ = stack.PopRef() // this
    stack.PushBoolean(false)
}

func getClassLoader0(frame *rtda.Frame) {
    // todo
    stack := frame.OperandStack()
    _ = stack.PopRef() // this
    stack.PushRef(nil)
}

func getName0(frame *rtda.Frame) {
    panic("getName0")
}

//static native Class<?> getPrimitiveClass(String name);
//getPrimitiveClass(Ljava/lang/String;)Ljava/lang/Class;
func getPrimitiveClass(frame *rtda.Frame) {
    // todo
    stack := frame.OperandStack()
    _ = stack.PopRef() // name
    stack.PushRef(nil) // todo
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package confighistory

import (
	"bytes"
	"math"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestEncodeDecodeCompositeKey(t *testing.T) {
	sampleKeys := []*compositeKey{
		{ns: "ns0", key: "key0", blockNum: 0},
		{ns: "ns1", key: "key1", blockNum: 1},
		{ns: "ns2", key: "key2", blockNum: 99},
		{ns: "ns3", key: "key3", blockNum: math.MaxUint64},
	}
	for _, k := range sampleKeys {
		k1 := decodeCompositeKey(encodeCompositeKey(k.ns, k.key, k.blockNum))
		assert.Equal(t, k, k1)
	}
}

func TesComparetEncodedHight(t *testing.T) {
	assert.Equal(t, bytes.Compare(encodeBlockNum(20), encodeBlockNum(40)), 1)
	assert.Equal(t, bytes.Compare(encodeBlockNum(40), encodeBlockNum(10)), -1)
}

func TestQueries(t *testing.T) {
	testDBPath := "/tmp/fabric/core/ledger/confighistory"
	deleteTestPath(t, testDBPath)
	provider := newDBProvider(testDBPath)
	defer deleteTestPath(t, testDBPath)

	db := provider.getDB("ledger1")
	// A query on an empty store
	checkEntryAt(t, "testcase-query1", db, "ns1", "key1", 45, nil)
	// test data
	sampleData := []*compositeKV{
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 40}, []byte("val1_40")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 30}, []byte("val1_30")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 20}, []byte("val1_20")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 10}, []byte("val1_10")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 0}, []byte("val1_0")},
	}
	populateDBWithSampleData(t, db, sampleData)
	// access most recent entry below ht=[45] - expected item is the one committed at ht = 40
	checkRecentEntryBelow(t, "testcase-query2", db, "ns1", "key1", 45, sampleData[0])
	checkRecentEntryBelow(t, "testcase-query3", db, "ns1", "key1", 35, sampleData[1])
	checkRecentEntryBelow(t, "testcase-query4", db, "ns1", "key1", 30, sampleData[2])
	checkRecentEntryBelow(t, "testcase-query5", db, "ns1", "key1", 10, sampleData[4])

	checkEntryAt(t, "testcase-query6", db, "ns1", "key1", 40, sampleData[0])
	checkEntryAt(t, "testcase-query7", db, "ns1", "key1", 30, sampleData[1])
	checkEntryAt(t, "testcase-query8", db, "ns1", "key1", 0, sampleData[4])
	checkEntryAt(t, "testcase-query9", db, "ns1", "key1", 35, nil)
	checkEntryAt(t, "testcase-query10", db, "ns1", "key1", 45, nil)
}

func populateDBWithSampleData(t *testing.T, db *db, sampledata []*compositeKV) {
	batch := newBatch()
	for _, data := range sampledata {
		batch.add(data.ns, data.key, data.blockNum, data.value)
	}
	assert.NoError(t, db.writeBatch(batch, true))
}

func checkRecentEntryBelow(t *testing.T, testcase string, db *db, ns, key string, commitHt uint64, expectedOutput *compositeKV) {
	t.Run(testcase,
		func(t *testing.T) {
			kv, err := db.mostRecentEntryBelow(commitHt, ns, key)
			assert.NoError(t, err)
			assert.Equal(t, expectedOutput, kv)
		})
}

func checkEntryAt(t *testing.T, testcase string, db *db, ns, key string, commitHt uint64, expectedOutput *compositeKV) {
	t.Run(testcase,
		func(t *testing.T) {
			kv, err := db.entryAt(commitHt, ns, key)
			assert.NoError(t, err)
			assert.Equal(t, expectedOutput, kv)
		})
}

func deleteTestPath(t *testing.T, dbPath string) {
	err := os.RemoveAll(dbPath)
	assert.NoError(t, err)
}
<commit_msg>[FAB-11959] improve unit tests for confighistory<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package confighistory

import (
	"bytes"
	"math"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestEncodeDecodeCompositeKey(t *testing.T) {
	sampleKeys := []*compositeKey{
		{ns: "ns0", key: "key0", blockNum: 0},
		{ns: "ns1", key: "key1", blockNum: 1},
		{ns: "ns2", key: "key2", blockNum: 99},
		{ns: "ns3", key: "key3", blockNum: math.MaxUint64},
	}
	for _, k := range sampleKeys {
		k1 := decodeCompositeKey(encodeCompositeKey(k.ns, k.key, k.blockNum))
		assert.Equal(t, k, k1)
	}
}

func TestCompareEncodedHeight(t *testing.T) {
	assert.Equal(t, bytes.Compare(encodeBlockNum(20), encodeBlockNum(40)), 1)
	assert.Equal(t, bytes.Compare(encodeBlockNum(40), encodeBlockNum(10)), -1)
}

func TestQueries(t *testing.T) {
	testDBPath := "/tmp/fabric/core/ledger/confighistory"
	deleteTestPath(t, testDBPath)
	provider := newDBProvider(testDBPath)
	defer deleteTestPath(t, testDBPath)

	db := provider.getDB("ledger1")
	// A query on an empty store
	checkEntryAt(t, "testcase-query1", db, "ns1", "key1", 45, nil)
	// test data
	sampleData := []*compositeKV{
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 40}, []byte("val1_40")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 30}, []byte("val1_30")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 20}, []byte("val1_20")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 10}, []byte("val1_10")},
		{&compositeKey{ns: "ns1", key: "key1", blockNum: 0}, []byte("val1_0")},
	}
	populateDBWithSampleData(t, db, sampleData)
	// access most recent entry below ht=[45] - expected item is the one committed at ht = 40
	checkRecentEntryBelow(t, "testcase-query2", db, "ns1", "key1", 45, sampleData[0])
	checkRecentEntryBelow(t, "testcase-query3", db, "ns1", "key1", 35, sampleData[1])
	checkRecentEntryBelow(t, "testcase-query4", db, "ns1", "key1", 30, sampleData[2])
	checkRecentEntryBelow(t, "testcase-query5", db, "ns1", "key1", 10, sampleData[4])

	checkEntryAt(t, "testcase-query6", db, "ns1", "key1", 40, sampleData[0])
	checkEntryAt(t, "testcase-query7", db, "ns1", "key1", 30, sampleData[1])
	checkEntryAt(t, "testcase-query8", db, "ns1", "key1", 0, sampleData[4])
	checkEntryAt(t, "testcase-query9", db, "ns1", "key1", 35, nil)
	checkEntryAt(t, "testcase-query10", db, "ns1", "key1", 45, nil)
}

func populateDBWithSampleData(t *testing.T, db *db, sampledata []*compositeKV) {
	batch := newBatch()
	for _, data := range sampledata {
		batch.add(data.ns, data.key, data.blockNum, data.value)
	}
	assert.NoError(t, db.writeBatch(batch, true))
}

func checkRecentEntryBelow(t *testing.T, testcase string, db *db, ns, key string, commitHt uint64, expectedOutput *compositeKV) {
	t.Run(testcase,
		func(t *testing.T) {
			kv, err := db.mostRecentEntryBelow(commitHt, ns, key)
			assert.NoError(t, err)
			assert.Equal(t, expectedOutput, kv)
		})
}

func checkEntryAt(t *testing.T, testcase string, db *db, ns, key string, commitHt uint64, expectedOutput *compositeKV) {
	t.Run(testcase,
		func(t *testing.T) {
			kv, err := db.entryAt(commitHt, ns, key)
			assert.NoError(t, err)
			assert.Equal(t, expectedOutput, kv)
		})
}

func deleteTestPath(t *testing.T, dbPath string) {
	err := os.RemoveAll(dbPath)
	assert.NoError(t, err)
}
<|endoftext|>


<commit_before>package middleware_test

import (
	"encoding/base64"
	"fmt"
	"net/http"
	htt "net/http/httptest"
	"net/url"
	"os"
	"testing"
	"time"

	"github.com/synapse-garden/sg-proto/auth"
	"github.com/synapse-garden/sg-proto/rest/middleware"
	"github.com/synapse-garden/sg-proto/rest/ws"
	"github.com/synapse-garden/sg-proto/store"
	sgt "github.com/synapse-garden/sg-proto/testing"

	"github.com/boltdb/bolt"
	"github.com/julienschmidt/httprouter"
	xws "golang.org/x/net/websocket"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MiddlewareSuite struct {
	db     *bolt.DB
	tmpDir string
}

var _ = Suite(&MiddlewareSuite{})

func (s *MiddlewareSuite) SetUpTest(c *C) {
	db, tmpDir, err := sgt.TempDB("sg-test")
	c.Assert(err, IsNil)
	c.Assert(db.Update(store.Wrap(
		store.Migrate(store.VerCurrent),
		store.SetupBuckets(
			auth.SessionBucket,
			auth.RefreshBucket,
			auth.ContextBucket,
		),
	)), IsNil)
	s.db, s.tmpDir = db, tmpDir
}

func (s *MiddlewareSuite) TearDownTest(c *C) {
	if db := s.db; db != nil {
		c.Assert(sgt.CleanupDB(db), IsNil)
		c.Assert(os.Remove(s.tmpDir), IsNil)
	}
}

func (s *MiddlewareSuite) TestAuthUser(c *C) {
	sess := &auth.Session{}
	c.Assert(s.db.Update(auth.NewSession(
		sess,
		time.Now().Add(1*time.Hour),
		time.Hour,
		auth.NewToken(auth.BearerType),
		auth.NewToken(auth.RefreshType),
		"friendo",
	)), IsNil)

	validToken := base64.StdEncoding.EncodeToString(sess.Token)

	r := htt.NewRequest("GET", "/foo", nil)
	r.Header.Set(string(middleware.AuthHeader), fmt.Sprintf("%s %s", auth.BearerType, validToken))
	h := func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		c.Check(middleware.CtxGetUserID(r), Equals, "friendo")
		w.Write([]byte("ok"))
	}
	w := htt.NewRecorder()
	middleware.AuthUser(h, s.db, middleware.CtxSetUserID)(w, r, nil)
	c.Check(w.Body.String(), Equals, "ok")
	// A request to an endpoint with the given auth scheme should
	// be rejected if header["Authorization"] is not "Bearer" and
	// present in the database.
}

func (s *MiddlewareSuite) TestAuthWS(c *C) {
	sess := &auth.Session{}
	c.Assert(s.db.Update(auth.NewSession(
		sess,
		time.Now().Add(1*time.Hour),
		time.Hour,
		auth.NewToken(auth.BearerType),
		auth.NewToken(auth.RefreshType),
		"friendo",
	)), IsNil)

	validToken := base64.RawURLEncoding.EncodeToString(sess.Token)
	c.Logf("valid WS token (base64.RawURLEncoding): %#q", validToken)

	h := func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		userID := middleware.CtxGetUserID(r)
		c.Check(userID, Equals, "friendo")
		xws.Server{
			Handshake: ws.Check,
			Handler: func(c *xws.Conn) {
				c.Write([]byte(`"hello ` + userID + `"`))
				c.Close()
			},
		}.ServeHTTP(w, r)
	}
	rt := httprouter.New()
	rt.GET("/foo", middleware.AuthWSUser(h, s.db, middleware.CtxSetUserID))
	srv := htt.NewServer(rt)

	wsURL, err := url.Parse(srv.URL + "/foo")
	c.Assert(err, IsNil)
	wsURL.Scheme = "ws"

	conn, err := xws.DialConfig(&xws.Config{
		Location: wsURL,
		Origin:   &url.URL{},
		Version:  xws.ProtocolVersionHybi13,
		// {"Sec-WebSocket-Protocol": "Bearer+12345,..."
		Protocol: []string{"Bearer+" + validToken},
	})
	c.Assert(err, IsNil)

	var got string
	c.Assert(xws.JSON.Receive(conn, &got), IsNil)
	c.Check(got, Equals, "hello friendo")
}

func (s *MiddlewareSuite) TestGetToken(c *C) {
	validToken := auth.NewToken(auth.BearerType)
	validBase64 := base64.StdEncoding.EncodeToString(validToken)

	c.Logf("valid token: %+v", validToken)

	for i, test := range []struct {
		given     string
		kind      auth.TokenType
		expect    []byte
		expectErr string
	}{{
		given:     "",
		expectErr: `no "Bearer" token provided`,
	}, {
		given:     "Basic",
		expectErr: `no "Bearer" token provided`,
	}, {
		given:     "Basic 12345 hoho",
		expectErr: `too many token fields`,
	}, {
		given:     "Basic 12345",
		expectErr: `invalid token kind "Basic", expected "Bearer"`,
	}, {
		given: "Bearer 12345",
		expectErr: "invalid token `12345`: illegal base64 " +
			"data at input byte 4",
	}, {
		given:     "Refresh " + validBase64,
		kind:      auth.RefreshType,
		expectErr: `invalid token kind "Refresh", expected "Bearer"`,
	}, {
		given:  "Bearer " + validBase64,
		expect: validToken,
	}} {
		c.Logf("test %d: %s", i, test.given)
		got, err := middleware.GetToken(auth.BearerType, test.given)
		if test.expectErr != "" {
			c.Check(err, ErrorMatches, test.expectErr)
			continue
		}
		c.Assert(err, IsNil)
		c.Check(got, DeepEquals, test.expect)
	}
}
<commit_msg>Add rest/middleware test for invalid Bearer tokens<commit_after>package middleware_test

import (
	"encoding/base64"
	"fmt"
	"net/http"
	htt "net/http/httptest"
	"net/url"
	"os"
	"testing"
	"time"

	"github.com/synapse-garden/sg-proto/auth"
	"github.com/synapse-garden/sg-proto/rest/middleware"
	"github.com/synapse-garden/sg-proto/rest/ws"
	"github.com/synapse-garden/sg-proto/store"
	sgt "github.com/synapse-garden/sg-proto/testing"

	"github.com/boltdb/bolt"
	"github.com/julienschmidt/httprouter"
	uuid "github.com/satori/go.uuid"
	xws "golang.org/x/net/websocket"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MiddlewareSuite struct {
	db     *bolt.DB
	tmpDir string
}

var _ = Suite(&MiddlewareSuite{})

func (s *MiddlewareSuite) SetUpTest(c *C) {
	db, tmpDir, err := sgt.TempDB("sg-test")
	c.Assert(err, IsNil)
	c.Assert(db.Update(store.Wrap(
		store.Migrate(store.VerCurrent),
		store.SetupBuckets(
			auth.SessionBucket,
			auth.RefreshBucket,
			auth.ContextBucket,
		),
	)), IsNil)
	s.db, s.tmpDir = db, tmpDir
}

func (s *MiddlewareSuite) TearDownTest(c *C) {
	if db := s.db; db != nil {
		c.Assert(sgt.CleanupDB(db), IsNil)
		c.Assert(os.Remove(s.tmpDir), IsNil)
	}
}

func (s *MiddlewareSuite) TestAuthUser(c *C) {
	sess := &auth.Session{}
	c.Assert(s.db.Update(auth.NewSession(
		sess,
		time.Now().Add(1*time.Hour),
		time.Hour,
		auth.NewToken(auth.BearerType),
		auth.NewToken(auth.RefreshType),
		"friendo",
	)), IsNil)

	validToken := base64.StdEncoding.EncodeToString(sess.Token)

	r := htt.NewRequest("GET", "/foo", nil)
	r.Header.Set(string(middleware.AuthHeader), fmt.Sprintf("%s %s", auth.BearerType, validToken))
	h := func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		c.Check(middleware.CtxGetUserID(r), Equals, "friendo")
		w.Write([]byte("ok"))
	}
	w := htt.NewRecorder()
	middleware.AuthUser(h, s.db, middleware.CtxSetUserID)(w, r, nil)
	c.Check(w.Body.String(), Equals, "ok")

	// TODO:
	// A request to an endpoint with the given auth scheme should
	// be rejected if header["Authorization"] is not "Bearer" and
	// present in the database.

	uu := uuid.NewV4()
	invalidToken := base64.StdEncoding.EncodeToString(uu[:])
	r = htt.NewRequest("GET", "/foo", nil)
	r.Header.Set(string(middleware.AuthHeader), fmt.Sprintf("%s %s", auth.BearerType, invalidToken))
	h = func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		c.Check(middleware.CtxGetUserID(r), Not(Equals), "friendo")
		w.Write([]byte("ok"))
	}
	w = htt.NewRecorder()
	middleware.AuthUser(h, s.db, middleware.CtxSetUserID)(w, r, nil)
	c.Check(w.Body.String(), Equals, "invalid session token\n")
}

func (s *MiddlewareSuite) TestAuthWS(c *C) {
	sess := &auth.Session{}
	c.Assert(s.db.Update(auth.NewSession(
		sess,
		time.Now().Add(1*time.Hour),
		time.Hour,
		auth.NewToken(auth.BearerType),
		auth.NewToken(auth.RefreshType),
		"friendo",
	)), IsNil)

	validToken := base64.RawURLEncoding.EncodeToString(sess.Token)
	c.Logf("valid WS token (base64.RawURLEncoding): %#q", validToken)

	h := func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		userID := middleware.CtxGetUserID(r)
		c.Check(userID, Equals, "friendo")
		xws.Server{
			Handshake: ws.Check,
			Handler: func(c *xws.Conn) {
				c.Write([]byte(`"hello ` + userID + `"`))
				c.Close()
			},
		}.ServeHTTP(w, r)
	}
	rt := httprouter.New()
	rt.GET("/foo", middleware.AuthWSUser(h, s.db, middleware.CtxSetUserID))
	srv := htt.NewServer(rt)

	wsURL, err := url.Parse(srv.URL + "/foo")
	c.Assert(err, IsNil)
	wsURL.Scheme = "ws"

	conn, err := xws.DialConfig(&xws.Config{
		Location: wsURL,
		Origin:   &url.URL{},
		Version:  xws.ProtocolVersionHybi13,
		// {"Sec-WebSocket-Protocol": "Bearer+12345,..."
		Protocol: []string{"Bearer+" + validToken},
	})
	c.Assert(err, IsNil)

	var got string
	c.Assert(xws.JSON.Receive(conn, &got), IsNil)
	c.Check(got, Equals, "hello friendo")
}

func (s *MiddlewareSuite) TestGetToken(c *C) {
	validToken := auth.NewToken(auth.BearerType)
	validBase64 := base64.StdEncoding.EncodeToString(validToken)

	c.Logf("valid token: %+v", validToken)

	for i, test := range []struct {
		given     string
		kind      auth.TokenType
		expect    []byte
		expectErr string
	}{{
		given:     "",
		expectErr: `no "Bearer" token provided`,
	}, {
		given:     "Basic",
		expectErr: `no "Bearer" token provided`,
	}, {
		given:     "Basic 12345 hoho",
		expectErr: `too many token fields`,
	}, {
		given:     "Basic 12345",
		expectErr: `invalid token kind "Basic", expected "Bearer"`,
	}, {
		given: "Bearer 12345",
		expectErr: "invalid token `12345`: illegal base64 " +
			"data at input byte 4",
	}, {
		given:     "Refresh " + validBase64,
		kind:      auth.RefreshType,
		expectErr: `invalid token kind "Refresh", expected "Bearer"`,
	}, {
		given:  "Bearer " + validBase64,
		expect: validToken,
	}} {
		c.Logf("test %d: %s", i, test.given)
		got, err := middleware.GetToken(auth.BearerType, test.given)
		if test.expectErr != "" {
			c.Check(err, ErrorMatches, test.expectErr)
			continue
		}
		c.Assert(err, IsNil)
		c.Check(got, DeepEquals, test.expect)
	}
}
<|endoftext|>


<commit_before>//go:build !windows
// +build !windows

// This file contains tests for signal handling on Unix.
// Based on code from https://github.com/marco-m/timeit
// Due to how signals work, for robustness we always spawn a separate process;
// we never send signals to the test process.

package task_test

import (
	"bytes"
	"errors"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"testing"
	"time"
)

var (
	SLEEPIT, _ = filepath.Abs("./tmp/sleepit")
)

func TestSignalSentToProcessGroup(t *testing.T) {
	task, err := filepath.Abs("./bin/task")
	if err != nil {
		t.Fatal(err)
	}
	testCases := map[string]struct {
		args     []string
		sendSigs int
		want     []string
		notWant  []string
	}{
		// regression:
		// - child is terminated, immediately, by "context canceled" (another bug???)
		"child does not handle sigint: receives sigint and terminates immediately": {
			args:     []string{task, "--", SLEEPIT, "default", "-sleep=10s"},
			sendSigs: 1,
			want: []string{
				"sleepit: ready\n",
				"sleepit: work started\n",
				"task: signal received: interrupt\n",
				// 130 = 128 + SIGINT
				"task: Failed to run task \"default\": exit status 130\n",
			},
			notWant: []string{
				"task: Failed to run task \"default\": context canceled\n",
			},
		},
		// 2 regressions:
		// - child receives 2 signals instead of 1
		// - child is terminated, immediately, by "context canceled" (another bug???)
		// TODO we need -cleanup=2s only to show reliably the bug; once the fix is committed,
		// we can use -cleanup=50ms to speed the test up
		"child intercepts sigint: receives sigint and does cleanup": {
			args:     []string{task, "--", SLEEPIT, "handle", "-sleep=10s", "-cleanup=2s"},
			sendSigs: 1,
			want: []string{
				"sleepit: ready\n",
				"sleepit: work started\n",
				"task: signal received: interrupt\n",
				"sleepit: got signal=interrupt count=1\n",
				"sleepit: work canceled\n",
				"sleepit: cleanup started\n",
				"sleepit: cleanup done\n",
				"task: Failed to run task \"default\": exit status 3\n",
			},
			notWant: []string{
				"sleepit: got signal=interrupt count=2\n",
				"task: Failed to run task \"default\": context canceled\n",
			},
		},
		// regression: child receives 2 signal instead of 1 and thus terminates abruptly
		"child simulates terraform: receives 1 sigint and does cleanup": {
			args:     []string{task, "--", SLEEPIT, "handle", "-term-after=2", "-sleep=10s", "-cleanup=50ms"},
			sendSigs: 1,
			want: []string{
				"sleepit: ready\n",
				"sleepit: work started\n",
				"task: signal received: interrupt\n",
				"sleepit: got signal=interrupt count=1\n",
				"sleepit: work canceled\n",
				"sleepit: cleanup started\n",
				"sleepit: cleanup done\n",
				"task: Failed to run task \"default\": exit status 3\n",
			},
			notWant: []string{
				"sleepit: got signal=interrupt count=2\n",
				"sleepit: cleanup canceled\n",
				"task: Failed to run task \"default\": exit status 4\n",
			},
		},
	}

	for name, tc := range testCases {
		t.Run(name, func(t *testing.T) {
			var out bytes.Buffer
			sut := exec.Command(tc.args[0], tc.args[1:]...)
			sut.Stdout = &out
			sut.Stderr = &out
			sut.Dir = "testdata/ignore_signals"
			// Create a new process group by setting the process group ID of the child
			// to the child PID.
			// By default, the child would inherit the process group of the parent, but
			// we want to avoid this, to protect the parent (the test process) from the
			// signal that this test will send. More info in the comments below for
			// syscall.Kill().
			sut.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0}

			if err := sut.Start(); err != nil {
				t.Fatalf("starting the SUT process: %v", err)
			}

			// After the child is started, we want to avoid a race condition where we send
			// it a signal before it had time to setup its own signal handlers. Sleeping
			// is way too flaky, instead we parse the child output until we get a line
			// that we know is printed after the signal handlers are installed...
			ready := false
			timeout := time.Duration(time.Second)
			start := time.Now()
			for time.Since(start) < timeout {
				if strings.Contains(out.String(), "sleepit: ready\n") {
					ready = true
					break
				}
				time.Sleep(10 * time.Millisecond)
			}
			if !ready {
				t.Fatalf("sleepit not ready after %v\n"+
					"additional information:\n"+
					"  output:\n%s",
					timeout, out.String())
			}

			// When we have a running program in a shell and type CTRL-C, the tty driver
			// will send a SIGINT signal to all the processes in the foreground process
			// group (see https://en.wikipedia.org/wiki/Process_group).
			//
			// Here we want to emulate this behavior: send SIGINT to the process group of
			// the test executable. Although Go for some reasons doesn't wrap the
			// killpg(2) system call, what works is using syscall.Kill(-PID, SIGINT),
			// where the negative PID means the corresponding process group. Note that
			// this negative PID works only as long as the caller of the kill(2) system
			// call has a different PID, which is the case for this test.
			for i := 1; i <= tc.sendSigs; i++ {
				if err := syscall.Kill(-sut.Process.Pid, syscall.SIGINT); err != nil {
					t.Fatalf("sending INT signal to the process group: %v", err)
				}
				time.Sleep(1 * time.Millisecond)
			}

			err := sut.Wait()

			// In case of a subprocess failing, Task always returns exit code 1, not the
			// exit code returned by the subprocess. This is understandable, since Task
			// supports parallel execution: if two parallel subprocess fail, each with a
			// different exit code, which one should Task report? This would be a race.
			var wantErr *exec.ExitError
			const wantExitStatus = 1 // Task always returns exit code 1 in case of error
			if errors.As(err, &wantErr) {
				if wantErr.ExitCode() != wantExitStatus {
					t.Errorf(
						"waiting for child process: got exit status %v; want %d\n"+
							"additional information:\n"+
							"  process state: %q",
						wantErr.ExitCode(), wantExitStatus, wantErr.String())
				}
			} else {
				t.Errorf("waiting for child process: got unexpected error type %v (%T); want (%T)",
					err, err, wantErr)
			}

			gotLines := strings.SplitAfter(out.String(), "\n")
			notFound := listDifference(tc.want, gotLines)
			if len(notFound) > 0 {
				t.Errorf("\nwanted but not found:\n%v", notFound)
			}

			found := listIntersection(tc.notWant, gotLines)
			if len(found) > 0 {
				t.Errorf("\nunwanted but found:\n%v", found)
			}

			if len(notFound) > 0 || len(found) > 0 {
				t.Errorf("\noutput:\n%v", gotLines)
			}
		})
	}
}

// Return the difference of the two lists: the elements that are present in the first
// list, but not in the second one. The notion of presence is not with `=` but with
// string.Contains(l2, l1).
// FIXME this does not enforce ordering. We might want to support both.
func listDifference(lines1, lines2 []string) []string {
	difference := []string{}
	for _, l1 := range lines1 {
		found := false
		for _, l2 := range lines2 {
			if strings.Contains(l2, l1) {
				found = true
				break
			}
		}
		if !found {
			difference = append(difference, l1)
		}
	}

	return difference
}

// Return the intersection of the two lists: the elements that are present in both lists.
// The notion of presence is not with '=' but with string.Contains(l2, l1)
// FIXME this does not enforce ordering. We might want to support both.
func listIntersection(lines1, lines2 []string) []string {
	intersection := []string{}
	for _, l1 := range lines1 {
		for _, l2 := range lines2 {
			if strings.Contains(l2, l1) {
				intersection = append(intersection, l1)
				break
			}
		}
	}

	return intersection
}
<commit_msg>Make signal test work both locally and in CI<commit_after>//go:build !windows
// +build !windows

// This file contains tests for signal handling on Unix.
// Based on code from https://github.com/marco-m/timeit
// Due to how signals work, for robustness we always spawn a separate process;
// we never send signals to the test process.

package task_test

import (
	"bytes"
	"errors"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"testing"
	"time"
)

var (
	SLEEPIT, _ = filepath.Abs("./tmp/sleepit")
)

func TestSignalSentToProcessGroup(t *testing.T) {
	task, err := getTaskPath()
	if err != nil {
		t.Fatal(err)
	}

	testCases := map[string]struct {
		args     []string
		sendSigs int
		want     []string
		notWant  []string
	}{
		// regression:
		// - child is terminated, immediately, by "context canceled" (another bug???)
		"child does not handle sigint: receives sigint and terminates immediately": {
			args:     []string{task, "--", SLEEPIT, "default", "-sleep=10s"},
			sendSigs: 1,
			want: []string{
				"sleepit: ready\n",
				"sleepit: work started\n",
				"task: signal received: interrupt\n",
				// 130 = 128 + SIGINT
				"task: Failed to run task \"default\": exit status 130\n",
			},
			notWant: []string{
				"task: Failed to run task \"default\": context canceled\n",
			},
		},
		// 2 regressions:
		// - child receives 2 signals instead of 1
		// - child is terminated, immediately, by "context canceled" (another bug???)
		// TODO we need -cleanup=2s only to show reliably the bug; once the fix is committed,
		// we can use -cleanup=50ms to speed the test up
		"child intercepts sigint: receives sigint and does cleanup": {
			args:     []string{task, "--", SLEEPIT, "handle", "-sleep=10s", "-cleanup=2s"},
			sendSigs: 1,
			want: []string{
				"sleepit: ready\n",
				"sleepit: work started\n",
				"task: signal received: interrupt\n",
				"sleepit: got signal=interrupt count=1\n",
				"sleepit: work canceled\n",
				"sleepit: cleanup started\n",
				"sleepit: cleanup done\n",
				"task: Failed to run task \"default\": exit status 3\n",
			},
			notWant: []string{
				"sleepit: got signal=interrupt count=2\n",
				"task: Failed to run task \"default\": context canceled\n",
			},
		},
		// regression: child receives 2 signal instead of 1 and thus terminates abruptly
		"child simulates terraform: receives 1 sigint and does cleanup": {
			args:     []string{task, "--", SLEEPIT, "handle", "-term-after=2", "-sleep=10s", "-cleanup=50ms"},
			sendSigs: 1,
			want: []string{
				"sleepit: ready\n",
				"sleepit: work started\n",
				"task: signal received: interrupt\n",
				"sleepit: got signal=interrupt count=1\n",
				"sleepit: work canceled\n",
				"sleepit: cleanup started\n",
				"sleepit: cleanup done\n",
				"task: Failed to run task \"default\": exit status 3\n",
			},
			notWant: []string{
				"sleepit: got signal=interrupt count=2\n",
				"sleepit: cleanup canceled\n",
				"task: Failed to run task \"default\": exit status 4\n",
			},
		},
	}

	for name, tc := range testCases {
		t.Run(name, func(t *testing.T) {
			var out bytes.Buffer
			sut := exec.Command(tc.args[0], tc.args[1:]...)
			sut.Stdout = &out
			sut.Stderr = &out
			sut.Dir = "testdata/ignore_signals"
			// Create a new process group by setting the process group ID of the child
			// to the child PID.
			// By default, the child would inherit the process group of the parent, but
			// we want to avoid this, to protect the parent (the test process) from the
			// signal that this test will send. More info in the comments below for
			// syscall.Kill().
			sut.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0}

			if err := sut.Start(); err != nil {
				t.Fatalf("starting the SUT process: %v", err)
			}

			// After the child is started, we want to avoid a race condition where we send
			// it a signal before it had time to setup its own signal handlers. Sleeping
			// is way too flaky, instead we parse the child output until we get a line
			// that we know is printed after the signal handlers are installed...
			ready := false
			timeout := time.Duration(time.Second)
			start := time.Now()
			for time.Since(start) < timeout {
				if strings.Contains(out.String(), "sleepit: ready\n") {
					ready = true
					break
				}
				time.Sleep(10 * time.Millisecond)
			}
			if !ready {
				t.Fatalf("sleepit not ready after %v\n"+
					"additional information:\n"+
					"  output:\n%s",
					timeout, out.String())
			}

			// When we have a running program in a shell and type CTRL-C, the tty driver
			// will send a SIGINT signal to all the processes in the foreground process
			// group (see https://en.wikipedia.org/wiki/Process_group).
			//
			// Here we want to emulate this behavior: send SIGINT to the process group of
			// the test executable. Although Go for some reasons doesn't wrap the
			// killpg(2) system call, what works is using syscall.Kill(-PID, SIGINT),
			// where the negative PID means the corresponding process group. Note that
			// this negative PID works only as long as the caller of the kill(2) system
			// call has a different PID, which is the case for this test.
			for i := 1; i <= tc.sendSigs; i++ {
				if err := syscall.Kill(-sut.Process.Pid, syscall.SIGINT); err != nil {
					t.Fatalf("sending INT signal to the process group: %v", err)
				}
				time.Sleep(1 * time.Millisecond)
			}

			err := sut.Wait()

			// In case of a subprocess failing, Task always returns exit code 1, not the
			// exit code returned by the subprocess. This is understandable, since Task
			// supports parallel execution: if two parallel subprocess fail, each with a
			// different exit code, which one should Task report? This would be a race.
			var wantErr *exec.ExitError
			const wantExitStatus = 1 // Task always returns exit code 1 in case of error
			if errors.As(err, &wantErr) {
				if wantErr.ExitCode() != wantExitStatus {
					t.Errorf(
						"waiting for child process: got exit status %v; want %d\n"+
							"additional information:\n"+
							"  process state: %q",
						wantErr.ExitCode(), wantExitStatus, wantErr.String())
				}
			} else {
				t.Errorf("waiting for child process: got unexpected error type %v (%T); want (%T)",
					err, err, wantErr)
			}

			gotLines := strings.SplitAfter(out.String(), "\n")
			notFound := listDifference(tc.want, gotLines)
			if len(notFound) > 0 {
				t.Errorf("\nwanted but not found:\n%v", notFound)
			}

			found := listIntersection(tc.notWant, gotLines)
			if len(found) > 0 {
				t.Errorf("\nunwanted but found:\n%v", found)
			}

			if len(notFound) > 0 || len(found) > 0 {
				t.Errorf("\noutput:\n%v", gotLines)
			}
		})
	}
}

func getTaskPath() (string, error) {
	if info, err := os.Stat("./bin/task"); err == nil {
		return info.Name(), nil
	}

	if path, err := exec.LookPath("task"); err == nil {
		return path, nil
	}

	return "", errors.New("task: \"task\" binary was not found!")
}

// Return the difference of the two lists: the elements that are present in the first
// list, but not in the second one. The notion of presence is not with `=` but with
// string.Contains(l2, l1).
// FIXME this does not enforce ordering. We might want to support both.
func listDifference(lines1, lines2 []string) []string {
	difference := []string{}
	for _, l1 := range lines1 {
		found := false
		for _, l2 := range lines2 {
			if strings.Contains(l2, l1) {
				found = true
				break
			}
		}
		if !found {
			difference = append(difference, l1)
		}
	}

	return difference
}

// Return the intersection of the two lists: the elements that are present in both lists.
// The notion of presence is not with '=' but with string.Contains(l2, l1)
// FIXME this does not enforce ordering. We might want to support both.
func listIntersection(lines1, lines2 []string) []string {
	intersection := []string{}
	for _, l1 := range lines1 {
		for _, l2 := range lines2 {
			if strings.Contains(l2, l1) {
				intersection = append(intersection, l1)
				break
			}
		}
	}

	return intersection
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package monitor

import (
	"context"

	"github.com/tsavola/gate/server"
	"github.com/tsavola/gate/server/detail"
	"github.com/tsavola/gate/server/event"
)

const (
	DefaultBufSize = 1360
)

type Config struct {
	BufSize int
}

type Item struct {
	Position *detail.Position
	Event    server.Event
	Err      error
}

type req struct {
	sub       chan<- Item
	subscribe chan<- State
}

type MonitorState struct {
	current State
	items   chan Item
	kill    chan struct{}
	reqs    chan req
	subs    map[chan<- Item]struct{}
	done    <-chan struct{}
}

func New(ctx context.Context, config *Config) (m server.Monitor, s *MonitorState) {
	bufsize := config.BufSize
	if bufsize == 0 {
		bufsize = DefaultBufSize
	}

	s = &MonitorState{
		items: make(chan Item, bufsize),
		kill:  make(chan struct{}),
		reqs:  make(chan req),
		subs:  make(map[chan<- Item]struct{}),
		done:  ctx.Done(),
	}

	m.MonitorError = s.monitorError
	m.MonitorEvent = s.monitorEvent

	go s.loop()

	return
}

func (s *MonitorState) Subscribe(ctx context.Context, sub chan<- Item) (snapshot State, err error) {
	resp := make(chan State)

	select {
	case s.reqs <- req{sub, resp}:
		select {
		case snapshot = <-resp:
			return

		case <-ctx.Done():
			err = ctx.Err()
			return
		}

	case <-s.done: // monitor
		err = context.Canceled
		return

	case <-ctx.Done(): // caller
		err = ctx.Err()
		return
	}
}

func (s *MonitorState) Unsubscribe(ctx context.Context, sub chan Item) error {
	for {
		select {
		case _, open := <-sub:
			if open {
				// we unclogged loop()
			} else {
				return nil // loop() has killed us prematurely
			}

		case s.reqs <- req{sub, nil}:
			return nil

		case <-s.done: // monitor
			return context.Canceled

		case <-ctx.Done(): // caller
			return ctx.Err()
		}
	}
}

func (s *MonitorState) monitorError(p *detail.Position, err error) {
	s.monitorItem(Item{Position: p, Err: err})
}

func (s *MonitorState) monitorEvent(ev server.Event, err error) {
	s.monitorItem(Item{Event: ev, Err: err})
}

func (s *MonitorState) monitorItem(i Item) {
	select {
	case s.items <- i:
		// ok

	default:
		select {
		case s.kill <- struct{}{}:
			select {
			case s.items <- i:
				// ok

			case <-s.done:
			}

		case <-s.done:
		}
	}
}

func (s *MonitorState) loop() {
	for {
		select {
		case i := <-s.items:
			if i.Event != nil {
				s.current.update(i.Event)
			}

			var killed []chan<- Item

			for sub := range s.subs {
				select {
				case sub <- i:
					// ok

				case <-s.kill:
					killed = append(killed, sub)
				}
			}

			for _, sub := range killed {
				delete(s.subs, sub)
				close(sub)
			}

		case r := <-s.reqs:
			if r.subscribe != nil {
				s.subs[r.sub] = struct{}{}
				r.subscribe <- s.current
			} else {
				delete(s.subs, r.sub)
				close(r.sub)
			}

		case <-s.done:
			return
		}
	}
}

func (s *State) update(x server.Event) {
	switch x.(type) {
	case *event.ProgramLoad:
		s.ProgramsLoaded++

	case *event.ProgramCreate:
		s.ProgramLinks++

	case *event.InstanceCreate:
		s.Instances++

	case *event.InstanceDelete:
		s.Instances--
	}
}
<commit_msg>server: monitor: override protobuf package doc<commit_after>// Copyright (c) 2018 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package monitor implements server.Monitor.
package monitor

import (
	"context"

	"github.com/tsavola/gate/server"
	"github.com/tsavola/gate/server/detail"
	"github.com/tsavola/gate/server/event"
)

const (
	DefaultBufSize = 1360
)

type Config struct {
	BufSize int
}

type Item struct {
	Position *detail.Position
	Event    server.Event
	Err      error
}

type req struct {
	sub       chan<- Item
	subscribe chan<- State
}

type MonitorState struct {
	current State
	items   chan Item
	kill    chan struct{}
	reqs    chan req
	subs    map[chan<- Item]struct{}
	done    <-chan struct{}
}

func New(ctx context.Context, config *Config) (m server.Monitor, s *MonitorState) {
	bufsize := config.BufSize
	if bufsize == 0 {
		bufsize = DefaultBufSize
	}

	s = &MonitorState{
		items: make(chan Item, bufsize),
		kill:  make(chan struct{}),
		reqs:  make(chan req),
		subs:  make(map[chan<- Item]struct{}),
		done:  ctx.Done(),
	}

	m.MonitorError = s.monitorError
	m.MonitorEvent = s.monitorEvent

	go s.loop()

	return
}

func (s *MonitorState) Subscribe(ctx context.Context, sub chan<- Item) (snapshot State, err error) {
	resp := make(chan State)

	select {
	case s.reqs <- req{sub, resp}:
		select {
		case snapshot = <-resp:
			return

		case <-ctx.Done():
			err = ctx.Err()
			return
		}

	case <-s.done: // monitor
		err = context.Canceled
		return

	case <-ctx.Done(): // caller
		err = ctx.Err()
		return
	}
}

func (s *MonitorState) Unsubscribe(ctx context.Context, sub chan Item) error {
	for {
		select {
		case _, open := <-sub:
			if open {
				// we unclogged loop()
			} else {
				return nil // loop() has killed us prematurely
			}

		case s.reqs <- req{sub, nil}:
			return nil

		case <-s.done: // monitor
			return context.Canceled

		case <-ctx.Done(): // caller
			return ctx.Err()
		}
	}
}

func (s *MonitorState) monitorError(p *detail.Position, err error) {
	s.monitorItem(Item{Position: p, Err: err})
}

func (s *MonitorState) monitorEvent(ev server.Event, err error) {
	s.monitorItem(Item{Event: ev, Err: err})
}

func (s *MonitorState) monitorItem(i Item) {
	select {
	case s.items <- i:
		// ok

	default:
		select {
		case s.kill <- struct{}{}:
			select {
			case s.items <- i:
				// ok

			case <-s.done:
			}

		case <-s.done:
		}
	}
}

func (s *MonitorState) loop() {
	for {
		select {
		case i := <-s.items:
			if i.Event != nil {
				s.current.update(i.Event)
			}

			var killed []chan<- Item

			for sub := range s.subs {
				select {
				case sub <- i:
					// ok

				case <-s.kill:
					killed = append(killed, sub)
				}
			}

			for _, sub := range killed {
				delete(s.subs, sub)
				close(sub)
			}

		case r := <-s.reqs:
			if r.subscribe != nil {
				s.subs[r.sub] = struct{}{}
				r.subscribe <- s.current
			} else {
				delete(s.subs, r.sub)
				close(r.sub)
			}

		case <-s.done:
			return
		}
	}
}

func (s *State) update(x server.Event) {
	switch x.(type) {
	case *event.ProgramLoad:
		s.ProgramsLoaded++

	case *event.ProgramCreate:
		s.ProgramLinks++

	case *event.InstanceCreate:
		s.Instances++

	case *event.InstanceDelete:
		s.Instances--
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// This file contains the mechanism to "linkify" html source
// text containing EBNF sections (as found in go_spec.html).
// The result is the input source text with the EBNF sections
// modified such that identifiers are linked to the respective
// definitions.

package main

import (
	"bytes"
	"fmt"
	"go/scanner"
	"go/token"
	"io"
)

type ebnfParser struct {
	out     io.Writer   // parser output
	src     []byte      // parser source
	file    *token.File // for position information
	scanner scanner.Scanner
	prev    int         // offset of previous token
	pos     token.Pos   // token position
	tok     token.Token // one token look-ahead
	lit     []byte      // token literal
}

func (p *ebnfParser) flush() {
	offs := p.file.Offset(p.pos)
	p.out.Write(p.src[p.prev:offs])
	p.prev = offs
}

func (p *ebnfParser) next() {
	p.flush()
	p.pos, p.tok, p.lit = p.scanner.Scan()
	if p.tok.IsKeyword() {
		// TODO Should keyword mapping always happen outside scanner?
		//      Or should there be a flag to scanner to enable keyword mapping?
		p.tok = token.IDENT
	}
}

func (p *ebnfParser) Error(pos token.Position, msg string) {
	fmt.Fprintf(p.out, `<span class="alert">error: %s</span>`, msg)
}

func (p *ebnfParser) errorExpected(pos token.Pos, msg string) {
	msg = "expected " + msg
	if pos == p.pos {
		// the error happened at the current position;
		// make the error message more specific
		msg += ", found '" + p.tok.String() + "'"
		if p.tok.IsLiteral() {
			msg += " " + string(p.lit)
		}
	}
	p.Error(p.file.Position(pos), msg)
}

func (p *ebnfParser) expect(tok token.Token) token.Pos {
	pos := p.pos
	if p.tok != tok {
		p.errorExpected(pos, "'"+tok.String()+"'")
	}
	p.next() // make progress in any case
	return pos
}

func (p *ebnfParser) parseIdentifier(def bool) {
	name := string(p.lit)
	p.expect(token.IDENT)
	if def {
		fmt.Fprintf(p.out, `<a id="%s">%s</a>`, name, name)
	} else {
		fmt.Fprintf(p.out, `<a href="#%s" class="noline">%s</a>`, name, name)
	}
	p.prev += len(name) // skip identifier when calling flush
}

func (p *ebnfParser) parseTerm() bool {
	switch p.tok {
	case token.IDENT:
		p.parseIdentifier(false)

	case token.STRING:
		p.next()
		if p.tok == token.ELLIPSIS {
			p.next()
			p.expect(token.STRING)
		}

	case token.LPAREN:
		p.next()
		p.parseExpression()
		p.expect(token.RPAREN)

	case token.LBRACK:
		p.next()
		p.parseExpression()
		p.expect(token.RBRACK)

	case token.LBRACE:
		p.next()
		p.parseExpression()
		p.expect(token.RBRACE)

	default:
		return false
	}

	return true
}

func (p *ebnfParser) parseSequence() {
	for p.parseTerm() {
	}
}

func (p *ebnfParser) parseExpression() {
	for {
		p.parseSequence()
		if p.tok != token.OR {
			break
		}
		p.next()
	}
}

func (p *ebnfParser) parseProduction() {
	p.parseIdentifier(true)
	p.expect(token.ASSIGN)
	p.parseExpression()
	p.expect(token.PERIOD)
}

func (p *ebnfParser) parse(fset *token.FileSet, out io.Writer, src []byte) {
	// initialize ebnfParser
	p.out = out
	p.src = src
	p.file = p.scanner.Init(fset, "", src, p, 0)
	p.next() // initializes pos, tok, lit

	// process source
	for p.tok != token.EOF {
		p.parseProduction()
	}
	p.flush()
}

// Markers around EBNF sections
var (
	openTag  = []byte(`<pre class="ebnf">`)
	closeTag = []byte(`</pre>`)
)

func linkify(out io.Writer, src []byte) {
	fset := token.NewFileSet()
	for len(src) > 0 {
		n := len(src)

		// i: beginning of EBNF text (or end of source)
		i := bytes.Index(src, openTag)
		if i < 0 {
			i = n - len(openTag)
		}
		i += len(openTag)

		// j: end of EBNF text (or end of source)
		j := bytes.Index(src[i:n], closeTag) // close marker
		if j < 0 {
			j = n - i
		}
		j += i

		// write text before EBNF
		out.Write(src[0:i])
		// parse and write EBNF
		var p ebnfParser
		p.parse(fset, out, src[i:j])

		// advance
		src = src[j:n]
	}
}
<commit_msg>godoc: don't call flush if position is not valid (fixes new godoc crash when looking at spec)<commit_after>// Copyright 2009 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.

// This file contains the mechanism to "linkify" html source
// text containing EBNF sections (as found in go_spec.html).
// The result is the input source text with the EBNF sections
// modified such that identifiers are linked to the respective
// definitions.

package main

import (
	"bytes"
	"fmt"
	"go/scanner"
	"go/token"
	"io"
)

type ebnfParser struct {
	out     io.Writer   // parser output
	src     []byte      // parser source
	file    *token.File // for position information
	scanner scanner.Scanner
	prev    int         // offset of previous token
	pos     token.Pos   // token position
	tok     token.Token // one token look-ahead
	lit     []byte      // token literal
}

func (p *ebnfParser) flush() {
	offs := p.file.Offset(p.pos)
	p.out.Write(p.src[p.prev:offs])
	p.prev = offs
}

func (p *ebnfParser) next() {
	if p.pos.IsValid() {
		p.flush()
	}
	p.pos, p.tok, p.lit = p.scanner.Scan()
	if p.tok.IsKeyword() {
		// TODO Should keyword mapping always happen outside scanner?
		//      Or should there be a flag to scanner to enable keyword mapping?
		p.tok = token.IDENT
	}
}

func (p *ebnfParser) Error(pos token.Position, msg string) {
	fmt.Fprintf(p.out, `<span class="alert">error: %s</span>`, msg)
}

func (p *ebnfParser) errorExpected(pos token.Pos, msg string) {
	msg = "expected " + msg
	if pos == p.pos {
		// the error happened at the current position;
		// make the error message more specific
		msg += ", found '" + p.tok.String() + "'"
		if p.tok.IsLiteral() {
			msg += " " + string(p.lit)
		}
	}
	p.Error(p.file.Position(pos), msg)
}

func (p *ebnfParser) expect(tok token.Token) token.Pos {
	pos := p.pos
	if p.tok != tok {
		p.errorExpected(pos, "'"+tok.String()+"'")
	}
	p.next() // make progress in any case
	return pos
}

func (p *ebnfParser) parseIdentifier(def bool) {
	name := string(p.lit)
	p.expect(token.IDENT)
	if def {
		fmt.Fprintf(p.out, `<a id="%s">%s</a>`, name, name)
	} else {
		fmt.Fprintf(p.out, `<a href="#%s" class="noline">%s</a>`, name, name)
	}
	p.prev += len(name) // skip identifier when calling flush
}

func (p *ebnfParser) parseTerm() bool {
	switch p.tok {
	case token.IDENT:
		p.parseIdentifier(false)

	case token.STRING:
		p.next()
		if p.tok == token.ELLIPSIS {
			p.next()
			p.expect(token.STRING)
		}

	case token.LPAREN:
		p.next()
		p.parseExpression()
		p.expect(token.RPAREN)

	case token.LBRACK:
		p.next()
		p.parseExpression()
		p.expect(token.RBRACK)

	case token.LBRACE:
		p.next()
		p.parseExpression()
		p.expect(token.RBRACE)

	default:
		return false
	}

	return true
}

func (p *ebnfParser) parseSequence() {
	for p.parseTerm() {
	}
}

func (p *ebnfParser) parseExpression() {
	for {
		p.parseSequence()
		if p.tok != token.OR {
			break
		}
		p.next()
	}
}

func (p *ebnfParser) parseProduction() {
	p.parseIdentifier(true)
	p.expect(token.ASSIGN)
	p.parseExpression()
	p.expect(token.PERIOD)
}

func (p *ebnfParser) parse(fset *token.FileSet, out io.Writer, src []byte) {
	// initialize ebnfParser
	p.out = out
	p.src = src
	p.file = p.scanner.Init(fset, "", src, p, 0)
	p.next() // initializes pos, tok, lit

	// process source
	for p.tok != token.EOF {
		p.parseProduction()
	}
	p.flush()
}

// Markers around EBNF sections
var (
	openTag  = []byte(`<pre class="ebnf">`)
	closeTag = []byte(`</pre>`)
)

func linkify(out io.Writer, src []byte) {
	fset := token.NewFileSet()
	for len(src) > 0 {
		n := len(src)

		// i: beginning of EBNF text (or end of source)
		i := bytes.Index(src, openTag)
		if i < 0 {
			i = n - len(openTag)
		}
		i += len(openTag)

		// j: end of EBNF text (or end of source)
		j := bytes.Index(src[i:n], closeTag) // close marker
		if j < 0 {
			j = n - i
		}
		j += i

		// write text before EBNF
		out.Write(src[0:i])
		// parse and write EBNF
		var p ebnfParser
		p.parse(fset, out, src[i:j])

		// advance
		src = src[j:n]
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Jetstack Ltd.
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 certificate

import (
	"flag"
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	cmutil "github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	"github.com/jetstack/cert-manager/test/util"
)

const invalidACMEURL = "http://not-a-real-acme-url.com"
const testingACMEEmail = "test@example.com"
const testingACMEPrivateKey = "test-acme-private-key"
const foreverTestTimeout = time.Second * 60

var acmeIngressClass string

func init() {
	flag.StringVar(&acmeIngressClass, "acme-nginx-ingress-class", "nginx", ""+
		"The ingress class for the nginx ingress controller")
}

var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
	f := framework.NewDefaultFramework("create-acme-certificate-http01")

	issuerName := "test-acme-issuer"
	certificateName := "test-acme-certificate"
	certificateSecretName := "test-acme-certificate"

	BeforeEach(func() {
		By("Verifying there is no existing ACME private key")
		_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(testingACMEPrivateKey, metav1.GetOptions{})
		Expect(err).To(MatchError(apierrors.NewNotFound(corev1.Resource("secrets"), testingACMEPrivateKey)))
		By("Verifying there is no existing TLS certificate secret")
		_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(certificateSecretName, metav1.GetOptions{})
		Expect(err).To(MatchError(apierrors.NewNotFound(corev1.Resource("secrets"), certificateSecretName)))
		By("Creating an Issuer")
		_, err = f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerACMEIssuer(issuerName, framework.TestContext.ACMEURL, testingACMEEmail, testingACMEPrivateKey))
		Expect(err).NotTo(HaveOccurred())
		By("Waiting for Issuer to become Ready")
		err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
			issuerName,
			v1alpha1.IssuerCondition{
				Type:   v1alpha1.IssuerConditionReady,
				Status: v1alpha1.ConditionTrue,
			})
		Expect(err).NotTo(HaveOccurred())
		By("Verifying the ACME account URI is set")
		err = util.WaitForIssuerStatusFunc(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
			issuerName,
			func(i *v1alpha1.Issuer) (bool, error) {
				if i.GetStatus().ACMEStatus().URI == "" {
					return false, nil
				}
				return true, nil
			})
		Expect(err).NotTo(HaveOccurred())
		By("Verifying ACME account private key exists")
		secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(testingACMEPrivateKey, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		if len(secret.Data) != 1 {
			Fail("Expected 1 key in ACME account private key secret, but there was %d", len(secret.Data))
		}
	})

	AfterEach(func() {
		By("Cleaning up")
		f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Delete(issuerName, nil)
		f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(testingACMEPrivateKey, nil)
	})

	It("should obtain a signed certificate with a single CN from the ACME server", func() {
		By("Creating a Certificate")
		cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, util.ACMECertificateDomain))
		Expect(err).NotTo(HaveOccurred())
		f.WaitCertificateIssuedValid(cert)
	})

	It("should obtain a signed certificate for a long domain using http01 validation", func() {
		// the maximum length of a single segment of the domain being requested
		const maxLengthOfDomainSegment = 63
		By("Creating a Certificate")
		cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(maxLengthOfDomainSegment), util.ACMECertificateDomain)))
		Expect(err).NotTo(HaveOccurred())
		f.WaitCertificateIssuedValid(cert)
	})

	It("should obtain a signed certificate with a CN and single subdomain as dns name from the ACME server", func() {
		By("Creating a Certificate")
		cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, util.ACMECertificateDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), util.ACMECertificateDomain)))
		Expect(err).NotTo(HaveOccurred())
		f.WaitCertificateIssuedValid(cert)
	})

	It("should fail to obtain a certificate for an invalid ACME dns name", func() {
		By("Creating a Certificate")
		_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, "google.com"))
		Expect(err).NotTo(HaveOccurred())
		By("Waiting for the Certificate to not have a ready condition")
		err = util.WaitForCertificateCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name),
			certificateName,
			v1alpha1.CertificateCondition{
				Type:   v1alpha1.CertificateConditionReady,
				Status: v1alpha1.ConditionTrue,
			}, foreverTestTimeout)
		Expect(err).To(HaveOccurred())
		By("Verifying TLS certificate secret does not exist")
		_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(certificateSecretName, metav1.GetOptions{})
		Expect(err).To(MatchError(apierrors.NewNotFound(corev1.Resource("secrets"), certificateSecretName)))
	})
})
<commit_msg>Update e2e test ACME email<commit_after>/*
Copyright 2017 Jetstack Ltd.
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 certificate

import (
	"flag"
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	cmutil "github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	"github.com/jetstack/cert-manager/test/util"
)

const invalidACMEURL = "http://not-a-real-acme-url.com"
const testingACMEEmail = "e2e@cert-manager.io"
const testingACMEPrivateKey = "test-acme-private-key"
const foreverTestTimeout = time.Second * 60

var acmeIngressClass string

func init() {
	flag.StringVar(&acmeIngressClass, "acme-nginx-ingress-class", "nginx", ""+
		"The ingress class for the nginx ingress controller")
}

var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
	f := framework.NewDefaultFramework("create-acme-certificate-http01")

	issuerName := "test-acme-issuer"
	certificateName := "test-acme-certificate"
	certificateSecretName := "test-acme-certificate"

	BeforeEach(func() {
		By("Verifying there is no existing ACME private key")
		_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(testingACMEPrivateKey, metav1.GetOptions{})
		Expect(err).To(MatchError(apierrors.NewNotFound(corev1.Resource("secrets"), testingACMEPrivateKey)))
		By("Verifying there is no existing TLS certificate secret")
		_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(certificateSecretName, metav1.GetOptions{})
		Expect(err).To(MatchError(apierrors.NewNotFound(corev1.Resource("secrets"), certificateSecretName)))
		By("Creating an Issuer")
		_, err = f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerACMEIssuer(issuerName, framework.TestContext.ACMEURL, testingACMEEmail, testingACMEPrivateKey))
		Expect(err).NotTo(HaveOccurred())
		By("Waiting for Issuer to become Ready")
		err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
			issuerName,
			v1alpha1.IssuerCondition{
				Type:   v1alpha1.IssuerConditionReady,
				Status: v1alpha1.ConditionTrue,
			})
		Expect(err).NotTo(HaveOccurred())
		By("Verifying the ACME account URI is set")
		err = util.WaitForIssuerStatusFunc(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
			issuerName,
			func(i *v1alpha1.Issuer) (bool, error) {
				if i.GetStatus().ACMEStatus().URI == "" {
					return false, nil
				}
				return true, nil
			})
		Expect(err).NotTo(HaveOccurred())
		By("Verifying ACME account private key exists")
		secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(testingACMEPrivateKey, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		if len(secret.Data) != 1 {
			Fail("Expected 1 key in ACME account private key secret, but there was %d", len(secret.Data))
		}
	})

	AfterEach(func() {
		By("Cleaning up")
		f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Delete(issuerName, nil)
		f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(testingACMEPrivateKey, nil)
	})

	It("should obtain a signed certificate with a single CN from the ACME server", func() {
		By("Creating a Certificate")
		cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, util.ACMECertificateDomain))
		Expect(err).NotTo(HaveOccurred())
		f.WaitCertificateIssuedValid(cert)
	})

	It("should obtain a signed certificate for a long domain using http01 validation", func() {
		// the maximum length of a single segment of the domain being requested
		const maxLengthOfDomainSegment = 63
		By("Creating a Certificate")
		cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(maxLengthOfDomainSegment), util.ACMECertificateDomain)))
		Expect(err).NotTo(HaveOccurred())
		f.WaitCertificateIssuedValid(cert)
	})

	It("should obtain a signed certificate with a CN and single subdomain as dns name from the ACME server", func() {
		By("Creating a Certificate")
		cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, util.ACMECertificateDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), util.ACMECertificateDomain)))
		Expect(err).NotTo(HaveOccurred())
		f.WaitCertificateIssuedValid(cert)
	})

	It("should fail to obtain a certificate for an invalid ACME dns name", func() {
		By("Creating a Certificate")
		_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, "google.com"))
		Expect(err).NotTo(HaveOccurred())
		By("Waiting for the Certificate to not have a ready condition")
		err = util.WaitForCertificateCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name),
			certificateName,
			v1alpha1.CertificateCondition{
				Type:   v1alpha1.CertificateConditionReady,
				Status: v1alpha1.ConditionTrue,
			}, foreverTestTimeout)
		Expect(err).To(HaveOccurred())
		By("Verifying TLS certificate secret does not exist")
		_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(certificateSecretName, metav1.GetOptions{})
		Expect(err).To(MatchError(apierrors.NewNotFound(corev1.Resource("secrets"), certificateSecretName)))
	})
})
<|endoftext|>


<commit_before>package node

import (
	"testing"

	"github.com/NebulousLabs/Sia/siatest"
)

// TestNew is a basic smoke test for New that uses all of the templates to
// verify a working New function.
func TestNew(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	// Test AllModulesTemplate.
	dir, err := siatest.TestDir("node", t.Name()+"-AllModulesTemplate")
	if err != nil {
		t.Fatal(err)
	}
	n, err := New(AllModules(dir))
	if err != nil {
		t.Fatal(err)
	}
	if n.Gateway == nil {
		t.Error("gateway not set correctly")
	}
	if n.ConsensusSet == nil {
		t.Error("consensus set not set correctly")
	}
	if n.Explorer == nil {
		// TODO: Add the explorer to the node package.
		t.Log("Need to add the explorer to the SiaNode framework.")
	}
	if n.TransactionPool == nil {
		t.Error("transaction pool not set correctly")
	}
	if n.Wallet == nil {
		t.Error("wallet not set correctly")
	}
	if n.Host == nil {
		t.Error("host not set correctly")
	}
	if n.Renter == nil {
		t.Error("renter not set correctly")
	}
	if n.Miner == nil {
		t.Error("miner not set correctly")
	}
	err = n.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Test WalletTemplate.
	dir, err = siatest.TestDir("node", t.Name()+"-WalletTemplate")
	if err != nil {
		t.Fatal(err)
	}
	n, err = New(Wallet(dir))
	if err != nil {
		t.Fatal(err)
	}
	if n.Gateway == nil {
		t.Error("gateway not set correctly")
	}
	if n.ConsensusSet == nil {
		t.Error("consensus set not set correctly")
	}
	if n.Explorer != nil {
		t.Error("explorer should not be created when using the wallet template")
	}
	if n.TransactionPool == nil {
		t.Error("transaction pool not set correctly")
	}
	if n.Wallet == nil {
		t.Error("wallet not set correctly")
	}
	if n.Host != nil {
		t.Error("host should not be created when using the wallet template")
	}
	if n.Renter != nil {
		t.Error("renter should not be created when using the wallet template")
	}
	if n.Miner != nil {
		t.Error("miner should not be created when using the wallet template")
	}
	err = n.Close()
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>fix circular dependency in template_test.go<commit_after>package node

import (
	"testing"

	"github.com/NebulousLabs/Sia/build"
)

// TestNew is a basic smoke test for New that uses all of the templates to
// verify a working New function.
func TestNew(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	// Test AllModulesTemplate.
	dir := build.TempDir("node", t.Name()+"-AllModulesTemplate")
	n, err := New(AllModules(dir))
	if err != nil {
		t.Fatal(err)
	}
	if n.Gateway == nil {
		t.Error("gateway not set correctly")
	}
	if n.ConsensusSet == nil {
		t.Error("consensus set not set correctly")
	}
	if n.Explorer == nil {
		// TODO: Add the explorer to the node package.
		t.Log("Need to add the explorer to the SiaNode framework.")
	}
	if n.TransactionPool == nil {
		t.Error("transaction pool not set correctly")
	}
	if n.Wallet == nil {
		t.Error("wallet not set correctly")
	}
	if n.Host == nil {
		t.Error("host not set correctly")
	}
	if n.Renter == nil {
		t.Error("renter not set correctly")
	}
	if n.Miner == nil {
		t.Error("miner not set correctly")
	}
	err = n.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Test WalletTemplate.
	dir = build.TempDir("node", t.Name()+"-WalletTemplate")
	n, err = New(Wallet(dir))
	if err != nil {
		t.Fatal(err)
	}
	if n.Gateway == nil {
		t.Error("gateway not set correctly")
	}
	if n.ConsensusSet == nil {
		t.Error("consensus set not set correctly")
	}
	if n.Explorer != nil {
		t.Error("explorer should not be created when using the wallet template")
	}
	if n.TransactionPool == nil {
		t.Error("transaction pool not set correctly")
	}
	if n.Wallet == nil {
		t.Error("wallet not set correctly")
	}
	if n.Host != nil {
		t.Error("host should not be created when using the wallet template")
	}
	if n.Renter != nil {
		t.Error("renter should not be created when using the wallet template")
	}
	if n.Miner != nil {
		t.Error("miner should not be created when using the wallet template")
	}
	err = n.Close()
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/dgraph-io/badger/v2"
	"github.com/gojp/goreportcard/download"
)

const (
	// RepoPrefix is the badger prefix for repos
	RepoPrefix string = "repos-"
)

// CheckHandler handles the request for checking a repo
func CheckHandler(w http.ResponseWriter, r *http.Request, db *badger.DB) {
	w.Header().Set("Content-Type", "application/json")

	repo, err := download.Clean(r.FormValue("repo"))
	if err != nil {
		log.Println("ERROR: from download.Clean:", err)
		http.Error(w, "Could not download the repository: "+err.Error(), http.StatusBadRequest)
		return
	}

	log.Printf("Checking repo %q...", repo)

	forceRefresh := r.Method != "GET" // if this is a GET request, try to fetch from cached version in badger first
	_, err = newChecksResp(db, repo, forceRefresh)
	if err != nil {
		log.Println("ERROR: from newChecksResp:", err)
		http.Error(w, "Could not analyze the repository: "+err.Error(), http.StatusBadRequest)
		return
	}

	b, err := json.Marshal(map[string]string{"redirect": "/report/" + repo})
	if err != nil {
		log.Println("JSON marshal error:", err)
	}
	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func updateHighScores(txn *badger.Txn, resp checksResp, repo string) error {
	// check if we need to update the high score list
	if resp.Files < 100 {
		// only repos with >= 100 files are considered for the high score list
		return nil
	}

	var scoreBytes []byte
	// start updating high score list
	item, err := txn.Get([]byte("scores"))
	if err != nil && err != badger.ErrKeyNotFound {
		return err
	}

	if item == nil {
		scoreBytes, _ = json.Marshal([]ScoreHeap{})
	}

	if item != nil {
		item.Value(func(val []byte) error {
			scoreBytes = val

			return nil
		})
	}

	scores := &ScoreHeap{}
	json.Unmarshal(scoreBytes, scores)

	heap.Init(scores)
	if len(*scores) > 0 && (*scores)[0].Score > resp.Average*100.0 && len(*scores) == 50 {
		// lowest score on list is higher than this repo's score, so no need to add, unless
		// we do not have 50 high scores yet
		return nil
	}

	// if this repo is already in the list, remove the original entry:
	for i := range *scores {
		if strings.EqualFold((*scores)[i].Repo, repo) {
			heap.Remove(scores, i)
			break
		}
	}

	// now we can safely push it onto the heap
	heap.Push(scores, scoreItem{
		Repo:  repo,
		Score: resp.Average * 100.0,
		Files: resp.Files,
	})

	if len(*scores) > 50 {
		// trim heap if it's grown to over 50
		*scores = (*scores)[1:51]
	}

	scoreBytes, err = json.Marshal(&scores)
	if err != nil {
		return err
	}

	return txn.Set([]byte("scores"), scoreBytes)
}

func updateReposCount(txn *badger.Txn, repo string) error {
	log.Printf("New repo %q, adding to repo count...", repo)
	totalInt := 0
	item, err := txn.Get([]byte("total_repos"))
	if err != nil && err != badger.ErrKeyNotFound {
		return err
	}

	if item != nil {
		err := item.Value(func(val []byte) error {
			err = json.Unmarshal(val, &totalInt)
			if err != nil {
				return fmt.Errorf("could not unmarshal total repos count: %v", err)
			}

			return nil

		})

		if err != nil {
			return err
		}
	}

	totalInt++ // increase repo count
	total, err := json.Marshal(totalInt)
	if err != nil {
		return fmt.Errorf("could not marshal total repos count: %v", err)
	}

	err = txn.Set([]byte("total_repos"), total)
	if err != nil {
		return err
	}
	log.Println("Repo count is now", totalInt)

	return nil
}

type recentItem struct {
	Repo string
}

func updateRecentlyViewed(txn *badger.Txn, repo string) error {
	var recent []recentItem
	item, err := txn.Get([]byte("recent"))
	if err != nil {
		return err
	}

	if item != nil {
		item.Value(func(val []byte) error {
			return json.Unmarshal(val, &recent)
		})
	}

	// add it to the slice, if it is not in there already
	for i := range recent {
		if recent[i].Repo == repo {
			return nil
		}
	}

	recent = append(recent, recentItem{Repo: repo})
	if len(recent) > 5 {
		// trim recent if it's grown to over 5
		recent = (recent)[1:6]
	}
	b, err := json.Marshal(&recent)
	if err != nil {
		return err
	}

	return txn.Set([]byte("recent"), b)
}

func updateMetadata(txn *badger.Txn, resp checksResp, repo string, isNewRepo bool) error {
	// update total repos count
	if isNewRepo {
		err := updateReposCount(txn, repo)
		if err != nil {
			return err
		}
	}

	return updateHighScores(txn, resp, repo)
}
<commit_msg>fix error when badger could not find recent repos key<commit_after>package handlers

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/dgraph-io/badger/v2"
	"github.com/gojp/goreportcard/download"
)

const (
	// RepoPrefix is the badger prefix for repos
	RepoPrefix string = "repos-"
)

// CheckHandler handles the request for checking a repo
func CheckHandler(w http.ResponseWriter, r *http.Request, db *badger.DB) {
	w.Header().Set("Content-Type", "application/json")

	repo, err := download.Clean(r.FormValue("repo"))
	if err != nil {
		log.Println("ERROR: from download.Clean:", err)
		http.Error(w, "Could not download the repository: "+err.Error(), http.StatusBadRequest)
		return
	}

	log.Printf("Checking repo %q...", repo)

	forceRefresh := r.Method != "GET" // if this is a GET request, try to fetch from cached version in badger first
	_, err = newChecksResp(db, repo, forceRefresh)
	if err != nil {
		log.Println("ERROR: from newChecksResp:", err)
		http.Error(w, "Could not analyze the repository: "+err.Error(), http.StatusBadRequest)
		return
	}

	b, err := json.Marshal(map[string]string{"redirect": "/report/" + repo})
	if err != nil {
		log.Println("JSON marshal error:", err)
	}
	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func updateHighScores(txn *badger.Txn, resp checksResp, repo string) error {
	// check if we need to update the high score list
	if resp.Files < 100 {
		// only repos with >= 100 files are considered for the high score list
		return nil
	}

	var scoreBytes []byte
	// start updating high score list
	item, err := txn.Get([]byte("scores"))
	if err != nil && err != badger.ErrKeyNotFound {
		return err
	}

	if item == nil {
		scoreBytes, _ = json.Marshal([]ScoreHeap{})
	}

	if item != nil {
		item.Value(func(val []byte) error {
			scoreBytes = val

			return nil
		})
	}

	scores := &ScoreHeap{}
	json.Unmarshal(scoreBytes, scores)

	heap.Init(scores)
	if len(*scores) > 0 && (*scores)[0].Score > resp.Average*100.0 && len(*scores) == 50 {
		// lowest score on list is higher than this repo's score, so no need to add, unless
		// we do not have 50 high scores yet
		return nil
	}

	// if this repo is already in the list, remove the original entry:
	for i := range *scores {
		if strings.EqualFold((*scores)[i].Repo, repo) {
			heap.Remove(scores, i)
			break
		}
	}

	// now we can safely push it onto the heap
	heap.Push(scores, scoreItem{
		Repo:  repo,
		Score: resp.Average * 100.0,
		Files: resp.Files,
	})

	if len(*scores) > 50 {
		// trim heap if it's grown to over 50
		*scores = (*scores)[1:51]
	}

	scoreBytes, err = json.Marshal(&scores)
	if err != nil {
		return err
	}

	return txn.Set([]byte("scores"), scoreBytes)
}

func updateReposCount(txn *badger.Txn, repo string) error {
	log.Printf("New repo %q, adding to repo count...", repo)
	totalInt := 0
	item, err := txn.Get([]byte("total_repos"))
	if err != nil && err != badger.ErrKeyNotFound {
		return err
	}

	if item != nil {
		err := item.Value(func(val []byte) error {
			err = json.Unmarshal(val, &totalInt)
			if err != nil {
				return fmt.Errorf("could not unmarshal total repos count: %v", err)
			}

			return nil

		})

		if err != nil {
			return err
		}
	}

	totalInt++ // increase repo count
	total, err := json.Marshal(totalInt)
	if err != nil {
		return fmt.Errorf("could not marshal total repos count: %v", err)
	}

	err = txn.Set([]byte("total_repos"), total)
	if err != nil {
		return err
	}
	log.Println("Repo count is now", totalInt)

	return nil
}

type recentItem struct {
	Repo string
}

func updateRecentlyViewed(txn *badger.Txn, repo string) error {
	var recent []recentItem
	item, err := txn.Get([]byte("recent"))
	if err != nil && err != badger.ErrKeyNotFound {
		return err
	}

	if item != nil {
		item.Value(func(val []byte) error {
			return json.Unmarshal(val, &recent)
		})
	}

	// add it to the slice, if it is not in there already
	for i := range recent {
		if recent[i].Repo == repo {
			return nil
		}
	}

	recent = append(recent, recentItem{Repo: repo})
	if len(recent) > 5 {
		// trim recent if it's grown to over 5
		recent = (recent)[1:6]
	}
	b, err := json.Marshal(&recent)
	if err != nil {
		return err
	}

	return txn.Set([]byte("recent"), b)
}

func updateMetadata(txn *badger.Txn, resp checksResp, repo string, isNewRepo bool) error {
	// update total repos count
	if isNewRepo {
		err := updateReposCount(txn, repo)
		if err != nil {
			return err
		}
	}

	return updateHighScores(txn, resp, repo)
}
<|endoftext|>