<commit_before>package fcm

import (
	"errors"
	"github.com/Bogh/gcm"
	log "github.com/Sirupsen/logrus"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server/router"
	"github.com/smancke/guble/server/store"
	"strconv"
	"strings"
	"time"
)

const (
	// default subscription channel buffer size
	subBufferSize = 50

	// applicationIDKey is the key name set on the route params to identify the application
	applicationIDKey = "device_id"

	// userIDKey is the key name set on the route params to identify the user
	userIDKey = "user_id"
)

var (
	errIgnoreMessage        = errors.New("Message ignored")
	errSubscriptionExists   = errors.New("Subscription exists")
	errSubscriptionReplaced = errors.New("Subscription replaced")
)

// subscription represents a FCM subscription
type subscription struct {
	connector *Connector
	route     *router.Route
	lastID    uint64 // Last sent message id

	logger *log.Entry
}

// initSubscription creates a subscription and adds it in router/kvstore then starts listening for messages
func initSubscription(connector *Connector, topic, userID, fcmID string, lastID uint64, store bool) (*subscription, error) {
	route := router.NewRoute(router.RouteConfig{
		RouteParams: router.RouteParams{userIDKey: userID, applicationIDKey: fcmID},
		Path:        protocol.Path(topic),
		ChannelSize: subBufferSize,
		Matcher:     subscriptionMatcher,
	})

	s := newSubscription(connector, route, lastID)
	if s.exists() {
		return nil, errSubscriptionExists
	}

	// add subscription to map
	s.connector.subscriptions[s.Key()] = s

	s.logger.Debug("New subscription")
	if store {
		if err := s.store(); err != nil {
			return nil, err
		}
	}

	return s, s.restart()
}

func subscriptionMatcher(route, other router.RouteConfig, keys ...string) bool {
	return route.Path == other.Path && route.Get(applicationIDKey) == other.Get(applicationIDKey)
}

// newSubscription creates a subscription and returns the pointer
func newSubscription(connector *Connector, route *router.Route, lastID uint64) *subscription {
	subLogger := logger.WithFields(log.Fields{
		"fcmID":  route.Get(applicationIDKey),
		"userID": route.Get(userIDKey),
		"topic":  string(route.Path),
		"lastID": lastID,
	})
	if connector.cluster != nil {
		subLogger = subLogger.WithField("nodeID", connector.cluster.Config.ID)
	}

	return &subscription{
		connector: connector,
		route:     route,
		lastID:    lastID,
		logger:    subLogger,
	}
}

// exists returns true if the subscription is present with the same key in subscriptions map
func (s *subscription) exists() bool {
	_, ok := s.connector.subscriptions[s.Key()]
	return ok
}

// restart recreates the route and resubscribes
func (s *subscription) restart() error {
	s.route = router.NewRoute(router.RouteConfig{
		RouteParams: s.route.RouteParams,
		Path:        s.route.Path,
		ChannelSize: subBufferSize,
	})

	// subscribe to the router and start the loop
	return s.start()
}

// start loop to receive messages from route
func (s *subscription) start() error {
	s.route.FetchRequest = s.createFetchRequest()
	go s.subscriptionLoop()
	if err := s.route.Provide(s.connector.router, true); err != nil {
		return err
	}
	return nil
}

func (s *subscription) createFetchRequest() *store.FetchRequest {
	if s.lastID <= 0 {
		return nil
	}
	return store.NewFetchRequest("", s.lastID+1, 0, store.DirectionForward, -1)
}

// subscriptionLoop that will run in a goroutine and pipe messages from route to fcm
// Attention: in order for this loop to finish the route channel must stop sending messages
func (s *subscription) subscriptionLoop() {
	s.logger.Debug("Starting subscription loop")

	s.connector.wg.Add(1)
	defer func() {
		s.logger.Debug("Stopped subscription loop")
		s.connector.wg.Done()
	}()

	var (
		m      *protocol.Message
		opened = true
	)
	for opened {
		select {
		case m, opened = <-s.route.MessagesChannel():
			// TODO Bogdan This needs to be remade and we should gracefully shutdown
			// and wait for this channel to empty before stopping the loop
			select {
			case <-s.connector.stopC:
				return
			case <-time.After(5 * time.Millisecond):
			}

			if !opened {
				s.logger.Error("Route channel is closed")
				continue
			}

			if err := s.pipe(m); err != nil {
				// abandon route if the following 2 errors are met
				// the subscription has been replaced
				if err == errSubscriptionReplaced {
					return
				}
				// the subscription is not registered with FCM anymore
				if _, ok := err.(*jsonError); ok {
					return
				}

				s.logger.WithError(err).Error("Error pipelining message")
			}
		case <-s.connector.stopC:
			return
		}
	}

	// if route is closed and we are actually stopping then return
	if s.isStopping() {
		return
	}

	// assume that the route channel has been closed because of slow processing
	// try restarting, by fetching from lastId and then subscribing again
	if err := s.restart(); err != nil {
		if stoppingErr, ok := err.(*router.ModuleStoppingError); ok {
			s.logger.WithField("error", stoppingErr).Debug("Error restarting subscription")
		}
	}
}

// isStopping returns true if we are actually stopping the service
func (s *subscription) isStopping() bool {
	select {
	case <-s.connector.stopC:
		return true
	default:
	}
	return false
}

// Key returns a string that uniquely identifies this subscription
func (s *subscription) Key() string {
	return s.route.Key()
}

// pipe sends a message into the pipeline and waits for response saving the last id in the kvstore
func (s *subscription) pipe(m *protocol.Message) error {
	subscriptionMessage := newSubscriptionMessage(s, m)
	defer subscriptionMessage.closeChannels()

	s.connector.pipelineC <- subscriptionMessage

	// wait for response
	select {
	case response := <-subscriptionMessage.resultC:
		s.logger.WithField("messageID", m.ID).Debug("Delivered message to FCM")
		if err := s.setLastID(subscriptionMessage.message.ID); err != nil {
			return err
		}
		if response.Ok() {
			return nil
		}

		s.logger.WithField("count", response.Success).Debug("Handling FCM Error")
		if err := s.handleJSONError(response); err != nil {
			return err
		}

		if response.CanonicalIDs != 0 {
			// we only send to one receiver,
			// so we know that we can replace the old id with the first registration id (=canonical id)
			return s.replaceCanonical(response.Results[0].RegistrationID)
		}
		return nil

	case err := <-subscriptionMessage.errC:
		if err == errIgnoreMessage {
			s.logger.WithField("message", m).Info("Ignoring message")
			return nil
		}
		s.logger.WithField("error", err.Error()).Error("Error sending message to FCM")
		return err
	}
}

func (s *subscription) handleJSONError(response *gcm.Response) error {
	switch errText := response.Error.Error(); errText {
	case "NotRegistered":
		s.logger.Debug("Removing not registered FCM subscription")
		s.remove()
		return &jsonError{errText}
	case "InvalidRegistration":
		s.logger.WithField("jsonError", errText).Error("Subscription is not registered")
		return nil
	default:
		s.logger.WithField("jsonError", errText).Error("Unexpected error while sending to FCM")
		return nil
	}
}

// replaceCanonical replaces subscription with canonical id,
// creates a new subscription but alters the route to have the new ApplicationID
func (s *subscription) replaceCanonical(newFCMID string) error {
	s.logger.WithField("newFCMID", newFCMID).Info("Replacing with FCM canonicalID")
	// delete current route from kvstore
	s.remove()

	// reuse the route but change the ApplicationID
	route := s.route
	route.Set(applicationIDKey, newFCMID)
	newSub := newSubscription(s.connector, route, s.lastID)

	if err := newSub.store(); err != nil {
		return err
	}
	newSub.start()
	return errSubscriptionReplaced
}

func (s *subscription) setLastID(ID uint64) error {
	s.lastID = ID
	// update KV when last id is set
	return s.store()
}

// store data in kvstore
func (s *subscription) store() error {
	s.logger.WithField("lastID", s.lastID).Debug("Storing subscription")
	applicationID := s.route.Get(applicationIDKey)
	err := s.connector.kvStore.Put(schema, applicationID, s.bytes())
	if err != nil {
		s.logger.WithError(err).Error("Error storing in KVStore")
	}
	return err
}

// bytes returns the data to store in kvStore
func (s *subscription) bytes() []byte {
	return []byte(strings.Join([]string{
		s.route.Get(userIDKey),
		string(s.route.Path),
		strconv.FormatUint(s.lastID, 10),
	}, ":"))
}

// remove unsubscribes from router, delete from connector's subscriptions, and remove from KVStore
func (s *subscription) remove() *subscription {
	s.logger.Debug("Removing subscription")
	s.connector.router.Unsubscribe(s.route)
	delete(s.connector.subscriptions, s.Key())
	s.connector.kvStore.Delete(schema, s.Key())
	return s
}
<commit_msg>inlining func - fcm error-handling<commit_after>package fcm

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server/router"
	"github.com/smancke/guble/server/store"
	"strconv"
	"strings"
	"time"
)

const (
	// default subscription channel buffer size
	subBufferSize = 50

	// applicationIDKey is the key name set on the route params to identify the application
	applicationIDKey = "device_id"

	// userIDKey is the key name set on the route params to identify the user
	userIDKey = "user_id"
)

var (
	errIgnoreMessage        = errors.New("Message ignored")
	errSubscriptionExists   = errors.New("Subscription exists")
	errSubscriptionReplaced = errors.New("Subscription replaced")
)

// subscription represents a FCM subscription
type subscription struct {
	connector *Connector
	route     *router.Route
	lastID    uint64 // Last sent message id

	logger *log.Entry
}

// initSubscription creates a subscription and adds it in router/kvstore then starts listening for messages
func initSubscription(connector *Connector, topic, userID, fcmID string, lastID uint64, store bool) (*subscription, error) {
	route := router.NewRoute(router.RouteConfig{
		RouteParams: router.RouteParams{userIDKey: userID, applicationIDKey: fcmID},
		Path:        protocol.Path(topic),
		ChannelSize: subBufferSize,
		Matcher:     subscriptionMatcher,
	})

	s := newSubscription(connector, route, lastID)
	if s.exists() {
		return nil, errSubscriptionExists
	}

	// add subscription to map
	s.connector.subscriptions[s.Key()] = s

	s.logger.Debug("New subscription")
	if store {
		if err := s.store(); err != nil {
			return nil, err
		}
	}

	return s, s.restart()
}

func subscriptionMatcher(route, other router.RouteConfig, keys ...string) bool {
	return route.Path == other.Path && route.Get(applicationIDKey) == other.Get(applicationIDKey)
}

// newSubscription creates a subscription and returns the pointer
func newSubscription(connector *Connector, route *router.Route, lastID uint64) *subscription {
	subLogger := logger.WithFields(log.Fields{
		"fcmID":  route.Get(applicationIDKey),
		"userID": route.Get(userIDKey),
		"topic":  string(route.Path),
		"lastID": lastID,
	})
	if connector.cluster != nil {
		subLogger = subLogger.WithField("nodeID", connector.cluster.Config.ID)
	}

	return &subscription{
		connector: connector,
		route:     route,
		lastID:    lastID,
		logger:    subLogger,
	}
}

// exists returns true if the subscription is present with the same key in subscriptions map
func (s *subscription) exists() bool {
	_, ok := s.connector.subscriptions[s.Key()]
	return ok
}

// restart recreates the route and resubscribes
func (s *subscription) restart() error {
	s.route = router.NewRoute(router.RouteConfig{
		RouteParams: s.route.RouteParams,
		Path:        s.route.Path,
		ChannelSize: subBufferSize,
	})

	// subscribe to the router and start the loop
	return s.start()
}

// start loop to receive messages from route
func (s *subscription) start() error {
	s.route.FetchRequest = s.createFetchRequest()
	go s.subscriptionLoop()
	if err := s.route.Provide(s.connector.router, true); err != nil {
		return err
	}
	return nil
}

func (s *subscription) createFetchRequest() *store.FetchRequest {
	if s.lastID <= 0 {
		return nil
	}
	return store.NewFetchRequest("", s.lastID+1, 0, store.DirectionForward, -1)
}

// subscriptionLoop that will run in a goroutine and pipe messages from route to fcm
// Attention: in order for this loop to finish the route channel must stop sending messages
func (s *subscription) subscriptionLoop() {
	s.logger.Debug("Starting subscription loop")

	s.connector.wg.Add(1)
	defer func() {
		s.logger.Debug("Stopped subscription loop")
		s.connector.wg.Done()
	}()

	var (
		m      *protocol.Message
		opened = true
	)
	for opened {
		select {
		case m, opened = <-s.route.MessagesChannel():
			// TODO Bogdan This needs to be remade and we should gracefully shutdown
			// and wait for this channel to empty before stopping the loop
			select {
			case <-s.connector.stopC:
				return
			case <-time.After(5 * time.Millisecond):
			}

			if !opened {
				s.logger.Error("Route channel is closed")
				continue
			}

			if err := s.pipe(m); err != nil {
				// abandon route if the following 2 errors are met
				// the subscription has been replaced
				if err == errSubscriptionReplaced {
					return
				}
				// the subscription is not registered with FCM anymore
				if _, ok := err.(*jsonError); ok {
					return
				}

				s.logger.WithError(err).Error("Error pipelining message")
			}
		case <-s.connector.stopC:
			return
		}
	}

	// if route is closed and we are actually stopping then return
	if s.isStopping() {
		return
	}

	// assume that the route channel has been closed because of slow processing
	// try restarting, by fetching from lastId and then subscribing again
	if err := s.restart(); err != nil {
		if stoppingErr, ok := err.(*router.ModuleStoppingError); ok {
			s.logger.WithField("error", stoppingErr).Debug("Error restarting subscription")
		}
	}
}

// isStopping returns true if we are actually stopping the service
func (s *subscription) isStopping() bool {
	select {
	case <-s.connector.stopC:
		return true
	default:
	}
	return false
}

// Key returns a string that uniquely identifies this subscription
func (s *subscription) Key() string {
	return s.route.Key()
}

// pipe sends a message into the pipeline and waits for response saving the last id in the kvstore
func (s *subscription) pipe(m *protocol.Message) error {
	subscriptionMessage := newSubscriptionMessage(s, m)
	defer subscriptionMessage.closeChannels()

	s.connector.pipelineC <- subscriptionMessage

	// wait for response
	select {
	case response := <-subscriptionMessage.resultC:
		s.logger.WithField("messageID", m.ID).Debug("Delivered message to FCM")
		if err := s.setLastID(subscriptionMessage.message.ID); err != nil {
			return err
		}
		if response.Ok() {
			return nil
		}

		s.logger.WithField("success", response.Success).Debug("Handling FCM Error")

		switch errText := response.Error.Error(); errText {
		case "NotRegistered":
			s.logger.Debug("Removing not registered FCM subscription")
			s.remove()
			return &jsonError{errText}
		case "InvalidRegistration":
			s.logger.WithField("jsonError", errText).Error("InvalidRegistration of FCM subscription")
		default:
			s.logger.WithField("jsonError", errText).Error("Unexpected error while sending to FCM")
		}

		if response.CanonicalIDs != 0 {
			// we only send to one receiver, so we know that we can replace the old id with the first registration id (=canonical id)
			return s.replaceCanonical(response.Results[0].RegistrationID)
		}
		return nil

	case err := <-subscriptionMessage.errC:
		if err == errIgnoreMessage {
			s.logger.WithField("message", m).Info("Ignoring message")
			return nil
		}
		s.logger.WithField("error", err.Error()).Error("Error sending message to FCM")
		return err
	}
}

// replaceCanonical replaces subscription with canonical id,
// creates a new subscription but alters the route to have the new ApplicationID
func (s *subscription) replaceCanonical(newFCMID string) error {
	s.logger.WithField("newFCMID", newFCMID).Info("Replacing with FCM canonicalID")
	// delete current route from kvstore
	s.remove()

	// reuse the route but change the ApplicationID
	route := s.route
	route.Set(applicationIDKey, newFCMID)
	newSub := newSubscription(s.connector, route, s.lastID)

	if err := newSub.store(); err != nil {
		return err
	}
	newSub.start()
	return errSubscriptionReplaced
}

func (s *subscription) setLastID(ID uint64) error {
	s.lastID = ID
	// update KV when last id is set
	return s.store()
}

// store data in kvstore
func (s *subscription) store() error {
	s.logger.WithField("lastID", s.lastID).Debug("Storing subscription")
	applicationID := s.route.Get(applicationIDKey)
	err := s.connector.kvStore.Put(schema, applicationID, s.bytes())
	if err != nil {
		s.logger.WithError(err).Error("Error storing in KVStore")
	}
	return err
}

// bytes returns the data to store in kvStore
func (s *subscription) bytes() []byte {
	return []byte(strings.Join([]string{
		s.route.Get(userIDKey),
		string(s.route.Path),
		strconv.FormatUint(s.lastID, 10),
	}, ":"))
}

// remove unsubscribes from router, delete from connector's subscriptions, and remove from KVStore
func (s *subscription) remove() *subscription {
	s.logger.Debug("Removing subscription")
	s.connector.router.Unsubscribe(s.route)
	delete(s.connector.subscriptions, s.Key())
	s.connector.kvStore.Delete(schema, s.Key())
	return s
}
<|endoftext|>


<commit_before>// +build darwin

package process

import (
	"bytes"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"unsafe"

	common "github.com/shirou/gopsutil/common"
	cpu "github.com/shirou/gopsutil/cpu"
	net "github.com/shirou/gopsutil/net"
)

// copied from sys/sysctl.h
const (
	CTLKern          = 1  // "high kernel": proc, limits
	KernProc         = 14 // struct: process entries
	KernProcPID      = 1  // by process id
	KernProcProc     = 8  // only return procs
	KernProcAll      = 0  // everything
	KernProcPathname = 12 // path to executable
)

type _Ctype_struct___0 struct {
	Pad uint64
}

// MemoryInfoExStat is different between OSes
type MemoryInfoExStat struct {
}

type MemoryMapsStat struct {
}

func Pids() ([]int32, error) {
	var ret []int32

	pids, err := callPs("pid", 0)
	if err != nil {
		return ret, err
	}

	for _, pid := range pids {
		v, err := strconv.Atoi(pid[0])
		if err != nil {
			return ret, err
		}
		ret = append(ret, int32(v))
	}

	return ret, nil
}

func (p *Process) Ppid() (int32, error) {
	r, err := callPs("ppid", p.Pid)
	v, err := strconv.Atoi(r[0][0])
	if err != nil {
		return 0, err
	}

	return int32(v), err
}
func (p *Process) Name() (string, error) {
	k, err := p.getKProc()
	if err != nil {
		return "", err
	}

	return common.IntToString(k.Proc.P_comm[:]), nil
}
func (p *Process) Exe() (string, error) {
	return "", common.NotImplementedError
}
func (p *Process) Cmdline() (string, error) {
	r, err := callPs("command", p.Pid)
	if err != nil {
		return "", err
	}
	return strings.Join(r[0], " "), err
}
func (p *Process) CreateTime() (int64, error) {
	return 0, common.NotImplementedError
}
func (p *Process) Cwd() (string, error) {
	return "", common.NotImplementedError
}
func (p *Process) Parent() (*Process, error) {
	return p, common.NotImplementedError
}
func (p *Process) Status() (string, error) {
	r, err := callPs("state", p.Pid)
	if err != nil {
		return "", err
	}

	return r[0][0], err
}
func (p *Process) Uids() ([]int32, error) {
	k, err := p.getKProc()
	if err != nil {
		return nil, err
	}

	uids := make([]int32, 0, 3)

	uids = append(uids, int32(k.Eproc.Pcred.P_ruid), int32(k.Eproc.Ucred.Uid), int32(k.Eproc.Pcred.P_svuid))

	return uids, nil
}
func (p *Process) Gids() ([]int32, error) {
	k, err := p.getKProc()
	if err != nil {
		return nil, err
	}

	gids := make([]int32, 0, 3)
	gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid))

	return gids, nil
}
func (p *Process) Terminal() (string, error) {
	return "", common.NotImplementedError
	/*
		k, err := p.getKProc()
		if err != nil {
			return "", err
		}

		ttyNr := uint64(k.Eproc.Tdev)
		termmap, err := getTerminalMap()
		if err != nil {
			return "", err
		}

		return termmap[ttyNr], nil
	*/
}
func (p *Process) Nice() (int32, error) {
	k, err := p.getKProc()
	if err != nil {
		return 0, err
	}
	return int32(k.Proc.P_nice), nil
}
func (p *Process) IOnice() (int32, error) {
	return 0, common.NotImplementedError
}
func (p *Process) Rlimit() ([]RlimitStat, error) {
	var rlimit []RlimitStat
	return rlimit, common.NotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
	return nil, common.NotImplementedError
}
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
	return nil, common.NotImplementedError
}
func (p *Process) NumFDs() (int32, error) {
	return 0, common.NotImplementedError
}
func (p *Process) NumThreads() (int32, error) {
	return 0, common.NotImplementedError

	/*
		k, err := p.getKProc()
		if err != nil {
			return 0, err
		}

			return k.KiNumthreads, nil
	*/
}
func (p *Process) Threads() (map[string]string, error) {
	ret := make(map[string]string, 0)
	return ret, common.NotImplementedError
}
func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
	return nil, common.NotImplementedError
}
func (p *Process) CPUAffinity() ([]int32, error) {
	return nil, common.NotImplementedError
}
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
	r, err := callPs("rss,vsize,pagein", p.Pid)
	if err != nil {
		return nil, err
	}
	rss, err := strconv.Atoi(r[0][0])
	if err != nil {
		return nil, err
	}
	vms, err := strconv.Atoi(r[0][1])
	if err != nil {
		return nil, err
	}
	pagein, err := strconv.Atoi(r[0][2])
	if err != nil {
		return nil, err
	}

	ret := &MemoryInfoStat{
		RSS:  uint64(rss),
		VMS:  uint64(vms),
		Swap: uint64(pagein),
	}

	return ret, nil
}
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
	return nil, common.NotImplementedError
}
func (p *Process) MemoryPercent() (float32, error) {
	return 0, common.NotImplementedError
}

func (p *Process) Children() ([]*Process, error) {
	return nil, common.NotImplementedError
}

func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
	return nil, common.NotImplementedError
}

func (p *Process) Connections() ([]net.NetConnectionStat, error) {
	return nil, common.NotImplementedError
}

func (p *Process) IsRunning() (bool, error) {
	return true, common.NotImplementedError
}
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
	var ret []MemoryMapsStat
	return &ret, common.NotImplementedError
}

func copyParams(k *KinfoProc, p *Process) error {

	return nil
}

func processes() ([]Process, error) {
	results := make([]Process, 0, 50)

	mib := []int32{CTLKern, KernProc, KernProcAll, 0}
	buf, length, err := common.CallSyscall(mib)
	if err != nil {
		return results, err
	}

	// get kinfo_proc size
	k := KinfoProc{}
	procinfoLen := int(unsafe.Sizeof(k))
	count := int(length / uint64(procinfoLen))
	/*
		fmt.Println(length, procinfoLen, count)
		b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen]
		fmt.Println(b)
		kk, err := parseKinfoProc(b)
		fmt.Printf("%#v", kk)
	*/

	// parse buf to procs
	for i := 0; i < count; i++ {
		b := buf[i*procinfoLen : i*procinfoLen+procinfoLen]
		k, err := parseKinfoProc(b)
		if err != nil {
			continue
		}
		p, err := NewProcess(int32(k.Proc.P_pid))
		if err != nil {
			continue
		}
		copyParams(&k, p)

		results = append(results, *p)
	}

	return results, nil
}

func parseKinfoProc(buf []byte) (KinfoProc, error) {
	var k KinfoProc
	br := bytes.NewReader(buf)

	err := Read(br, LittleEndian, &k)
	if err != nil {
		return k, err
	}

	return k, nil
}

func (p *Process) getKProc() (*KinfoProc, error) {
	mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}
	procK := KinfoProc{}
	length := uint64(unsafe.Sizeof(procK))
	buf := make([]byte, length)
	_, _, syserr := syscall.Syscall6(
		syscall.SYS___SYSCTL,
		uintptr(unsafe.Pointer(&mib[0])),
		uintptr(len(mib)),
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(unsafe.Pointer(&length)),
		0,
		0)
	if syserr != 0 {
		return nil, syserr
	}
	k, err := parseKinfoProc(buf)
	if err != nil {
		return nil, err
	}

	return &k, nil
}

func NewProcess(pid int32) (*Process, error) {
	p := &Process{Pid: pid}

	return p, nil
}

// call ps command.
// Return value deletes Header line(you must not input wrong arg).
// And splited by Space. Caller have responsibility to manage.
// If passed arg pid is 0, get information from all process.
func callPs(arg string, pid int32) ([][]string, error) {
	var cmd []string
	if pid == 0 { // will get from all processes.
		cmd = []string{"-x", "-o", arg}
	} else {
		cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
	}
	out, err := exec.Command("/bin/ps", cmd...).Output()
	if err != nil {
		return [][]string{}, err
	}
	lines := strings.Split(string(out), "\n")

	var ret [][]string
	for _, l := range lines[1:] {
		var lr []string
		for _, r := range strings.Split(l, " ") {
			if r == "" {
				continue
			}
			lr = append(lr, strings.TrimSpace(r))
		}
		if len(lr) != 0 {
			ret = append(ret, lr)
		}
	}

	return ret, nil
}
<commit_msg>implement CPUTimes (and CPUPercent) on darwin. use ps command.<commit_after>// +build darwin

package process

import (
	"bytes"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"unsafe"

	common "github.com/shirou/gopsutil/common"
	cpu "github.com/shirou/gopsutil/cpu"
	net "github.com/shirou/gopsutil/net"
)

// copied from sys/sysctl.h
const (
	CTLKern          = 1  // "high kernel": proc, limits
	KernProc         = 14 // struct: process entries
	KernProcPID      = 1  // by process id
	KernProcProc     = 8  // only return procs
	KernProcAll      = 0  // everything
	KernProcPathname = 12 // path to executable
)

const (
	ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK)
)

type _Ctype_struct___0 struct {
	Pad uint64
}

// MemoryInfoExStat is different between OSes
type MemoryInfoExStat struct {
}

type MemoryMapsStat struct {
}

func Pids() ([]int32, error) {
	var ret []int32

	pids, err := callPs("pid", 0)
	if err != nil {
		return ret, err
	}

	for _, pid := range pids {
		v, err := strconv.Atoi(pid[0])
		if err != nil {
			return ret, err
		}
		ret = append(ret, int32(v))
	}

	return ret, nil
}

func (p *Process) Ppid() (int32, error) {
	r, err := callPs("ppid", p.Pid)
	v, err := strconv.Atoi(r[0][0])
	if err != nil {
		return 0, err
	}

	return int32(v), err
}
func (p *Process) Name() (string, error) {
	k, err := p.getKProc()
	if err != nil {
		return "", err
	}

	return common.IntToString(k.Proc.P_comm[:]), nil
}
func (p *Process) Exe() (string, error) {
	return "", common.NotImplementedError
}
func (p *Process) Cmdline() (string, error) {
	r, err := callPs("command", p.Pid)
	if err != nil {
		return "", err
	}
	return strings.Join(r[0], " "), err
}
func (p *Process) CreateTime() (int64, error) {
	return 0, common.NotImplementedError
}
func (p *Process) Cwd() (string, error) {
	return "", common.NotImplementedError
}
func (p *Process) Parent() (*Process, error) {
	return p, common.NotImplementedError
}
func (p *Process) Status() (string, error) {
	r, err := callPs("state", p.Pid)
	if err != nil {
		return "", err
	}

	return r[0][0], err
}
func (p *Process) Uids() ([]int32, error) {
	k, err := p.getKProc()
	if err != nil {
		return nil, err
	}

	uids := make([]int32, 0, 3)

	uids = append(uids, int32(k.Eproc.Pcred.P_ruid), int32(k.Eproc.Ucred.Uid), int32(k.Eproc.Pcred.P_svuid))

	return uids, nil
}
func (p *Process) Gids() ([]int32, error) {
	k, err := p.getKProc()
	if err != nil {
		return nil, err
	}

	gids := make([]int32, 0, 3)
	gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid))

	return gids, nil
}
func (p *Process) Terminal() (string, error) {
	return "", common.NotImplementedError
	/*
		k, err := p.getKProc()
		if err != nil {
			return "", err
		}

		ttyNr := uint64(k.Eproc.Tdev)
		termmap, err := getTerminalMap()
		if err != nil {
			return "", err
		}

		return termmap[ttyNr], nil
	*/
}
func (p *Process) Nice() (int32, error) {
	k, err := p.getKProc()
	if err != nil {
		return 0, err
	}
	return int32(k.Proc.P_nice), nil
}
func (p *Process) IOnice() (int32, error) {
	return 0, common.NotImplementedError
}
func (p *Process) Rlimit() ([]RlimitStat, error) {
	var rlimit []RlimitStat
	return rlimit, common.NotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
	return nil, common.NotImplementedError
}
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
	return nil, common.NotImplementedError
}
func (p *Process) NumFDs() (int32, error) {
	return 0, common.NotImplementedError
}
func (p *Process) NumThreads() (int32, error) {
	return 0, common.NotImplementedError

	/*
		k, err := p.getKProc()
		if err != nil {
			return 0, err
		}

			return k.KiNumthreads, nil
	*/
}
func (p *Process) Threads() (map[string]string, error) {
	ret := make(map[string]string, 0)
	return ret, common.NotImplementedError
}

func convertCpuTimes(s string) (ret float64, err error) {
	var t int
	var _tmp string
	if strings.Contains(s, ":") {
		_t := strings.Split(s, ":")
		hour, err := strconv.Atoi(_t[0])
		if err != nil {
			return ret, err
		}
		t += hour * 60 * 100
		_tmp = _t[1]
	} else {
		_tmp = s
	}

	_t := strings.Split(_tmp, ".")
	if err != nil {
		return ret, err
	}
	h, err := strconv.Atoi(_t[0])
	t += h * 100
	h, err = strconv.Atoi(_t[1])
	t += h
	return float64(t) / ClockTicks, nil
}
func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
	r, err := callPs("utime,stime", p.Pid)

	utime, err := convertCpuTimes(r[0][0])
	if err != nil {
		return nil, err
	}
	stime, err := convertCpuTimes(r[0][1])
	if err != nil {
		return nil, err
	}

	ret := &cpu.CPUTimesStat{
		CPU:    "cpu",
		User:   utime,
		System: stime,
	}
	return ret, nil
}
func (p *Process) CPUAffinity() ([]int32, error) {
	return nil, common.NotImplementedError
}
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
	r, err := callPs("rss,vsize,pagein", p.Pid)
	if err != nil {
		return nil, err
	}
	rss, err := strconv.Atoi(r[0][0])
	if err != nil {
		return nil, err
	}
	vms, err := strconv.Atoi(r[0][1])
	if err != nil {
		return nil, err
	}
	pagein, err := strconv.Atoi(r[0][2])
	if err != nil {
		return nil, err
	}

	ret := &MemoryInfoStat{
		RSS:  uint64(rss),
		VMS:  uint64(vms),
		Swap: uint64(pagein),
	}

	return ret, nil
}
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
	return nil, common.NotImplementedError
}
func (p *Process) MemoryPercent() (float32, error) {
	return 0, common.NotImplementedError
}

func (p *Process) Children() ([]*Process, error) {
	return nil, common.NotImplementedError
}

func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
	return nil, common.NotImplementedError
}

func (p *Process) Connections() ([]net.NetConnectionStat, error) {
	return nil, common.NotImplementedError
}

func (p *Process) IsRunning() (bool, error) {
	return true, common.NotImplementedError
}
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
	var ret []MemoryMapsStat
	return &ret, common.NotImplementedError
}

func copyParams(k *KinfoProc, p *Process) error {

	return nil
}

func processes() ([]Process, error) {
	results := make([]Process, 0, 50)

	mib := []int32{CTLKern, KernProc, KernProcAll, 0}
	buf, length, err := common.CallSyscall(mib)
	if err != nil {
		return results, err
	}

	// get kinfo_proc size
	k := KinfoProc{}
	procinfoLen := int(unsafe.Sizeof(k))
	count := int(length / uint64(procinfoLen))
	/*
		fmt.Println(length, procinfoLen, count)
		b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen]
		fmt.Println(b)
		kk, err := parseKinfoProc(b)
		fmt.Printf("%#v", kk)
	*/

	// parse buf to procs
	for i := 0; i < count; i++ {
		b := buf[i*procinfoLen : i*procinfoLen+procinfoLen]
		k, err := parseKinfoProc(b)
		if err != nil {
			continue
		}
		p, err := NewProcess(int32(k.Proc.P_pid))
		if err != nil {
			continue
		}
		copyParams(&k, p)

		results = append(results, *p)
	}

	return results, nil
}

func parseKinfoProc(buf []byte) (KinfoProc, error) {
	var k KinfoProc
	br := bytes.NewReader(buf)

	err := Read(br, LittleEndian, &k)
	if err != nil {
		return k, err
	}

	return k, nil
}

func (p *Process) getKProc() (*KinfoProc, error) {
	mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}
	procK := KinfoProc{}
	length := uint64(unsafe.Sizeof(procK))
	buf := make([]byte, length)
	_, _, syserr := syscall.Syscall6(
		syscall.SYS___SYSCTL,
		uintptr(unsafe.Pointer(&mib[0])),
		uintptr(len(mib)),
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(unsafe.Pointer(&length)),
		0,
		0)
	if syserr != 0 {
		return nil, syserr
	}
	k, err := parseKinfoProc(buf)
	if err != nil {
		return nil, err
	}

	return &k, nil
}

func NewProcess(pid int32) (*Process, error) {
	p := &Process{Pid: pid}

	return p, nil
}

// call ps command.
// Return value deletes Header line(you must not input wrong arg).
// And splited by Space. Caller have responsibility to manage.
// If passed arg pid is 0, get information from all process.
func callPs(arg string, pid int32) ([][]string, error) {
	var cmd []string
	if pid == 0 { // will get from all processes.
		cmd = []string{"-x", "-o", arg}
	} else {
		cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
	}
	out, err := exec.Command("/bin/ps", cmd...).Output()
	if err != nil {
		return [][]string{}, err
	}
	lines := strings.Split(string(out), "\n")

	var ret [][]string
	for _, l := range lines[1:] {
		var lr []string
		for _, r := range strings.Split(l, " ") {
			if r == "" {
				continue
			}
			lr = append(lr, strings.TrimSpace(r))
		}
		if len(lr) != 0 {
			ret = append(ret, lr)
		}
	}

	return ret, nil
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/cosminrentea/gobbler/client"
	"github.com/cosminrentea/gobbler/protocol"
	"github.com/cosminrentea/gobbler/server/service"

	"github.com/stretchr/testify/assert"

	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/cosminrentea/gobbler/restclient"
	"github.com/cosminrentea/gobbler/testutil"
)

func initServerAndClients(t *testing.T) (*service.Service, client.Client, client.Client, func()) {
	*Config.HttpListen = "localhost:0"
	*Config.KVS = "memory"
	s := StartService()

	time.Sleep(time.Millisecond * 100)

	var err error
	client1, err := client.Open("ws://"+s.WebServer().GetAddr()+"/stream/user/user1", "http://localhost", 1, false)
	assert.NoError(t, err)

	checkConnectedNotificationJSON(t, "user1",
		expectStatusMessage(t, client1, protocol.SUCCESS_CONNECTED, "You are connected to the server."),
	)

	client2, err := client.Open("ws://"+s.WebServer().GetAddr()+"/stream/user/user2", "http://localhost", 1, false)
	assert.NoError(t, err)
	checkConnectedNotificationJSON(t, "user2",
		expectStatusMessage(t, client2, protocol.SUCCESS_CONNECTED, "You are connected to the server."),
	)

	return s, client1, client2, func() {
		if client1 != nil {
			client1.Close()
		}
		if client2 != nil {
			client2.Close()
		}
		s.Stop()
	}
}

func expectStatusMessage(t *testing.T, client client.Client, name string, arg string) string {
	select {
	case notify := <-client.StatusMessages():
		assert.Equal(t, name, notify.Name)
		assert.Equal(t, arg, notify.Arg)
		return notify.Json
	case <-time.After(time.Second * 10):
		t.Logf("no notification of type %s after 2 second", name)
		t.Fail()
		return ""
	}
}

func checkConnectedNotificationJSON(t *testing.T, user string, connectedJSON string) {
	m := make(map[string]string)
	err := json.Unmarshal([]byte(connectedJSON), &m)
	assert.NoError(t, err)
	assert.Equal(t, user, m["UserId"])
	assert.True(t, len(m["ApplicationId"]) > 0)
	_, e := time.Parse(time.RFC3339, m["Time"])
	assert.NoError(t, e)
}

//Used only for test and unmarshalling of the json response
type Subscriber struct {
	DeviceToken string `json:"device_token"`
	UserID      string `json:"user_id"`
}

func TestSubscribersIntegration(t *testing.T) {
	testutil.SkipIfShort(t)
	testutil.SkipIfDisabled(t)

	defer testutil.ResetDefaultRegistryHealthCheck()
	defer testutil.EnableDebugForMethod()()

	a := assert.New(t)

	s, cleanup := serviceSetUp(t)
	defer cleanup()

	subscribeMultipleClients(t, s, 4)
	a.Nil(nil)

	restClient := restclient.New(fmt.Sprintf("http://%s/api", s.WebServer().GetAddr()))
	content, err := restClient.GetSubscribers(testTopic)
	a.NoError(err)
	routeParams := make([]*Subscriber, 0)

	err = json.Unmarshal(content, &routeParams)
	a.Equal(4, len(routeParams), "Should have 4 subscribers")
	for i, rp := range routeParams {
		a.Equal(fmt.Sprintf("gcmId%d", i), rp.DeviceToken)
		a.Equal(fmt.Sprintf("user%d", i), rp.UserID)
	}
	a.NoError(err)
}

func subscribeMultipleClients(t *testing.T, service *service.Service, noOfClients int) {
	a := assert.New(t)

	// create FCM subscription for topic
	for i := 0; i < noOfClients; i++ {
		urlFormat := fmt.Sprintf("http://%s/fcm/gcmId%%d/user%%d/%%s", service.WebServer().GetAddr())
		url := fmt.Sprintf(urlFormat, i, i, strings.TrimPrefix(testTopic, "/"))
		response, errPost := http.Post(
			url,
			"text/plain",
			bytes.NewBufferString(""),
		)
		logger.WithField("url", url).Debug("subscribe")
		a.NoError(errPost)
		a.Equal(response.StatusCode, 200)

		body, errReadAll := ioutil.ReadAll(response.Body)
		a.NoError(errReadAll)
		a.Equal(fmt.Sprintf(`{"subscribed":"%s"}`, testTopic), string(body))
	}
}
<commit_msg>Skip only if disabled<commit_after>package server

import (
	"github.com/cosminrentea/gobbler/client"
	"github.com/cosminrentea/gobbler/protocol"
	"github.com/cosminrentea/gobbler/server/service"

	"github.com/stretchr/testify/assert"

	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/cosminrentea/gobbler/restclient"
	"github.com/cosminrentea/gobbler/testutil"
)

func initServerAndClients(t *testing.T) (*service.Service, client.Client, client.Client, func()) {
	*Config.HttpListen = "localhost:0"
	*Config.KVS = "memory"
	s := StartService()

	time.Sleep(time.Millisecond * 100)

	var err error
	client1, err := client.Open("ws://"+s.WebServer().GetAddr()+"/stream/user/user1", "http://localhost", 1, false)
	assert.NoError(t, err)

	checkConnectedNotificationJSON(t, "user1",
		expectStatusMessage(t, client1, protocol.SUCCESS_CONNECTED, "You are connected to the server."),
	)

	client2, err := client.Open("ws://"+s.WebServer().GetAddr()+"/stream/user/user2", "http://localhost", 1, false)
	assert.NoError(t, err)
	checkConnectedNotificationJSON(t, "user2",
		expectStatusMessage(t, client2, protocol.SUCCESS_CONNECTED, "You are connected to the server."),
	)

	return s, client1, client2, func() {
		if client1 != nil {
			client1.Close()
		}
		if client2 != nil {
			client2.Close()
		}
		s.Stop()
	}
}

func expectStatusMessage(t *testing.T, client client.Client, name string, arg string) string {
	select {
	case notify := <-client.StatusMessages():
		assert.Equal(t, name, notify.Name)
		assert.Equal(t, arg, notify.Arg)
		return notify.Json
	case <-time.After(time.Second * 10):
		t.Logf("no notification of type %s after 2 second", name)
		t.Fail()
		return ""
	}
}

func checkConnectedNotificationJSON(t *testing.T, user string, connectedJSON string) {
	m := make(map[string]string)
	err := json.Unmarshal([]byte(connectedJSON), &m)
	assert.NoError(t, err)
	assert.Equal(t, user, m["UserId"])
	assert.True(t, len(m["ApplicationId"]) > 0)
	_, e := time.Parse(time.RFC3339, m["Time"])
	assert.NoError(t, e)
}

//Used only for test and unmarshalling of the json response
type Subscriber struct {
	DeviceToken string `json:"device_token"`
	UserID      string `json:"user_id"`
}

func TestSubscribersIntegration(t *testing.T) {
	testutil.SkipIfDisabled(t)

	defer testutil.ResetDefaultRegistryHealthCheck()
	defer testutil.EnableDebugForMethod()()

	a := assert.New(t)

	s, cleanup := serviceSetUp(t)
	defer cleanup()

	subscribeMultipleClients(t, s, 4)
	a.Nil(nil)

	restClient := restclient.New(fmt.Sprintf("http://%s/api", s.WebServer().GetAddr()))
	content, err := restClient.GetSubscribers(testTopic)
	a.NoError(err)
	routeParams := make([]*Subscriber, 0)

	err = json.Unmarshal(content, &routeParams)
	a.Equal(4, len(routeParams), "Should have 4 subscribers")
	for i, rp := range routeParams {
		a.Equal(fmt.Sprintf("gcmId%d", i), rp.DeviceToken)
		a.Equal(fmt.Sprintf("user%d", i), rp.UserID)
	}
	a.NoError(err)
}

func subscribeMultipleClients(t *testing.T, service *service.Service, noOfClients int) {
	a := assert.New(t)

	// create FCM subscription for topic
	for i := 0; i < noOfClients; i++ {
		urlFormat := fmt.Sprintf("http://%s/fcm/gcmId%%d/user%%d/%%s", service.WebServer().GetAddr())
		url := fmt.Sprintf(urlFormat, i, i, strings.TrimPrefix(testTopic, "/"))
		response, errPost := http.Post(
			url,
			"text/plain",
			bytes.NewBufferString(""),
		)
		logger.WithField("url", url).Debug("subscribe")
		a.NoError(errPost)
		a.Equal(response.StatusCode, 200)

		body, errReadAll := ioutil.ReadAll(response.Body)
		a.NoError(errReadAll)
		a.Equal(fmt.Sprintf(`{"subscribed":"%s"}`, testTopic), string(body))
	}
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Cornelius Buschka.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package config4go

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"unicode"
)

const (
	initial   = 0
	inKey     = 1
	postKey   = 2
	eqSeen    = 4
	inValue   = 3
	inComment = 5
	done      = 11
)

// ConfigParser is a parser capable to parse config streams.
type ConfigParser struct {
	state       int
	keyBuffer   bytes.Buffer
	valueBuffer bytes.Buffer
}

// Parse reads a config from reader in into map dest. Returns error
// in case of error.
func (configParser *ConfigParser) Parse(in *bufio.Reader) (map[string]string, error) {
	dest := make(map[string]string)

	configParser.state = initial

	for configParser.state != done {
		rune, _, err := in.ReadRune()
		if err != nil && err != io.EOF {
			return nil, err
		}

		switch configParser.state {
		case initial:
			if err == io.EOF {
				configParser.state = done
			} else if '#' == rune {
				configParser.state = inComment
			} else if unicode.IsSpace(rune) {
				// skip
			} else if unicode.IsLetter(rune) || '_' == rune {
				configParser.keyBuffer.WriteRune(rune)
				configParser.state = inKey
			} else {
				return nil, errors.New("Invalid input.")
			}
			break
		case inKey:
			if err == io.EOF {
				return nil, errors.New("Unexpected end of input.")
			} else if unicode.IsSpace(rune) {
				configParser.state = postKey
			} else if '=' == rune {
				configParser.state = eqSeen
			} else if unicode.IsDigit(rune) || unicode.IsLetter(rune) || '_' == rune {
				configParser.keyBuffer.WriteRune(rune)
			} else {
				return nil, errors.New("Invalid input.")
			}
			break
		case postKey:
			if err == io.EOF {
				return nil, errors.New("Unexpected end of input.")
			} else if unicode.IsSpace(rune) {
				// skip
			} else if '=' == rune {
				configParser.state = eqSeen
			} else {
				return nil, errors.New("Invalid input.")
			}
			break
		case eqSeen:
			if err == io.EOF {
				key := configParser.keyBuffer.String()
				value := configParser.valueBuffer.String()
				dest[key] = value

				configParser.state = done
			} else if unicode.IsSpace(rune) {
				// skip
			} else if '\n' == rune {
				key := configParser.keyBuffer.String()
				value := configParser.valueBuffer.String()
				dest[key] = value

				configParser.keyBuffer.Truncate(0)
				configParser.valueBuffer.Truncate(0)

				configParser.state = initial
			} else {
				configParser.valueBuffer.WriteRune(rune)
			}
			break
		case inValue:
			if err == io.EOF {
				key := configParser.keyBuffer.String()
				value := configParser.valueBuffer.String()
				dest[key] = value

				configParser.state = done
			} else if '\n' == rune {
				key := configParser.keyBuffer.String()
				value := configParser.valueBuffer.String()
				dest[key] = value

				configParser.keyBuffer.Truncate(0)
				configParser.valueBuffer.Truncate(0)

				configParser.state = initial
			} else {
				configParser.valueBuffer.WriteRune(rune)
			}
			break
		case inComment:
			if '\n' == rune {
				configParser.state = inKey
			}
			break
		case done:
			return nil, errors.New("Invalid state.")
		default:
			return nil, errors.New("Invalid input.")
		}
	}

	return dest, nil
}
<commit_msg>Split up parser into smaller functions.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Cornelius Buschka.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package config4go

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"unicode"
)

const (
	initial   = 0
	inKey     = 1
	postKey   = 2
	eqSeen    = 4
	inValue   = 3
	inComment = 5
	done      = 11
)

// ConfigParser is a parser capable to parse config streams.
type ConfigParser struct {
	state       int
	keyBuffer   bytes.Buffer
	valueBuffer bytes.Buffer
	dest        map[string]string
}

func (configParser *ConfigParser) reset() {
	configParser.dest = make(map[string]string)
	configParser.state = initial
}

// Parse reads a config from reader in into map dest. Returns error
// in case of error.
func (configParser *ConfigParser) Parse(in *bufio.Reader) (map[string]string, error) {
	configParser.reset()

	for configParser.state != done {
		symbol, _, err := in.ReadRune()
		if err != nil && err != io.EOF {
			return nil, err
		}

		if err := configParser.handleInput(symbol, err); err != nil {
			return nil, err
		}
	}

	return configParser.dest, nil
}

func (configParser *ConfigParser) handleInput(symbol rune, err error) error {
	switch configParser.state {
	case initial:
		return configParser.handleInitial(symbol, err)
	case inKey:
		return configParser.handleInKey(symbol, err)
	case postKey:
		return configParser.handlePostKey(symbol, err)
	case eqSeen:
		return configParser.handleEqSeen(symbol, err)
	case inValue:
		return configParser.handleInValue(symbol, err)
	case inComment:
		return configParser.handleInValue(symbol, err)
	case done:
		return errors.New("Invalid state.")
	default:
		return errors.New("Invalid input.")
	}

	return errors.New("Invalid state.")
}

func (configParser *ConfigParser) handleInitial(symbol rune, err error) error {
	if err == io.EOF {
		configParser.state = done
	} else if '#' == symbol {
		configParser.state = inComment
	} else if unicode.IsSpace(symbol) {
		// skip
	} else if unicode.IsLetter(symbol) || '_' == symbol {
		configParser.keyBuffer.WriteRune(symbol)
		configParser.state = inKey
	} else {
		return errors.New("Invalid input.")
	}

	return nil
}

func (configParser *ConfigParser) handleInKey(symbol rune, err error) error {
	if err == io.EOF {
		return errors.New("Unexpected end of input.")
	} else if unicode.IsSpace(symbol) {
		configParser.state = postKey
	} else if '=' == symbol {
		configParser.state = eqSeen
	} else if unicode.IsDigit(symbol) || unicode.IsLetter(symbol) || '_' == symbol {
		configParser.keyBuffer.WriteRune(symbol)
	} else {
		return errors.New("Invalid input.")
	}

	return nil
}

func (configParser *ConfigParser) handlePostKey(symbol rune, err error) error {

	if err == io.EOF {
		return errors.New("Unexpected end of input.")
	} else if unicode.IsSpace(symbol) {
		// skip
	} else if '=' == symbol {
		configParser.state = eqSeen
	} else {
		return errors.New("Invalid input.")
	}
	return nil
}

func (configParser *ConfigParser) handleEqSeen(symbol rune, err error) error {
	if err == io.EOF {
		configParser.addKeyValue()
		configParser.state = done
	} else if unicode.IsSpace(symbol) {
		// skip
	} else if '\n' == symbol {
		configParser.addKeyValue()
		configParser.state = initial
	} else {
		configParser.valueBuffer.WriteRune(symbol)
	}

	return nil
}

func (configParser *ConfigParser) handleInValue(symbol rune, err error) error {
	if err == io.EOF {
		configParser.addKeyValue()
		configParser.state = done
	} else if '\n' == symbol {
		configParser.addKeyValue()
		configParser.state = initial
	} else {
		configParser.valueBuffer.WriteRune(symbol)
	}

	return nil
}

func (configParser *ConfigParser) handleInComment(symbol rune, err error) error {
	if '\n' == symbol {
		configParser.state = inKey
	} else {
		// ok
	}

	return nil
}

func (configParser *ConfigParser) addKeyValue() {

	key := configParser.keyBuffer.String()
	value := configParser.valueBuffer.String()
	if key != "" {
		configParser.dest[key] = value
	}
	configParser.keyBuffer.Truncate(0)
	configParser.valueBuffer.Truncate(0)
}
<|endoftext|>


<commit_before>// dupes finds duplicate files in the given root directory
package main

import (
	"bytes"
	"crypto/sha256"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
)

const (
	globDefault = "*"
)

var (
	paranoid    = flag.Bool("p", false, "paranoid byte-by-byte file comparison")
	minimumSize = flag.Int64("s", 1, "minimum size (in bytes) of files to consider")
	globbing    = flag.String("g", globDefault, "glob expression for files to consider")
)

// hashes maps from digests to paths
var hashes = make(map[string]string)

// sizes maps from sizes to paths
var sizes = make(map[int64]string)

// final maps from paths to duplicate paths (collates all dupes)
var final = make(map[string][]string)

// files counts the number of files examined
var files counter

// dupes counts the number of duplicate files
var dupes counter

// wasted counts the space (in bytes) occupied by duplicates
var wasted bytesize

// fileContentsMatch does a byte-by-byte comparison of the files with the
// given paths
func fileContentsMatch(pa, pb string) (bool, error) {
	a, err := os.Open(pa)
	if err != nil {
		return false, err
	}
	defer a.Close()
	b, err := os.Open(pb)
	if err != nil {
		return false, err
	}
	defer b.Close()

	return fileContentsHelper(a, b)
}

func fileContentsHelper(a, b io.Reader) (bool, error) {
	bufferSize := os.Getpagesize()

	ba := make([]byte, bufferSize)
	bb := make([]byte, bufferSize)

	for {
		la, erra := a.Read(ba)
		lb, errb := b.Read(bb)

		// specification of Read() says to check returned size
		// before considering errors; who are we to disagree?
		if la > 0 || lb > 0 {
			// it's okay to use Equal here because whatever may
			// be left behind in the buffers after a short read
			// had to be Equal in the prior iteration; note that
			// we don't have to check la == lb either because if
			// they were not, Equal should fail
			if !bytes.Equal(ba, bb) {
				return false, nil
			}
		}

		// specification of Read() says that sooner or later we'll
		// see io.EOF regardless of returned size; only if both
		// files end in the same iteration (and made it past Equal
		// above) do we have a duplicate
		switch {
		case erra == io.EOF && errb == io.EOF:
			return true, nil
		case erra != nil:
			return false, erra
		case errb != nil:
			return false, errb
		}
	}
}

// checksum calculates a hash digest for the file with the given path
func checksum(path string) (string, error) {
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hasher := sha256.New()
	io.Copy(hasher, file)
	sum := fmt.Sprintf("%x", hasher.Sum(nil))

	return sum, nil
}

// check is called for each path we walk. It only examines regular, non-empty
// files. It first rules out duplicates by file size; for files that remain
// it calculates a checksum; if it has seen the same checksum before, it
// signals a duplicate; otherwise it remembers the checksum and the path of
// the original file before moving on; in paranoid mode it follows up with a
// byte-by-byte file comparison.
func check(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	size := info.Size()

	if !info.Mode().IsRegular() || size < *minimumSize {
		return nil
	}

	if *globbing != globDefault {
		matched, err := filepath.Match(*globbing, info.Name())
		if err != nil {
			return err
		}
		if !matched {
			return nil
		}
	}

	files++

	var dupe string
	var ok bool
	if dupe, ok = sizes[size]; !ok {
		sizes[size] = path
		return nil
	}

	// backpatch new file into hashes
	sum, err := checksum(dupe)
	if err != nil {
		return err
	}
	hashes[sum] = dupe

	sum, err = checksum(path)
	if err != nil {
		return err
	}

	if dupe, ok = hashes[sum]; !ok {
		hashes[sum] = path
		return nil
	}

	if *paranoid {
		same, err := fileContentsMatch(path, dupe)
		if err != nil {
			return err
		}
		if !same {
			fmt.Printf("cool: %s sha256-collides with %s!\n", path, dupe)
			return nil
		}
	}

	dupes++
	wasted += bytesize(size)

	final[dupe] = append(final[dupe], path)

	return nil
}

func sortedDupes() []string {
	var sk []string
	for k := range final {
		sk = append(sk, k)
	}
	sort.Strings(sk)
	return sk
}

func main() {
	flag.Usage = func() {
		var program = os.Args[0]
		fmt.Fprintf(os.Stderr, "Usage: %s [option...] directory...\n", program)
		flag.PrintDefaults()
	}

	flag.Parse()
	if len(flag.Args()) < 1 {
		flag.Usage()
	}

	_, err := filepath.Match(*globbing, "checking pattern syntax")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: invalid pattern for -g (%v)\n", err)
		os.Exit(1)
	}

	for _, root := range flag.Args() {
		filepath.Walk(root, check)
	}

	sk := sortedDupes()
	for _, k := range sk {
		vs := final[k]
		fmt.Println(k)
		for _, v := range vs {
			fmt.Println(v)
		}
		fmt.Println()
	}

	if len(sizes) > 0 || len(hashes) > 0 {
		fmt.Printf("%v files examined, %v duplicates found, %v wasted\n", files, dupes, wasted)
	}
}
<commit_msg>Just some layout changes.<commit_after>// Command dupes finds duplicate files in the given root directory
package main

import (
	"bytes"
	"crypto/sha256"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
)

const (
	globDefault = "*"
)

var (
	paranoid    = flag.Bool("p", false, "paranoid byte-by-byte file comparison")
	minimumSize = flag.Int64("s", 1, "minimum size (in bytes) of files to consider")
	globbing    = flag.String("g", globDefault, "glob expression for files to consider")
)

var (
	hashes = make(map[string]string)   // maps from digests to paths
	sizes  = make(map[int64]string)    // maps from sizes to paths
	final  = make(map[string][]string) // maps from paths to duplicate paths (collates all dupes)

	files  counter  // number of files examined
	dupes  counter  // number of duplicate files
	wasted bytesize // space (in bytes) occupied by duplicates
)

// fileContentsMatch does a byte-by-byte comparison of the files with the
// given paths
func fileContentsMatch(pa, pb string) (bool, error) {
	a, err := os.Open(pa)
	if err != nil {
		return false, err
	}
	defer a.Close()
	b, err := os.Open(pb)
	if err != nil {
		return false, err
	}
	defer b.Close()

	return fileContentsHelper(a, b)
}

func fileContentsHelper(a, b io.Reader) (bool, error) {
	bufferSize := os.Getpagesize()

	ba := make([]byte, bufferSize)
	bb := make([]byte, bufferSize)

	for {
		la, erra := a.Read(ba)
		lb, errb := b.Read(bb)

		// specification of Read() says to check returned size
		// before considering errors; who are we to disagree?
		if la > 0 || lb > 0 {
			// it's okay to use Equal here because whatever may
			// be left behind in the buffers after a short read
			// had to be Equal in the prior iteration; note that
			// we don't have to check la == lb either because if
			// they were not, Equal should fail
			if !bytes.Equal(ba, bb) {
				return false, nil
			}
		}

		// specification of Read() says that sooner or later we'll
		// see io.EOF regardless of returned size; only if both
		// files end in the same iteration (and made it past Equal
		// above) do we have a duplicate
		switch {
		case erra == io.EOF && errb == io.EOF:
			return true, nil
		case erra != nil:
			return false, erra
		case errb != nil:
			return false, errb
		}
	}
}

// checksum calculates a hash digest for the file with the given path
func checksum(path string) (string, error) {
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hasher := sha256.New()
	io.Copy(hasher, file)
	sum := fmt.Sprintf("%x", hasher.Sum(nil))

	return sum, nil
}

// check is called for each path we walk. It only examines regular, non-empty
// files. It first rules out duplicates by file size; for files that remain
// it calculates a checksum; if it has seen the same checksum before, it
// signals a duplicate; otherwise it remembers the checksum and the path of
// the original file before moving on; in paranoid mode it follows up with a
// byte-by-byte file comparison.
func check(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	size := info.Size()

	if !info.Mode().IsRegular() || size < *minimumSize {
		return nil
	}

	if *globbing != globDefault {
		matched, err := filepath.Match(*globbing, info.Name())
		if err != nil {
			return err
		}
		if !matched {
			return nil
		}
	}

	files++

	var dupe string
	var ok bool
	if dupe, ok = sizes[size]; !ok {
		sizes[size] = path
		return nil
	}

	// backpatch new file into hashes
	sum, err := checksum(dupe)
	if err != nil {
		return err
	}
	hashes[sum] = dupe

	sum, err = checksum(path)
	if err != nil {
		return err
	}

	if dupe, ok = hashes[sum]; !ok {
		hashes[sum] = path
		return nil
	}

	if *paranoid {
		same, err := fileContentsMatch(path, dupe)
		if err != nil {
			return err
		}
		if !same {
			fmt.Printf("cool: %s sha256-collides with %s!\n", path, dupe)
			return nil
		}
	}

	dupes++
	wasted += bytesize(size)

	final[dupe] = append(final[dupe], path)

	return nil
}

func sortedDupes() []string {
	var sk []string
	for k := range final {
		sk = append(sk, k)
	}
	sort.Strings(sk)
	return sk
}

func main() {
	flag.Usage = func() {
		var program = os.Args[0]
		fmt.Fprintf(os.Stderr, "Usage: %s [option...] directory...\n", program)
		flag.PrintDefaults()
	}

	flag.Parse()
	if len(flag.Args()) < 1 {
		flag.Usage()
	}

	_, err := filepath.Match(*globbing, "checking pattern syntax")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: invalid pattern for -g (%v)\n", err)
		os.Exit(1)
	}

	for _, root := range flag.Args() {
		filepath.Walk(root, check)
	}

	sk := sortedDupes()
	for _, k := range sk {
		vs := final[k]
		fmt.Println(k)
		for _, v := range vs {
			fmt.Println(v)
		}
		fmt.Println()
	}

	if len(sizes) > 0 || len(hashes) > 0 {
		fmt.Printf("%v files examined, %v duplicates found, %v wasted\n", files, dupes, wasted)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"koding/db/mongodb/modelhelper"
	"testing"
	"time"

	"gopkg.in/mgo.v2/bson"

	. "github.com/smartystreets/goconvey/convey"
)

func TestWarningsQuery(t *testing.T) {
	Convey("Given user who is inactive & not warned", t, func() {
		warning := VMDeletionWarning1

		user, err := createInactiveUser(21)
		So(err, ShouldBeNil)

		Convey("Then it should fetch the user", func() {
			count, err := modelhelper.CountUsersByQuery(warning.Select[0])
			So(err, ShouldBeNil)
			So(count, ShouldBeGreaterThan, 0)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})

	Convey("Given user who is inactive & warned", t, func() {
		warning := VMDeletionWarning2

		user, err := createInactiveUserWithWarning(25, warning.ID)
		So(err, ShouldBeNil)

		Convey("Then it should fetch the user", func() {
			newuser, err := modelhelper.GetUserByQuery(warning.Select[0])
			So(err, ShouldBeNil)

			So(newuser.ObjectId, ShouldEqual, user.ObjectId)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})

	Convey("Given user who is inactive & warned twice", t, func() {
		warning := DeleteInactiveUserVM

		user, err := createInactiveUserWithWarning(30, warning.ID)
		So(err, ShouldBeNil)

		Convey("Then it should fetch the user", func() {
			newuser, err := modelhelper.GetUserByQuery(warning.Select[0])
			So(err, ShouldBeNil)

			So(newuser.ObjectId, ShouldEqual, user.ObjectId)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})
}

func TestWarningsFull(t *testing.T) {
	Convey("Given user who is inactive & not warned", t, func() {
		user, err := createInactiveUser(21)
		So(err, ShouldBeNil)

		resetFakeEmails()

		warning := VMDeletionWarning1
		warning.ExemptCheckers = []*ExemptChecker{}
		warning.Action = fakeEmailActionFn()

		warning.Run()

		Convey("Then they should get an email", func() {
			So(len(fakeEmails), ShouldEqual, 1)
			So(fakeEmails[0], ShouldEqual, warning.ID)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})

	Convey("Given user who is inactive & been warned", t, func() {
		user, err := createInactiveUser(21)
		So(err, ShouldBeNil)

		resetFakeEmails()

		warning := VMDeletionWarning1
		warning.ExemptCheckers = []*ExemptChecker{}
		warning.Action = fakeEmailActionFn()
		warning.Run()

		So(len(fakeEmails), ShouldEqual, 1)
		So(fakeEmails[0], ShouldEqual, warning.ID)

		Convey("Then they should get an email", func() {
			user, err := modelhelper.GetUser(user.Name)
			So(err, ShouldBeNil)
			So(user.Inactive.Warning, ShouldEqual, warning.ID)

			Convey("Then they should get another email", func() {
				selector := bson.M{"username": user.Name}
				update := bson.M{
					"lastLoginDate": timeNow().Add(-time.Hour * 24 * time.Duration(25)),
				}

				err := modelhelper.UpdateUser(selector, update)
				So(err, ShouldBeNil)

				updateUserModifiedAt(user, yesterday())

				resetFakeEmails()

				warning := VMDeletionWarning2
				warning.ExemptCheckers = []*ExemptChecker{}
				warning.Action = fakeEmailActionFn()

				warning.Run()

				So(len(fakeEmails), ShouldEqual, 1)
				So(fakeEmails[0], ShouldEqual, warning.ID)

				user, err := modelhelper.GetUser(user.Name)
				So(err, ShouldBeNil)
				So(user.Inactive.Warning, ShouldEqual, warning.ID)
			})
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})
}
<commit_msg>go: added test case for warning of account deletion<commit_after>package main

import (
	"koding/db/models"
	"koding/db/mongodb/modelhelper"
	"testing"
	"time"

	"gopkg.in/mgo.v2/bson"

	. "github.com/smartystreets/goconvey/convey"
)

func TestWarningsQuery(t *testing.T) {
	Convey("Given user who is inactive & not warned", t, func() {
		warning := VMDeletionWarning1

		user, err := createInactiveUser(21)
		So(err, ShouldBeNil)

		Convey("Then it should fetch the user", func() {
			count, err := modelhelper.CountUsersByQuery(warning.Select[0])
			So(err, ShouldBeNil)
			So(count, ShouldBeGreaterThan, 0)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})

	Convey("Given user who is inactive & warned", t, func() {
		warning := VMDeletionWarning2

		user, err := createInactiveUserWithWarning(25, warning.ID)
		So(err, ShouldBeNil)

		Convey("Then it should fetch the user", func() {
			newuser, err := modelhelper.GetUserByQuery(warning.Select[0])
			So(err, ShouldBeNil)

			So(newuser.ObjectId, ShouldEqual, user.ObjectId)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})

	Convey("Given user who is inactive & warned twice", t, func() {
		warning := DeleteInactiveUserVM

		user, err := createInactiveUserWithWarning(30, warning.ID)
		So(err, ShouldBeNil)

		Convey("Then it should fetch the user", func() {
			newuser, err := modelhelper.GetUserByQuery(warning.Select[0])
			So(err, ShouldBeNil)

			So(newuser.ObjectId, ShouldEqual, user.ObjectId)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})
}

func TestWarningsFull(t *testing.T) {
	Convey("Given user who is inactive & not warned", t, func() {
		user, err := createInactiveUser(21)
		So(err, ShouldBeNil)

		resetFakeEmails()

		warning := VMDeletionWarning1
		warning.ExemptCheckers = []*ExemptChecker{}
		warning.Action = fakeEmailActionFn()

		warning.Run()

		Convey("Then they should get an email", func() {
			So(len(fakeEmails), ShouldEqual, 1)
			So(fakeEmails[0], ShouldEqual, warning.ID)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})

	Convey("Given user who is inactive & been warned", t, func() {
		user, err := createInactiveUser(21)
		So(err, ShouldBeNil)

		resetFakeEmails()

		warning := VMDeletionWarning1
		warning.ExemptCheckers = []*ExemptChecker{}
		warning.Action = fakeEmailActionFn()
		warning.Run()

		So(len(fakeEmails), ShouldEqual, 1)
		So(fakeEmails[0], ShouldEqual, warning.ID)

		Convey("Then they should get an email", func() {
			user, err := modelhelper.GetUser(user.Name)
			So(err, ShouldBeNil)
			So(user.Inactive.Warning, ShouldEqual, warning.ID)

			Convey("Then they should get another email", func() {
				selector := bson.M{"username": user.Name}
				update := bson.M{
					"lastLoginDate": timeNow().Add(-time.Hour * 24 * time.Duration(25)),
				}

				err := modelhelper.UpdateUser(selector, update)
				So(err, ShouldBeNil)

				updateUserModifiedAt(user, yesterday())

				resetFakeEmails()

				warning := VMDeletionWarning2
				warning.ExemptCheckers = []*ExemptChecker{}
				warning.Action = fakeEmailActionFn()

				warning.Run()

				So(len(fakeEmails), ShouldEqual, 1)
				So(fakeEmails[0], ShouldEqual, warning.ID)

				user, err := modelhelper.GetUser(user.Name)
				So(err, ShouldBeNil)
				So(user.Inactive.Warning, ShouldEqual, warning.ID)
			})
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})
}

func TestWarningsDeleteUser(t *testing.T) {

	warning := DeleteInactiveUsers
	warning.Throttled = false
	warning.ExemptCheckers = []*ExemptChecker{}

	Convey("Given user who is inactive for more than 45 days", t, func() {
		// if user's machines are deleted and 45 days has passed since last
		// login, delete user.
		mtime := time.Now().Add(-time.Hour * 24)
		user, err := createInactiveUserWithWarningAndModificationTime(46, DeleteInactiveUserVM.ID, mtime)
		So(err, ShouldBeNil)
		resetFakeEmails()

		var deletedUser *models.User
		warning.Action = func(user *models.User, _ string) error {
			deletedUser = user
			return nil
		}

		warning.Run()

		Convey("should deleted users be set", func() {
			So(deletedUser, ShouldNotBeNil)
			So(deletedUser.Email, ShouldEqual, user.Email)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})

	Convey("Given user who is inactive for LTE 45 days", t, func() {
		// if user's machines are deleted and 45 days has passed, delete user.
		mtime := time.Now().Add(-time.Hour * 24)
		user, err := createInactiveUserWithWarningAndModificationTime(45, DeleteInactiveUserVM.ID, mtime)
		So(err, ShouldBeNil)
		resetFakeEmails()

		var deletedUser *models.User
		warning.Action = func(user *models.User, _ string) error {
			deletedUser = user
			return nil
		}
		warning.Run()

		Convey("deletedUser should be empty", func() {
			So(deletedUser, ShouldBeNil)
		})

		Reset(func() {
			deleteUserWithUsername(user)
		})
	})
}
<|endoftext|>


<commit_before>package models

import (
	"socialapi/workers/common/runner"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelMessageCreate(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	Convey("while creating channel message", t, func() {
		Convey("0 length body could not be inserted ", func() {
			cm := NewChannelMessage()
			So(cm.Create(), ShouldNotBeNil)
			So(cm.Create().Error(), ShouldContainSubstring, "message body length should be greater than")
		})

		Convey("type constant should be given", func() {
			cm := createMessageWithTest()
			cm.TypeConstant = ""
			So(cm.Create(), ShouldNotBeNil)
			So(cm.Create().Error(), ShouldContainSubstring, "pq: invalid input value for enum channel_message_type_constant_enum: \"\"")
		})

		Convey("type constant can be post", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_POST

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_POST)
		})

		Convey("type constant can be reply", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_REPLY

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_REPLY)
		})
		Convey("type constant can be join", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_JOIN

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_JOIN)
		})

		Convey("type constant can be leave", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_LEAVE

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_LEAVE)
		})

		Convey("type constant can be privatemessage", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_PRIVATE_MESSAGE

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_PRIVATE_MESSAGE)
		})

		Convey("5 length body could be inserted ", func() {
			cm := createMessageWithTest()
			// set body
			cm.Body = "5five"

			// try to create the message
			So(cm.Create(), ShouldBeNil)
		})

		Convey("message should have slug after inserting ", func() {
			// create message
			cm := createMessageWithTest()
			// be sure that our message doesnt have slug
			So(cm.Id, ShouldBeZeroValue)
			So(cm.Slug, ShouldBeZeroValue)
			// try to create the message
			So(cm.Create(), ShouldBeNil)
			So(cm.Id, ShouldNotEqual, 0)

			// fetch created message
			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldNotEqual, 0)
			So(icm.Slug, ShouldNotEqual, "")
		})
	})
}

func TestChannelMessageUpdate(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	Convey("while updating channel message", t, func() {
		Convey("0 length body could not be updated ", func() {
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// clear message body
			cm.Body = ""
			So(cm.Update(), ShouldNotBeEmpty)
			So(cm.Update().Error(), ShouldContainSubstring, "message body length should be greater than")
		})

		Convey("valid length body can  be updated ", func() {
			// create message in the db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// update the db
			body := "my updated text"

			// clear message body
			cm.Body = body
			So(cm.Update(), ShouldBeEmpty)

			// fetch createed/updated message from db
			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Body, ShouldEqual, body)
		})

		Convey("valid length body can  be updated ", func() {
			// create message in the db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// update the db
			body := "my updated text"

			// clear message body
			cm.Body = body
			So(cm.Update(), ShouldBeEmpty)

			// fetch createed/updated message from db
			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Body, ShouldEqual, body)
		})

		Convey("meta bits can not be updated", nil)

		Convey("token can not  be updated ", func() {
			// create message in the db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// update the db
			token := NewToken(time.Now()).String()

			// clear message body
			cm.Token = token

			err := cm.Update()
			So(err, ShouldBeNil)

			// fetch created/updated message from db
			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Token, ShouldNotEqual, token)
		})

		Convey("slug can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Slug, ShouldBeZeroValue)
			So(cm.Create(), ShouldBeNil)
			So(cm.Slug, ShouldNotEqual, "")

			// invoke a new slug
			slug := "another-test-for-slug"
			cm.Slug = slug

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Slug, ShouldNotEqual, slug)
		})

		Convey("typeConstant can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.TypeConstant, ShouldNotEqual, "")

			cm.TypeConstant = ChannelMessage_TYPE_JOIN

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.TypeConstant, ShouldNotEqual, ChannelMessage_TYPE_JOIN)
		})

		Convey("AccountId can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.AccountId, ShouldNotEqual, 0)

			accId := cm.AccountId + 1
			cm.AccountId = accId

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.AccountId, ShouldNotEqual, accId)
		})

		Convey("InitialChannelId can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.InitialChannelId, ShouldNotEqual, 0)

			cId := cm.InitialChannelId + 1
			cm.InitialChannelId = cId

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.InitialChannelId, ShouldNotEqual, cId)
		})

		Convey("CreatedAt can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.CreatedAt, ShouldNotBeEmpty)

			timeNow := time.Now()
			cm.CreatedAt = timeNow

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.CreatedAt, ShouldNotEqual, timeNow)
		})
	})
}

func TestChannelMessageGetTableName(t *testing.T) {
	Convey("while testing TableName()", t, func() {
		So(NewChannelMessage().TableName(), ShouldEqual, ChannelMessageTableName)
	})
}
<commit_msg>Wercker: Change "type constant should be given" assertion in TestChannelMessageCreate test<commit_after>package models

import (
	"socialapi/workers/common/runner"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelMessageCreate(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	Convey("while creating channel message", t, func() {
		Convey("0 length body could not be inserted ", func() {
			cm := NewChannelMessage()
			So(cm.Create(), ShouldNotBeNil)
			So(cm.Create().Error(), ShouldContainSubstring, "message body length should be greater than")
		})

		Convey("type constant should be given", func() {
			cm := createMessageWithTest()
			cm.TypeConstant = ""
			So(cm.Create(), ShouldNotBeNil)
			// complete error message is this: pq: invalid input value for enum channel_message_type_constant_enum: \"\"
			// it is trimmed because wercker add schema name and returns api.channel_message_type_constant_enum
			So(cm.Create().Error(), ShouldContainSubstring, "pq: invalid input value for enum")
		})

		Convey("type constant can be post", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_POST

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_POST)
		})

		Convey("type constant can be reply", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_REPLY

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_REPLY)
		})
		Convey("type constant can be join", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_JOIN

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_JOIN)
		})

		Convey("type constant can be leave", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_LEAVE

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_LEAVE)
		})

		Convey("type constant can be privatemessage", func() {
			cm := createMessageWithTest()
			// remove type Constant
			cm.TypeConstant = ChannelMessage_TYPE_PRIVATE_MESSAGE

			So(cm.Create(), ShouldBeNil)

			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldEqual, cm.Id)
			So(icm.TypeConstant, ShouldEqual, ChannelMessage_TYPE_PRIVATE_MESSAGE)
		})

		Convey("5 length body could be inserted ", func() {
			cm := createMessageWithTest()
			// set body
			cm.Body = "5five"

			// try to create the message
			So(cm.Create(), ShouldBeNil)
		})

		Convey("message should have slug after inserting ", func() {
			// create message
			cm := createMessageWithTest()
			// be sure that our message doesnt have slug
			So(cm.Id, ShouldBeZeroValue)
			So(cm.Slug, ShouldBeZeroValue)
			// try to create the message
			So(cm.Create(), ShouldBeNil)
			So(cm.Id, ShouldNotEqual, 0)

			// fetch created message
			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Id, ShouldNotEqual, 0)
			So(icm.Slug, ShouldNotEqual, "")
		})
	})
}

func TestChannelMessageUpdate(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	Convey("while updating channel message", t, func() {
		Convey("0 length body could not be updated ", func() {
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// clear message body
			cm.Body = ""
			So(cm.Update(), ShouldNotBeEmpty)
			So(cm.Update().Error(), ShouldContainSubstring, "message body length should be greater than")
		})

		Convey("valid length body can  be updated ", func() {
			// create message in the db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// update the db
			body := "my updated text"

			// clear message body
			cm.Body = body
			So(cm.Update(), ShouldBeEmpty)

			// fetch createed/updated message from db
			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Body, ShouldEqual, body)
		})

		Convey("valid length body can  be updated ", func() {
			// create message in the db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// update the db
			body := "my updated text"

			// clear message body
			cm.Body = body
			So(cm.Update(), ShouldBeEmpty)

			// fetch createed/updated message from db
			icm := NewChannelMessage()
			err := icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Body, ShouldEqual, body)
		})

		Convey("meta bits can not be updated", nil)

		Convey("token can not  be updated ", func() {
			// create message in the db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)

			// update the db
			token := NewToken(time.Now()).String()

			// clear message body
			cm.Token = token

			err := cm.Update()
			So(err, ShouldBeNil)

			// fetch created/updated message from db
			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Token, ShouldNotEqual, token)
		})

		Convey("slug can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Slug, ShouldBeZeroValue)
			So(cm.Create(), ShouldBeNil)
			So(cm.Slug, ShouldNotEqual, "")

			// invoke a new slug
			slug := "another-test-for-slug"
			cm.Slug = slug

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.Slug, ShouldNotEqual, slug)
		})

		Convey("typeConstant can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.TypeConstant, ShouldNotEqual, "")

			cm.TypeConstant = ChannelMessage_TYPE_JOIN

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.TypeConstant, ShouldNotEqual, ChannelMessage_TYPE_JOIN)
		})

		Convey("AccountId can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.AccountId, ShouldNotEqual, 0)

			accId := cm.AccountId + 1
			cm.AccountId = accId

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.AccountId, ShouldNotEqual, accId)
		})

		Convey("InitialChannelId can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.InitialChannelId, ShouldNotEqual, 0)

			cId := cm.InitialChannelId + 1
			cm.InitialChannelId = cId

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.InitialChannelId, ShouldNotEqual, cId)
		})

		Convey("CreatedAt can not be updated", func() {
			// message is created in db
			cm := createMessageWithTest()
			So(cm.Create(), ShouldBeNil)
			So(cm.CreatedAt, ShouldNotBeEmpty)

			timeNow := time.Now()
			cm.CreatedAt = timeNow

			// update the message
			// updating channel message will assign defaults
			err := cm.Update()
			So(err, ShouldBeNil)

			icm := NewChannelMessage()
			err = icm.ById(cm.Id)
			So(err, ShouldBeNil)
			So(icm.CreatedAt, ShouldNotEqual, timeNow)
		})
	})
}

func TestChannelMessageGetTableName(t *testing.T) {
	Convey("while testing TableName()", t, func() {
		So(NewChannelMessage().TableName(), ShouldEqual, ChannelMessageTableName)
	})
}
<|endoftext|>