<commit_before>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"math"
	"math/rand"
	"sort"
	"strings"
)

type MirrorSelection interface {
	// Selection must return an ordered list of selected mirror,
	// a list of rejected mirrors and and an error code.
	Selection(*Context, *Cache, *FileInfo, GeoIPRec) (Mirrors, Mirrors, error)
}

// DefaultEngine is the default algorithm used for mirror selection
type DefaultEngine struct{}

func (h DefaultEngine) Selection(ctx *Context, cache *Cache, fileInfo *FileInfo, clientInfo GeoIPRec) (mirrors Mirrors, excluded Mirrors, err error) {
	// Get details about the requested file
	*fileInfo, err = cache.GetFileInfo(fileInfo.Path)
	if err != nil {
		return
	}

	// Prepare and return the list of all potential mirrors
	mirrors, err = cache.GetMirrors(fileInfo.Path, clientInfo)
	if err != nil {
		return
	}

	// Filter
	safeIndex := 0
	excluded = make([]Mirror, 0, len(mirrors))
	var closestMirror float32
	var farthestMirror float32
	for i, m := range mirrors {
		// Does it support http? Is it well formated?
		if !strings.HasPrefix(m.HttpURL, "http://") {
			m.ExcludeReason = "Invalid URL"
			goto delete
		}
		// Is it enabled?
		if !m.Enabled {
			if m.ExcludeReason == "" {
				m.ExcludeReason = "Disabled"
			}
			goto delete
		}
		// Is it up?
		if !m.Up {
			if m.ExcludeReason == "" {
				m.ExcludeReason = "Down"
			}
			goto delete
		}
		// Is it the same size as source?
		if m.FileInfo != nil {
			if m.FileInfo.Size != fileInfo.Size {
				m.ExcludeReason = "File size mismatch"
				goto delete
			}
		}
		// Is it configured to serve its continent only?
		if m.ContinentOnly {
			if !clientInfo.isValid() || clientInfo.ContinentCode != m.ContinentCode {
				m.ExcludeReason = "Continent only"
				goto delete
			}
		}
		// Is it configured to serve its country only?
		if m.CountryOnly {
			if !clientInfo.isValid() || !isInSlice(clientInfo.CountryCode, m.CountryFields) {
				m.ExcludeReason = "Country only"
				goto delete
			}
		}
		// Is it in the same AS number?
		if m.ASOnly {
			if !clientInfo.isValid() || clientInfo.ASNum != m.Asnum {
				m.ExcludeReason = "AS only"
				goto delete
			}
		}
		if safeIndex == 0 {
			closestMirror = m.Distance
		} else if closestMirror > m.Distance {
			closestMirror = m.Distance
		}
		if m.Distance > farthestMirror {
			farthestMirror = m.Distance
		}
		mirrors[safeIndex] = mirrors[i]
		safeIndex++
		continue
	delete:
		excluded = append(excluded, m)
	}

	// Reduce the slice to its new size
	mirrors = mirrors[:safeIndex]

	if !clientInfo.isValid() {
		// Shuffle the list
		//XXX Should we use the fallbacks instead?
		for i := range mirrors {
			j := rand.Intn(i + 1)
			mirrors[i], mirrors[j] = mirrors[j], mirrors[i]
		}

		// Shortcut
		if !ctx.IsMirrorlist() {
			// Reduce the number of mirrors to process
			mirrors = mirrors[:min(5, len(mirrors))]
		}
		return
	}

	// We're not interested in divisions by zero
	if closestMirror == 0 {
		closestMirror = math.SmallestNonzeroFloat32
	}

	/* Weight distribution for random selection [Probabilistic weight] */

	// Compute score for each mirror and return the mirrors eligible for weight distribution.
	// This includes:
	// - mirrors found in a 1.5x (configurable) range from the closest mirror
	// - mirrors targeting the given country (as primary or secondary)
	// - mirrors being in the same AS number
	totalScore := 0
	baseScore := int(farthestMirror)
	weights := map[string]int{}
	for i := 0; i < len(mirrors); i++ {
		m := &mirrors[i]

		m.ComputedScore = baseScore - int(m.Distance)

		if m.Distance <= closestMirror*GetConfig().WeightDistributionRange {
			m.ComputedScore += int(float32(baseScore) - ((m.Distance / closestMirror) * closestMirror))
		} else if isPrimaryCountry(clientInfo, m.CountryFields) {
			m.ComputedScore += max(0, int(float32(baseScore)-((m.Distance/closestMirror)*closestMirror))) / 2
		} else if isAdditionalCountry(clientInfo, m.CountryFields) {
			m.ComputedScore += int(float32(baseScore) - closestMirror)
		}

		if m.Asnum == clientInfo.ASNum {
			m.ComputedScore += baseScore / 2
		}

		m.ComputedScore += int(math.Max(float64(m.ComputedScore)*(float64(m.Score)/100)+0.5, 1))

		if m.ComputedScore >= baseScore {
			totalScore += m.ComputedScore - baseScore
			weights[m.ID] = m.ComputedScore - baseScore
		}
	}

	// Get the final number of mirrors selected for weight distribution
	selected := len(weights)

	// Sort mirrors by computed score
	sort.Sort(ByComputedScore{mirrors})

	// If mirrorlist is not requested we can discard most mirrors to
	// improve the processing speed.
	if !ctx.IsMirrorlist() {
		// Reduce the number of mirrors to process
		v := math.Min(math.Max(5, float64(selected)), float64(len(mirrors)))
		mirrors = mirrors[:int(v)]
	}

	if selected > 1 {
		// Randomize the order of the selected mirrors considering their weights
		weightedMirrors := make([]Mirror, selected)
		rest := totalScore
		for i := 0; i < selected; i++ {
			var id string
			rv := rand.Int31n(int32(rest))
			s := 0
			for k, v := range weights {
				s += v
				if int32(s) > rv {
					id = k
					break
				}
			}
			for _, m := range mirrors {
				if m.ID == id {
					m.Weight = int(float64(weights[id])*100/float64(totalScore) + 0.5)
					weightedMirrors[i] = m
					break
				}
			}
			rest -= weights[id]
			delete(weights, id)
		}

		// Replace the head of the list by its reordered counterpart
		mirrors = append(weightedMirrors, mirrors[selected:]...)
	} else if selected == 1 && len(mirrors) > 0 {
		mirrors[0].Weight = 100
	}
	return
}
<commit_msg>Fix a possible panic in the weight randomization algorithm<commit_after>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"math"
	"math/rand"
	"sort"
	"strings"
)

type MirrorSelection interface {
	// Selection must return an ordered list of selected mirror,
	// a list of rejected mirrors and and an error code.
	Selection(*Context, *Cache, *FileInfo, GeoIPRec) (Mirrors, Mirrors, error)
}

// DefaultEngine is the default algorithm used for mirror selection
type DefaultEngine struct{}

func (h DefaultEngine) Selection(ctx *Context, cache *Cache, fileInfo *FileInfo, clientInfo GeoIPRec) (mirrors Mirrors, excluded Mirrors, err error) {
	// Get details about the requested file
	*fileInfo, err = cache.GetFileInfo(fileInfo.Path)
	if err != nil {
		return
	}

	// Prepare and return the list of all potential mirrors
	mirrors, err = cache.GetMirrors(fileInfo.Path, clientInfo)
	if err != nil {
		return
	}

	// Filter
	safeIndex := 0
	excluded = make([]Mirror, 0, len(mirrors))
	var closestMirror float32
	var farthestMirror float32
	for i, m := range mirrors {
		// Does it support http? Is it well formated?
		if !strings.HasPrefix(m.HttpURL, "http://") {
			m.ExcludeReason = "Invalid URL"
			goto delete
		}
		// Is it enabled?
		if !m.Enabled {
			if m.ExcludeReason == "" {
				m.ExcludeReason = "Disabled"
			}
			goto delete
		}
		// Is it up?
		if !m.Up {
			if m.ExcludeReason == "" {
				m.ExcludeReason = "Down"
			}
			goto delete
		}
		// Is it the same size as source?
		if m.FileInfo != nil {
			if m.FileInfo.Size != fileInfo.Size {
				m.ExcludeReason = "File size mismatch"
				goto delete
			}
		}
		// Is it configured to serve its continent only?
		if m.ContinentOnly {
			if !clientInfo.isValid() || clientInfo.ContinentCode != m.ContinentCode {
				m.ExcludeReason = "Continent only"
				goto delete
			}
		}
		// Is it configured to serve its country only?
		if m.CountryOnly {
			if !clientInfo.isValid() || !isInSlice(clientInfo.CountryCode, m.CountryFields) {
				m.ExcludeReason = "Country only"
				goto delete
			}
		}
		// Is it in the same AS number?
		if m.ASOnly {
			if !clientInfo.isValid() || clientInfo.ASNum != m.Asnum {
				m.ExcludeReason = "AS only"
				goto delete
			}
		}
		if safeIndex == 0 {
			closestMirror = m.Distance
		} else if closestMirror > m.Distance {
			closestMirror = m.Distance
		}
		if m.Distance > farthestMirror {
			farthestMirror = m.Distance
		}
		mirrors[safeIndex] = mirrors[i]
		safeIndex++
		continue
	delete:
		excluded = append(excluded, m)
	}

	// Reduce the slice to its new size
	mirrors = mirrors[:safeIndex]

	if !clientInfo.isValid() {
		// Shuffle the list
		//XXX Should we use the fallbacks instead?
		for i := range mirrors {
			j := rand.Intn(i + 1)
			mirrors[i], mirrors[j] = mirrors[j], mirrors[i]
		}

		// Shortcut
		if !ctx.IsMirrorlist() {
			// Reduce the number of mirrors to process
			mirrors = mirrors[:min(5, len(mirrors))]
		}
		return
	}

	// We're not interested in divisions by zero
	if closestMirror == 0 {
		closestMirror = math.SmallestNonzeroFloat32
	}

	/* Weight distribution for random selection [Probabilistic weight] */

	// Compute score for each mirror and return the mirrors eligible for weight distribution.
	// This includes:
	// - mirrors found in a 1.5x (configurable) range from the closest mirror
	// - mirrors targeting the given country (as primary or secondary)
	// - mirrors being in the same AS number
	totalScore := 0
	baseScore := int(farthestMirror)
	weights := map[string]int{}
	for i := 0; i < len(mirrors); i++ {
		m := &mirrors[i]

		m.ComputedScore = baseScore - int(m.Distance) + 1

		if m.Distance <= closestMirror*GetConfig().WeightDistributionRange {
			m.ComputedScore += int(float32(baseScore) - ((m.Distance / closestMirror) * closestMirror))
		} else if isPrimaryCountry(clientInfo, m.CountryFields) {
			m.ComputedScore += max(0, int(float32(baseScore)-((m.Distance/closestMirror)*closestMirror))) / 2
		} else if isAdditionalCountry(clientInfo, m.CountryFields) {
			m.ComputedScore += int(float32(baseScore) - closestMirror)
		}

		if m.Asnum == clientInfo.ASNum {
			m.ComputedScore += baseScore / 2
		}

		m.ComputedScore += int(math.Max(float64(m.ComputedScore)*(float64(m.Score)/100)+0.5, 1))

		if m.ComputedScore > baseScore {
			// The weight must always be > 0 to not break the randomization below
			totalScore += m.ComputedScore - baseScore
			weights[m.ID] = m.ComputedScore - baseScore
		}
	}

	// Get the final number of mirrors selected for weight distribution
	selected := len(weights)

	// Sort mirrors by computed score
	sort.Sort(ByComputedScore{mirrors})

	// If mirrorlist is not requested we can discard most mirrors to
	// improve the processing speed.
	if !ctx.IsMirrorlist() {
		// Reduce the number of mirrors to process
		v := math.Min(math.Max(5, float64(selected)), float64(len(mirrors)))
		mirrors = mirrors[:int(v)]
	}

	if selected > 1 {
		// Randomize the order of the selected mirrors considering their weights
		weightedMirrors := make([]Mirror, selected)
		rest := totalScore
		for i := 0; i < selected; i++ {
			var id string
			rv := rand.Int31n(int32(rest))
			s := 0
			for k, v := range weights {
				s += v
				if int32(s) > rv {
					id = k
					break
				}
			}
			for _, m := range mirrors {
				if m.ID == id {
					m.Weight = int(float64(weights[id])*100/float64(totalScore) + 0.5)
					weightedMirrors[i] = m
					break
				}
			}
			rest -= weights[id]
			delete(weights, id)
		}

		// Replace the head of the list by its reordered counterpart
		mirrors = append(weightedMirrors, mirrors[selected:]...)
	} else if selected == 1 && len(mirrors) > 0 {
		mirrors[0].Weight = 100
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 OctoLab. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

// Package semaphore provides an implementation of Semaphore pattern
// with a timeout of lock/unlock operations based on channels.
package semaphore // import "github.com/kamilsk/semaphore"

import "errors"

// HealthChecker defines helpful methods related with semaphore status.
type HealthChecker interface {
	// Capacity returns a capacity of a semaphore.
	// It must be safe to call Capacity concurrently on a single semaphore.
	Capacity() int
	// Occupied returns a current number of occupied slots.
	// It must be safe to call Occupied concurrently on a single semaphore.
	Occupied() int
}

// Releaser defines a method to release the previously occupied semaphore.
type Releaser interface {
	// Release releases the previously occupied slot.
	// If no places were occupied, then it returns an appropriate error.
	// It must be safe to call Release concurrently on a single semaphore.
	Release() error
}

// ReleaseFunc tells a semaphore to release the previously occupied slot
// and ignore an error if it occurs.
type ReleaseFunc func()

// Release calls f().
func (f ReleaseFunc) Release() error {
	f()
	return nil
}

// Semaphore provides the functionality of the same named pattern.
type Semaphore interface {
	HealthChecker
	Releaser

	// Acquire tries to reduce the number of available slots for 1.
	// The operation can be canceled using context. In this case,
	// it returns an appropriate error.
	// It must be safe to call Acquire concurrently on a single semaphore.
	Acquire(deadline <-chan struct{}) (ReleaseFunc, error)
	// Signal returns a channel to send to it release function
	// only if Acquire is successful. In any case, the channel will be closed.
	Signal(deadline <-chan struct{}) <-chan ReleaseFunc
}

// New constructs a new thread-safe Semaphore with the given capacity.
func New(capacity int) Semaphore {
	return make(semaphore, capacity)
}

// IsEmpty checks if passed error is related to call Release on empty semaphore.
func IsEmpty(err error) bool {
	return err == errEmpty
}

// IsTimeout checks if passed error is related to call Acquire on full semaphore.
func IsTimeout(err error) bool {
	return err == errTimeout
}

var (
	nothing ReleaseFunc = func() {}

	errEmpty   = errors.New("semaphore is empty")
	errTimeout = errors.New("operation timeout")
)

type semaphore chan struct{}

func (sem semaphore) Acquire(deadline <-chan struct{}) (ReleaseFunc, error) {
	select {
	case sem <- struct{}{}:
		//nolint: gas
		return func() { _ = sem.Release() }, nil
	case <-deadline:
		return nothing, errTimeout
	}
}

func (sem semaphore) Capacity() int {
	return cap(sem)
}

func (sem semaphore) Occupied() int {
	return len(sem)
}

func (sem semaphore) Release() error {
	select {
	case <-sem:
		return nil
	default:
		return errEmpty
	}
}

func (sem semaphore) Signal(deadline <-chan struct{}) <-chan ReleaseFunc {
	ch := make(chan ReleaseFunc, 1)
	go func() {
		if release, err := sem.Acquire(deadline); err == nil {
			ch <- release
		}
		close(ch)
	}()
	return ch
}
<commit_msg>move nolint comment<commit_after>// Copyright (c) 2017 OctoLab. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

// Package semaphore provides an implementation of Semaphore pattern
// with a timeout of lock/unlock operations based on channels.
package semaphore // import "github.com/kamilsk/semaphore"

import "errors"

// HealthChecker defines helpful methods related with semaphore status.
type HealthChecker interface {
	// Capacity returns a capacity of a semaphore.
	// It must be safe to call Capacity concurrently on a single semaphore.
	Capacity() int
	// Occupied returns a current number of occupied slots.
	// It must be safe to call Occupied concurrently on a single semaphore.
	Occupied() int
}

// Releaser defines a method to release the previously occupied semaphore.
type Releaser interface {
	// Release releases the previously occupied slot.
	// If no places were occupied, then it returns an appropriate error.
	// It must be safe to call Release concurrently on a single semaphore.
	Release() error
}

// ReleaseFunc tells a semaphore to release the previously occupied slot
// and ignore an error if it occurs.
type ReleaseFunc func()

// Release calls f().
func (f ReleaseFunc) Release() error {
	f()
	return nil
}

// Semaphore provides the functionality of the same named pattern.
type Semaphore interface {
	HealthChecker
	Releaser

	// Acquire tries to reduce the number of available slots for 1.
	// The operation can be canceled using context. In this case,
	// it returns an appropriate error.
	// It must be safe to call Acquire concurrently on a single semaphore.
	Acquire(deadline <-chan struct{}) (ReleaseFunc, error)
	// Signal returns a channel to send to it release function
	// only if Acquire is successful. In any case, the channel will be closed.
	Signal(deadline <-chan struct{}) <-chan ReleaseFunc
}

// New constructs a new thread-safe Semaphore with the given capacity.
func New(capacity int) Semaphore {
	return make(semaphore, capacity)
}

// IsEmpty checks if passed error is related to call Release on empty semaphore.
func IsEmpty(err error) bool {
	return err == errEmpty
}

// IsTimeout checks if passed error is related to call Acquire on full semaphore.
func IsTimeout(err error) bool {
	return err == errTimeout
}

var (
	nothing ReleaseFunc = func() {}

	errEmpty   = errors.New("semaphore is empty")
	errTimeout = errors.New("operation timeout")
)

type semaphore chan struct{}

func (sem semaphore) Acquire(deadline <-chan struct{}) (ReleaseFunc, error) {
	select {
	case sem <- struct{}{}:
		return func() { _ = sem.Release() }, nil //nolint: gas
	case <-deadline:
		return nothing, errTimeout
	}
}

func (sem semaphore) Capacity() int {
	return cap(sem)
}

func (sem semaphore) Occupied() int {
	return len(sem)
}

func (sem semaphore) Release() error {
	select {
	case <-sem:
		return nil
	default:
		return errEmpty
	}
}

func (sem semaphore) Signal(deadline <-chan struct{}) <-chan ReleaseFunc {
	ch := make(chan ReleaseFunc, 1)
	go func() {
		if release, err := sem.Acquire(deadline); err == nil {
			ch <- release
		}
		close(ch)
	}()
	return ch
}
<|endoftext|>


<commit_before>package scoring

import (
	"sort"
	"strings"

	"github.com/gsamokovarov/jump/lcs"
)

type FuzzyEntries struct {
	Entries
	Target string
}

// Less compares the Longest Subsequence Length between the Target string and
// every entry. The entries with greater LCS come first.
func (fe FuzzyEntries) Less(i, j int) bool {
	iPath := strings.ToLower(fe.Entries[i].BasePath())
	jPath := strings.ToLower(fe.Entries[j].BasePath())

	return lcs.Length(iPath, fe.Target) >= lcs.Length(jPath, fe.Target)
}

func (fe FuzzyEntries) Sort() {
	// Leaving this, when fe.Sort() is called, the Entries.Sort method will be
	// call. In its context, the receiver is Entries, therefore, Entries.Less
	// and not FuzzyEntries.Less will be called during sorting.
	sort.Sort(fe)
}

// Select selects the entry with greatest LCS score.
func (fe FuzzyEntries) Select() (entry *Entry, empty bool) {
	fe.Sort()

	if fe.Len() == 0 {
		return nil, true
	}

	return &fe.Entries[0], false
}

// NewFuzzyEntries converts a FuzzyEntries and a target string to a
// FuzzyEntries struct.
//
// Entries is expected to be sorted before creating the FuzzyEntries. This
// gives us the best match. This is not enforced, however.
func NewFuzzyEntries(entries Entries, target string) *FuzzyEntries {
	return &FuzzyEntries{entries, target}
}
<commit_msg>Fix my broken grammar<commit_after>package scoring

import (
	"sort"
	"strings"

	"github.com/gsamokovarov/jump/lcs"
)

type FuzzyEntries struct {
	Entries
	Target string
}

// Less compares the Longest Subsequence Length between the Target string and
// every entry. The entries with greater LCS come first.
func (fe FuzzyEntries) Less(i, j int) bool {
	iPath := strings.ToLower(fe.Entries[i].BasePath())
	jPath := strings.ToLower(fe.Entries[j].BasePath())

	return lcs.Length(iPath, fe.Target) >= lcs.Length(jPath, fe.Target)
}

func (fe FuzzyEntries) Sort() {
	// If this method is left undefined, when fe.Sort() is called, the
	// Entries.Sort method will be called. In its context, the receiver is
	// Entries, therefore, Entries.Less, and not FuzzyEntries.Less, will be
	// called during sorting.
	sort.Sort(fe)
}

// Select selects the entry with greatest LCS score.
func (fe FuzzyEntries) Select() (entry *Entry, empty bool) {
	fe.Sort()

	if fe.Len() == 0 {
		return nil, true
	}

	return &fe.Entries[0], false
}

// NewFuzzyEntries converts a FuzzyEntries and a target string to a
// FuzzyEntries struct.
//
// Entries is expected to be sorted before creating the FuzzyEntries. This
// gives us the best match. This is not enforced, however.
func NewFuzzyEntries(entries Entries, target string) *FuzzyEntries {
	return &FuzzyEntries{entries, target}
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"testing"

	. "github.com/VonC/godbg"
	"github.com/VonC/godbg/exit"
	"github.com/VonC/senvgo/installer"
	"github.com/VonC/senvgo/prgs"
	. "github.com/smartystreets/goconvey/convey"
)

type testGetter0Prg struct{}
type testPrg struct{ name string }

func (tp *testPrg) Name() string { return tp.name }

func (tg0 testGetter0Prg) Get() []prgs.Prg {
	return []prgs.Prg{}
}

type testGetter3Prgs struct{}

var prefix string

func (tg3 testGetter3Prgs) Get() []prgs.Prg {
	return []prgs.Prg{&testPrg{name: prefix + "1"}, &testPrg{name: prefix + "2"}, &testPrg{name: prefix + "3"}}
}

type testInst struct{ p prgs.Prg }

func newTestInst(p prgs.Prg) installer.Inst {
	return &testInst{p: p}
}

func (ti *testInst) IsInstalled() bool {
	return strings.HasPrefix(ti.p.Name(), "prgi")
}
func (ti *testInst) HasFailed() bool {
	return strings.HasPrefix(ti.p.Name(), "prgf")
}

func TestMain(t *testing.T) {

	exiter = exit.New(func(int) {})

	Convey("senvgo main installation scenario with no command", t, func() {
		SetBuffers(nil)
		prefix = "prg"
		prgsGetter = testGetter0Prg{}
		newInstaller = newTestInst
		main()
		So(ErrString(), ShouldEqualNL, `  [main] (func)
    senvgo
`)
		So(exiter.Status(), ShouldEqual, 0)

		Convey("No prg means no prgs installed", func() {
			SetBuffers(nil)
			main()
			So(OutString(), ShouldEqual, `No program to install: nothing to do`)
			So(ErrString(), ShouldEqualNL, `  [main] (func)
    senvgo
`)
			So(exiter.Status(), ShouldEqual, 0)
			prgsGetter = testGetter3Prgs{}
			SetBuffers(nil)
			main()
			So(OutString(), ShouldNotEqual, `No program to install: nothing to do`)
		})

		Convey("A program already installed means nothing to do", func() {
			prefix = "prgi"
			prgsGetter = testGetter3Prgs{}
			SetBuffers(nil)
			main()
			So(OutString(), ShouldEqual, `'prgi1' (1/3)... already installed: nothing to do
'prgi2' (2/3)... already installed: nothing to do
'prgi3' (3/3)... already installed: nothing to do
`)
		})
		Convey("A program already failed means nothing to do", func() {
			prefix = "prgf"
			prgsGetter = testGetter3Prgs{}
			SetBuffers(nil)
			main()
			So(OutString(), ShouldEqual, `'prgf1' (1/3)... already failed to install
'prgf2' (2/3)... already failed to install
'prgf3' (3/3)... already failed to install
`)
		})
	})
}
<commit_msg>Test senv compile again<commit_after>package main

import (
	"strings"
	"testing"

	. "github.com/VonC/godbg"
	"github.com/VonC/godbg/exit"
	"github.com/VonC/senvgo/installer"
	"github.com/VonC/senvgo/prgs"
	. "github.com/smartystreets/goconvey/convey"
)

type testGetter0Prg struct{}
type testPrg struct{ name string }

func (tp *testPrg) Name() string { return tp.name }

func (tg0 testGetter0Prg) Get() []prgs.Prg {
	return []prgs.Prg{}
}

type testGetter3Prgs struct{}

var prefix string

func (tg3 testGetter3Prgs) Get() []prgs.Prg {
	return []prgs.Prg{&testPrg{name: prefix + "1"}, &testPrg{name: prefix + "2"}, &testPrg{name: prefix + "3"}}
}

type testInst struct{ p prgs.Prg }

func newTestInst(p prgs.Prg) installer.Inst {
	return &testInst{p: p}
}

func (ti *testInst) IsInstalled() bool {
	return strings.HasPrefix(ti.p.Name(), "prgi")
}
func (ti *testInst) HasFailed() bool {
	return strings.HasPrefix(ti.p.Name(), "prgf")
}
func (ti *testInst) Install() error {
	return nil
}

func TestMain(t *testing.T) {

	exiter = exit.New(func(int) {})

	Convey("senvgo main installation scenario with no command", t, func() {
		SetBuffers(nil)
		prefix = "prg"
		prgsGetter = testGetter0Prg{}
		newInstaller = newTestInst
		main()
		So(ErrString(), ShouldEqualNL, `  [main] (func)
    senvgo
`)
		So(exiter.Status(), ShouldEqual, 0)

		Convey("No prg means no prgs installed", func() {
			SetBuffers(nil)
			main()
			So(OutString(), ShouldEqual, `No program to install: nothing to do`)
			So(ErrString(), ShouldEqualNL, `  [main] (func)
    senvgo
`)
			So(exiter.Status(), ShouldEqual, 0)
			prgsGetter = testGetter3Prgs{}
			SetBuffers(nil)
			main()
			So(OutString(), ShouldNotEqual, `No program to install: nothing to do`)
		})

		Convey("A program already installed means nothing to do", func() {
			prefix = "prgi"
			prgsGetter = testGetter3Prgs{}
			SetBuffers(nil)
			main()
			So(OutString(), ShouldEqual, `'prgi1' (1/3)... already installed: nothing to do
'prgi2' (2/3)... already installed: nothing to do
'prgi3' (3/3)... already installed: nothing to do
`)
		})
		Convey("A program already failed means nothing to do", func() {
			prefix = "prgf"
			prgsGetter = testGetter3Prgs{}
			SetBuffers(nil)
			main()
			So(OutString(), ShouldEqual, `'prgf1' (1/3)... already failed to install
'prgf2' (2/3)... already failed to install
'prgf3' (3/3)... already failed to install
`)
		})
	})
}
<|endoftext|>


<commit_before>package scipipe

import (
	//"fmt"
	"github.com/stretchr/testify/assert"
	//"os"
	t "testing"
	//"time"
	"os"
)

func initTestLogs() {
	InitLogWarn()
}

func TestBasicRun(t *t.T) {
	initTestLogs()

	t1 := Shell("echo foo > {o:foo}")
	assert.IsType(t, t1.OutPorts["foo"], make(chan *FileTarget))
	t1.OutPathFuncs["foo"] = func(t *ShellTask) string {
		return "foo.txt"
	}

	t2 := Shell("sed 's/foo/bar/g' {i:foo} > {o:bar}")
	assert.IsType(t, t2.InPorts["foo"], make(chan *FileTarget))
	assert.IsType(t, t2.OutPorts["bar"], make(chan *FileTarget))
	t2.OutPathFuncs["bar"] = func(t *ShellTask) string {
		return t.GetInPath("foo") + ".bar.txt"
	}
	snk := NewSink()

	t2.InPorts["foo"] = t1.OutPorts["foo"]
	snk.In = t2.OutPorts["bar"]

	assert.IsType(t, t2.InPorts["foo"], make(chan *FileTarget))
	assert.IsType(t, t2.OutPorts["bar"], make(chan *FileTarget))

	pl := NewPipeline()
	pl.AddProcs(t1, t2, snk)
	pl.Run()

	cleanFiles("foo.txt", "foo.txt.bar.txt")
}

// func TestShellHasInOutPorts(t *t.T) {
// 	initTestLogs()
//
// 	tt := Sh("echo {i:in1} > {o:out1}")
// 	tt.OutPathFuncs["out1"] = func(task *ShellTask) string {
// 		return fmt.Sprint(task.GetInPath("in1"), ".bar")
// 	}
// 	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
//
// 	go tt.Run()
// 	go func() {
// 		defer close(tt.InPorts["in1"])
// 		tt.InPorts["in1"] <- NewFileTarget("foo.txt")
// 	}()
// 	<-tt.OutPorts["out1"]
//
// 	assert.NotNil(t, tt.InPorts["in1"], "InPorts are nil!")
// 	assert.NotNil(t, tt.OutPorts["out1"], "OutPorts are nil!")
//
// 	cleanFiles("foo.txt", "foo.txt.bar")
// }
//
// func TestShellCloseOutPortOnInPortClose(t *t.T) {
// 	initTestLogs()
//
// 	foo := Sh("echo foo > {o:out1}")
// 	foo.OutPathFuncs["out1"] = func(task *ShellTask) string {
// 		return "/tmp/foo.txt"
// 	}
//
// 	f2b := Sh("sed 's/foo/bar/g' {i:foo} > {o:bar}")
// 	f2b.OutPathFuncs["bar"] = func(task *ShellTask) string {
// 		return task.GetInPath("foo") + ".bar"
// 	}
//
// 	f2b.InPorts["foo"] = foo.OutPorts["out1"]
//
// 	go foo.Run()
// 	f2b.Run()
// 	//<-f2b.OutPorts["bar"]
//
// 	// Assert no more content coming on channels
// 	assert.Nil(t, <-foo.OutPorts["out1"])
// 	assert.Nil(t, <-f2b.OutPorts["bar"])
//
// 	_, fooErr := os.Stat("/tmp/foo.txt")
// 	assert.Nil(t, fooErr)
// 	_, barErr := os.Stat("/tmp/foo.txt.bar")
// 	assert.Nil(t, barErr)
//
// 	cleanFiles("/tmp/foo.txt", "/tmp/foo.txt.bar")
// }
//
// // func TestReplacePlaceholdersInCmd(t *t.T) {
// // 	initTestLogs()
// //
// // 	rawCmd := "echo {i:in1} > {o:out1}"
// // 	tt := Sh(rawCmd)
// // 	tt.OutPathFuncs["out1"] = func(task *ShellTask) string {
// // 		return fmt.Sprint(t.GetInPath("in1"), ".bar")
// // 	}
// //
// // 	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
// // 	ift := NewFileTarget("foo.txt")
// // 	go func() {
// // 		defer close(tt.InPorts["in1"])
// // 		tt.InPorts["in1"] <- ift
// // 	}()
// //
// // 	// Assert inport is still open after first read
// // 	_, inPortsOpen := tt.receiveInputs()
// // 	assert.Equal(t, true, inPortsOpen)
// //
// // 	// Assert inport is closed after second read
// // 	_, inPortsOpen = tt.receiveInputs()
// // 	assert.Equal(t, false, inPortsOpen)
// //
// // 	// Assert InPath is correct
// // 	//assert.Equal(t, "foo.txt", tt.GetInPath("in1"), "foo.txt")
// //
// // 	// Assert placeholders are correctly replaced in command
// // 	outTargets := tt.createOutTargets()
// // 	cmd := tt.formatCommand(rawCmd, outTargets)
// // 	assert.EqualValues(t, "echo foo.txt > foo.txt.bar.tmp", cmd, "Command not properly formatted!")
// //
// // 	// Test prepend
// // 	tt.Prepend = "dash"
// // 	cmd = tt.formatCommand(rawCmd, outTargets)
// // 	assert.EqualValues(t, "dash echo foo.txt > foo.txt.bar.tmp", cmd, "Prepend not working!")
// // }
//
// // func TestParameterCommand(t *t.T) {
// // 	initTestLogs()
// //
// // 	cmb := NewCombinatoricsProcess()
// //
// // 	// An abc file printer
// // 	abc := Sh("echo {p:a} {p:b} {p:c} > {o:out}")
// // 	abc.OutPathFuncs["out"] = func(task *ShellTask) string {
// // 		return fmt.Sprintf(
// // 			"%s_%s_%s.txt",
// // 			abc.Params["a"],
// // 			abc.Params["b"],
// // 			abc.Params["c"],
// // 		)
// // 	}
// //
// // 	// A printer process
// // 	prt := Sh("cat {i:in} >> /tmp/log.txt; rm {i:in}")
// //
// // 	// Connection info
// // 	abc.ParamPorts["a"] = cmb.A
// // 	abc.ParamPorts["b"] = cmb.B
// // 	abc.ParamPorts["c"] = cmb.C
// // 	prt.InPorts["in"] = abc.OutPorts["out"]
// //
// // 	pl := NewPipeline()
// // 	pl.AddProcesses(cmb, abc, prt)
// // 	pl.Run()
// //
// // 	// Run tests
// // 	_, err := os.Stat("/tmp/log.txt")
// // 	assert.Nil(t, err)
// //
// // 	cleanFiles("/tmp/log.txt")
// // }
//
// func TestProcessWithoutInputsOutputs(t *t.T) {
// 	initTestLogs()
// 	Debug.Println("Starting test TestProcessWithoutInputsOutputs")
//
// 	f := "/tmp/hej.txt"
// 	tsk := Sh("echo hej > " + f)
// 	tsk.Run()
// 	_, err := os.Stat(f)
// 	assert.Nil(t, err)
// 	cleanFiles(f)
// }
//
// func TestDontOverWriteExistingOutputs(t *t.T) {
// 	InitLogError()
// 	Debug.Println("Starting test TestDontOverWriteExistingOutputs")
//
// 	f := "/tmp/hej.txt"
//
// 	// Assert file does not exist before running
// 	_, e1 := os.Stat(f)
// 	assert.NotNil(t, e1)
//
// 	// Run pipeline a first time
// 	tsk := Sh("echo hej > {o:hej}")
// 	tsk.OutPathFuncs["hej"] = func(task *ShellTask) string { return f }
// 	prt := Sh("echo {i:in} Done!")
// 	prt.InPorts["in"] = tsk.OutPorts["hej"]
// 	pl := NewPipeline()
// 	pl.AddProcs(tsk, prt)
// 	pl.Run()
//
// 	// Assert file DO exist after running
// 	fiBef, e2 := os.Stat(f)
// 	assert.Nil(t, e2)
//
// 	// Get modified time before
// 	mtBef := fiBef.ModTime()
//
// 	// Make sure some time has passed before the second write
// 	time.Sleep(1 * time.Millisecond)
//
// 	Debug.Println("Try running the same workflow again ...")
// 	// Run again with different output
// 	tsk = Sh("echo hej > {o:hej}")
// 	tsk.OutPathFuncs["hej"] = func(task *ShellTask) string { return f }
// 	prt.InPorts["in"] = tsk.OutPorts["hej"]
// 	pl = NewPipeline()
// 	pl.AddProcs(tsk, prt)
// 	pl.Run()
//
// 	// Assert exists
// 	fiAft, e3 := os.Stat(f)
// 	assert.Nil(t, e3)
//
// 	// Get modified time AFTER second run
// 	mtAft := fiAft.ModTime()
//
// 	// Assert file is not modified!
// 	assert.EqualValues(t, mtBef, mtAft)
//
// 	cleanFiles(f)
// }
//
// // func TestShellExpand(t *t.T) {
// // 	initTestLogs()
// //
// // 	cmdPat := "echo {p:txt} > {i:in}; cat {i:in} > {o:out}"
// // 	expectedCmd := "echo hej > in.txt; cat in.txt > out.txt"
// //
// // 	params := make(map[string]string)
// // 	params["txt"] = "hej"
// // 	ipaths := make(map[string]string)
// // 	ipaths["in"] = "in.txt"
// // 	opaths := make(map[string]string)
// // 	opaths["out"] = "out.txt"
// //
// // 	// cmd := expandCommandParamsAndPaths(cmdPat, params, ipaths, opaths)
// // 	// assert.EqualValues(t, expectedCmd, cmd, "Command not properly expanded!")
// //
// // 	// st := ShExp(cmdPat, ipaths, opaths, params)
// //
// // 	// Assert that no ports are created, since all place holders
// // 	// are replaced by the provided maps.
// // 	// assert.EqualValues(t, 0, len(st.InPorts), "Inports created where it should not!")
// // 	// assert.EqualValues(t, 0, len(st.OutPorts), "OutPorts created where it should not!")
// // 	// assert.EqualValues(t, 0, len(st.ParamPorts), "ParamPorts created where it should not!")
// //
// // 	// st.Run()
// // 	assert.True(t, NewFileTarget("out.txt").Exists())
// //
// // 	cleanFiles("in.txt", "out.txt")
// // }
//
// // Make sure that outputs are returned in order, even though they are
// // spawned to work in parallel.
// func TestSendsOrderedOutputs(t *t.T) {
// 	initTestLogs()
//
// 	fnames := []string{}
// 	for i := 1; i <= 10; i++ {
// 		fnames = append(fnames, fmt.Sprintf("/tmp/f%d.txt", i))
// 	}
//
// 	fq := NewFileQueue(fnames...)
//
// 	fc := Sh("echo {i:in} > {o:out}")
// 	sl := Sh("cat {i:in} > {o:out}")
//
// 	fc.OutPathFuncs["out"] = func(task *ShellTask) string { return task.GetInPath("in") }
// 	sl.OutPathFuncs["out"] = func(task *ShellTask) string { return task.GetInPath("in") + ".copy.txt" }
//
// 	go fq.Run()
// 	go fc.Run()
// 	go sl.Run()
//
// 	fc.InPorts["in"] = fq.Out
// 	sl.InPorts["in"] = fc.OutPorts["out"]
//
// 	assert.NotEmpty(t, sl.OutPorts)
//
// 	var expFname string
// 	i := 1
// 	for ft := range sl.OutPorts["out"] {
// 		expFname = fmt.Sprintf("/tmp/f%d.txt.copy.txt", i)
// 		assert.EqualValues(t, expFname, ft.GetPath())
// 		i++
// 	}
// 	expFnames := []string{}
// 	for i := 1; i <= 10; i++ {
// 		expFnames = append(expFnames, fmt.Sprintf("/tmp/f%d.txt.copy.txt", i))
// 	}
// 	cleanFiles(fnames...)
// 	cleanFiles(expFnames...)
// }
//
// // Test that streaming works
// // func TestStreaming(t *t.T) {
// // 	initTestLogs()
// //
// // 	// Init processes
// // 	ls := Shell("ls -l / > {os:filelist}")
// // 	ls.OutPathGenString("filelist", "filelist.txt")
// // 	sn := NewSink()
// //
// // 	// Connect
// // 	sn.In = ls.OutPorts["filelist"]
// //
// // 	// Run
// // 	go ls.Run()
// // 	sn.Run()
// //
// // 	// Clean up
// // 	cleanFiles("filelist.txt")
// // }
//

//
// // Helper processes
//
// type CombinatoricsProcess struct {
// 	BaseProcess
// 	A chan string
// 	B chan string
// 	C chan string
// }
//
// func NewCombinatoricsProcess() *CombinatoricsProcess {
// 	return &CombinatoricsProcess{
// 		A: make(chan string, BUFSIZE),
// 		B: make(chan string, BUFSIZE),
// 		C: make(chan string, BUFSIZE),
// 	}
// }
//
// func (proc *CombinatoricsProcess) Run() {
// 	defer close(proc.A)
// 	defer close(proc.B)
// 	defer close(proc.C)
//
// 	for _, a := range SS("a1", "a2", "a3") {
// 		for _, b := range SS("b1", "b2", "b3") {
// 			for _, c := range SS("c1", "c2", "c3") {
// 				proc.A <- a
// 				proc.B <- b
// 				proc.C <- c
// 			}
// 		}
// 	}
// }

// Helper functions
func cleanFiles(fileNames ...string) {
	Debug.Println("Starting to remove files:", fileNames)
	for _, fileName := range fileNames {
		if _, err := os.Stat(fileName); err == nil {
			os.Remove(fileName)
			Debug.Println("Successfully removed file", fileName)
		}
	}
}
<commit_msg>Enable some more tests, making sure they pass<commit_after>package scipipe

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	//"os"
	t "testing"
	//"time"
	"os"
)

func initTestLogs() {
	InitLogWarn()
}

func TestBasicRun(t *t.T) {
	initTestLogs()

	t1 := Shell("echo foo > {o:foo}")
	assert.IsType(t, t1.OutPorts["foo"], make(chan *FileTarget))
	t1.OutPathFuncs["foo"] = func(t *ShellTask) string {
		return "foo.txt"
	}

	t2 := Shell("sed 's/foo/bar/g' {i:foo} > {o:bar}")
	assert.IsType(t, t2.InPorts["foo"], make(chan *FileTarget))
	assert.IsType(t, t2.OutPorts["bar"], make(chan *FileTarget))
	t2.OutPathFuncs["bar"] = func(t *ShellTask) string {
		return t.GetInPath("foo") + ".bar.txt"
	}
	snk := NewSink()

	t2.InPorts["foo"] = t1.OutPorts["foo"]
	snk.In = t2.OutPorts["bar"]

	assert.IsType(t, t2.InPorts["foo"], make(chan *FileTarget))
	assert.IsType(t, t2.OutPorts["bar"], make(chan *FileTarget))

	pl := NewPipeline()
	pl.AddProcs(t1, t2, snk)
	pl.Run()

	cleanFiles("foo.txt", "foo.txt.bar.txt")
}

// func TestShellHasInOutPorts(t *t.T) {
// 	initTestLogs()
//
// 	tt := Sh("echo {i:in1} > {o:out1}")
// 	tt.OutPathFuncs["out1"] = func(task *ShellTask) string {
// 		return fmt.Sprint(task.GetInPath("in1"), ".bar")
// 	}
// 	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
//
// 	go tt.Run()
// 	go func() {
// 		defer close(tt.InPorts["in1"])
// 		tt.InPorts["in1"] <- NewFileTarget("foo.txt")
// 	}()
// 	<-tt.OutPorts["out1"]
//
// 	assert.NotNil(t, tt.InPorts["in1"], "InPorts are nil!")
// 	assert.NotNil(t, tt.OutPorts["out1"], "OutPorts are nil!")
//
// 	cleanFiles("foo.txt", "foo.txt.bar")
// }
//
// func TestShellCloseOutPortOnInPortClose(t *t.T) {
// 	initTestLogs()
//
// 	foo := Sh("echo foo > {o:out1}")
// 	foo.OutPathFuncs["out1"] = func(task *ShellTask) string {
// 		return "/tmp/foo.txt"
// 	}
//
// 	f2b := Sh("sed 's/foo/bar/g' {i:foo} > {o:bar}")
// 	f2b.OutPathFuncs["bar"] = func(task *ShellTask) string {
// 		return task.GetInPath("foo") + ".bar"
// 	}
//
// 	f2b.InPorts["foo"] = foo.OutPorts["out1"]
//
// 	go foo.Run()
// 	f2b.Run()
// 	//<-f2b.OutPorts["bar"]
//
// 	// Assert no more content coming on channels
// 	assert.Nil(t, <-foo.OutPorts["out1"])
// 	assert.Nil(t, <-f2b.OutPorts["bar"])
//
// 	_, fooErr := os.Stat("/tmp/foo.txt")
// 	assert.Nil(t, fooErr)
// 	_, barErr := os.Stat("/tmp/foo.txt.bar")
// 	assert.Nil(t, barErr)
//
// 	cleanFiles("/tmp/foo.txt", "/tmp/foo.txt.bar")
// }
//
// // func TestReplacePlaceholdersInCmd(t *t.T) {
// // 	initTestLogs()
// //
// // 	rawCmd := "echo {i:in1} > {o:out1}"
// // 	tt := Sh(rawCmd)
// // 	tt.OutPathFuncs["out1"] = func(task *ShellTask) string {
// // 		return fmt.Sprint(t.GetInPath("in1"), ".bar")
// // 	}
// //
// // 	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
// // 	ift := NewFileTarget("foo.txt")
// // 	go func() {
// // 		defer close(tt.InPorts["in1"])
// // 		tt.InPorts["in1"] <- ift
// // 	}()
// //
// // 	// Assert inport is still open after first read
// // 	_, inPortsOpen := tt.receiveInputs()
// // 	assert.Equal(t, true, inPortsOpen)
// //
// // 	// Assert inport is closed after second read
// // 	_, inPortsOpen = tt.receiveInputs()
// // 	assert.Equal(t, false, inPortsOpen)
// //
// // 	// Assert InPath is correct
// // 	//assert.Equal(t, "foo.txt", tt.GetInPath("in1"), "foo.txt")
// //
// // 	// Assert placeholders are correctly replaced in command
// // 	outTargets := tt.createOutTargets()
// // 	cmd := tt.formatCommand(rawCmd, outTargets)
// // 	assert.EqualValues(t, "echo foo.txt > foo.txt.bar.tmp", cmd, "Command not properly formatted!")
// //
// // 	// Test prepend
// // 	tt.Prepend = "dash"
// // 	cmd = tt.formatCommand(rawCmd, outTargets)
// // 	assert.EqualValues(t, "dash echo foo.txt > foo.txt.bar.tmp", cmd, "Prepend not working!")
// // }
//

func TestParameterCommand(t *t.T) {
	initTestLogs()

	cmb := NewCombinatoricsProcess()

	// An abc file printer
	abc := Sh("echo {p:a} {p:b} {p:c} > {o:out}")
	abc.OutPathFuncs["out"] = func(task *ShellTask) string {
		return fmt.Sprintf(
			"%s_%s_%s.txt",
			task.Params["a"],
			task.Params["b"],
			task.Params["c"],
		)
	}

	// A printer process
	prt := Sh("cat {i:in} >> /tmp/log.txt; rm {i:in}")

	// Connection info
	abc.ParamPorts["a"] = cmb.A
	abc.ParamPorts["b"] = cmb.B
	abc.ParamPorts["c"] = cmb.C
	prt.InPorts["in"] = abc.OutPorts["out"]

	pl := NewPipeline()
	pl.AddProcesses(cmb, abc, prt)
	pl.Run()

	// Run tests
	_, err := os.Stat("/tmp/log.txt")
	assert.Nil(t, err)

	cleanFiles("/tmp/log.txt")
}

//
// func TestProcessWithoutInputsOutputs(t *t.T) {
// 	initTestLogs()
// 	Debug.Println("Starting test TestProcessWithoutInputsOutputs")
//
// 	f := "/tmp/hej.txt"
// 	tsk := Sh("echo hej > " + f)
// 	tsk.Run()
// 	_, err := os.Stat(f)
// 	assert.Nil(t, err)
// 	cleanFiles(f)
// }
//
// func TestDontOverWriteExistingOutputs(t *t.T) {
// 	InitLogError()
// 	Debug.Println("Starting test TestDontOverWriteExistingOutputs")
//
// 	f := "/tmp/hej.txt"
//
// 	// Assert file does not exist before running
// 	_, e1 := os.Stat(f)
// 	assert.NotNil(t, e1)
//
// 	// Run pipeline a first time
// 	tsk := Sh("echo hej > {o:hej}")
// 	tsk.OutPathFuncs["hej"] = func(task *ShellTask) string { return f }
// 	prt := Sh("echo {i:in} Done!")
// 	prt.InPorts["in"] = tsk.OutPorts["hej"]
// 	pl := NewPipeline()
// 	pl.AddProcs(tsk, prt)
// 	pl.Run()
//
// 	// Assert file DO exist after running
// 	fiBef, e2 := os.Stat(f)
// 	assert.Nil(t, e2)
//
// 	// Get modified time before
// 	mtBef := fiBef.ModTime()
//
// 	// Make sure some time has passed before the second write
// 	time.Sleep(1 * time.Millisecond)
//
// 	Debug.Println("Try running the same workflow again ...")
// 	// Run again with different output
// 	tsk = Sh("echo hej > {o:hej}")
// 	tsk.OutPathFuncs["hej"] = func(task *ShellTask) string { return f }
// 	prt.InPorts["in"] = tsk.OutPorts["hej"]
// 	pl = NewPipeline()
// 	pl.AddProcs(tsk, prt)
// 	pl.Run()
//
// 	// Assert exists
// 	fiAft, e3 := os.Stat(f)
// 	assert.Nil(t, e3)
//
// 	// Get modified time AFTER second run
// 	mtAft := fiAft.ModTime()
//
// 	// Assert file is not modified!
// 	assert.EqualValues(t, mtBef, mtAft)
//
// 	cleanFiles(f)
// }
//
// // func TestShellExpand(t *t.T) {
// // 	initTestLogs()
// //
// // 	cmdPat := "echo {p:txt} > {i:in}; cat {i:in} > {o:out}"
// // 	expectedCmd := "echo hej > in.txt; cat in.txt > out.txt"
// //
// // 	params := make(map[string]string)
// // 	params["txt"] = "hej"
// // 	ipaths := make(map[string]string)
// // 	ipaths["in"] = "in.txt"
// // 	opaths := make(map[string]string)
// // 	opaths["out"] = "out.txt"
// //
// // 	// cmd := expandCommandParamsAndPaths(cmdPat, params, ipaths, opaths)
// // 	// assert.EqualValues(t, expectedCmd, cmd, "Command not properly expanded!")
// //
// // 	// st := ShExp(cmdPat, ipaths, opaths, params)
// //
// // 	// Assert that no ports are created, since all place holders
// // 	// are replaced by the provided maps.
// // 	// assert.EqualValues(t, 0, len(st.InPorts), "Inports created where it should not!")
// // 	// assert.EqualValues(t, 0, len(st.OutPorts), "OutPorts created where it should not!")
// // 	// assert.EqualValues(t, 0, len(st.ParamPorts), "ParamPorts created where it should not!")
// //
// // 	// st.Run()
// // 	assert.True(t, NewFileTarget("out.txt").Exists())
// //
// // 	cleanFiles("in.txt", "out.txt")
// // }

// Make sure that outputs are returned in order, even though they are
// spawned to work in parallel.
func TestSendsOrderedOutputs(t *t.T) {
	initTestLogs()

	fnames := []string{}
	for i := 1; i <= 10; i++ {
		fnames = append(fnames, fmt.Sprintf("/tmp/f%d.txt", i))
	}

	fq := NewFileQueue(fnames...)

	fc := Sh("echo {i:in} > {o:out}")
	sl := Sh("cat {i:in} > {o:out}")

	fc.OutPathFuncs["out"] = func(task *ShellTask) string { return task.GetInPath("in") }
	sl.OutPathFuncs["out"] = func(task *ShellTask) string { return task.GetInPath("in") + ".copy.txt" }

	go fq.Run()
	go fc.Run()
	go sl.Run()

	fc.InPorts["in"] = fq.Out
	sl.InPorts["in"] = fc.OutPorts["out"]

	assert.NotEmpty(t, sl.OutPorts)

	var expFname string
	i := 1
	for ft := range sl.OutPorts["out"] {
		expFname = fmt.Sprintf("/tmp/f%d.txt.copy.txt", i)
		assert.EqualValues(t, expFname, ft.GetPath())
		i++
	}
	expFnames := []string{}
	for i := 1; i <= 10; i++ {
		expFnames = append(expFnames, fmt.Sprintf("/tmp/f%d.txt.copy.txt", i))
	}
	cleanFiles(fnames...)
	cleanFiles(expFnames...)
}

//
// // Test that streaming works
// // func TestStreaming(t *t.T) {
// // 	initTestLogs()
// //
// // 	// Init processes
// // 	ls := Shell("ls -l / > {os:filelist}")
// // 	ls.OutPathGenString("filelist", "filelist.txt")
// // 	sn := NewSink()
// //
// // 	// Connect
// // 	sn.In = ls.OutPorts["filelist"]
// //
// // 	// Run
// // 	go ls.Run()
// // 	sn.Run()
// //
// // 	// Clean up
// // 	cleanFiles("filelist.txt")
// // }
//

// Helper processes

type CombinatoricsProcess struct {
	BaseProcess
	A chan string
	B chan string
	C chan string
}

func NewCombinatoricsProcess() *CombinatoricsProcess {
	return &CombinatoricsProcess{
		A: make(chan string, BUFSIZE),
		B: make(chan string, BUFSIZE),
		C: make(chan string, BUFSIZE),
	}
}

func (proc *CombinatoricsProcess) Run() {
	defer close(proc.A)
	defer close(proc.B)
	defer close(proc.C)

	for _, a := range SS("a1", "a2", "a3") {
		for _, b := range SS("b1", "b2", "b3") {
			for _, c := range SS("c1", "c2", "c3") {
				proc.A <- a
				proc.B <- b
				proc.C <- c
			}
		}
	}
}

// Helper functions
func cleanFiles(fileNames ...string) {
	Debug.Println("Starting to remove files:", fileNames)
	for _, fileName := range fileNames {
		if _, err := os.Stat(fileName); err == nil {
			os.Remove(fileName)
			Debug.Println("Successfully removed file", fileName)
		}
	}
}
<|endoftext|>


<commit_before>// typebrowser - View type information from your program in your browser!
//
// Copyright 2013 Arne Hormann and contributors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package typebrowser

import (
	"fmt"
	"github.com/arnehormann/mirror"
	"reflect"
	"strings"
)

func init() {
	typeWriters["html"] = htmlTypeWriter
}

func htmlTypeWriter(t *reflect.Type) (out string, err error) {
	if t == nil {
		return formWriter(nil)
	}
	lastDepth := 0
	Concat := func(text string) {
		out += text
	}
	Concatf := func(format string, args ...interface{}) {
		out += fmt.Sprintf(format, args...)
	}
	// write leading...
	Concatf(`
<!DOCTYPE html>
<title>Go: '%s'</title>
Next`, *t)
	expectInFunc := [][2]int{}
	typeToHtml := func(t *reflect.StructField, typeIndex, depth int) error {
		// close open tags
		if lastDepth > depth {
			Concat(strings.Repeat("
", lastDepth-depth))
		}
		// close this tag later
		lastDepth = depth + 1
		// if no type is given, return
		if t == nil {
			return nil
		}
		isParent := false
		tt := t.Type
		Concatf(
			`<div data-kind="%s" data-type="%s" data-memsize="%d" data-typeid="%d"`,
			tt.Kind(), tt, tt.Size(), typeIndex)
		if len(expectInFunc) <= depth {
			expectInFunc = append(expectInFunc, [2]int{})
		} else {
			if expectInFunc[depth][0] > 0 {
				expectInFunc[depth][0]--
				Concat(` data-funcval="arg"`)
			} else {
				expectInFunc[depth][1]--
				Concat(` data-funcval="ret"`)
			}
		}
		if len(t.Index) > 0 {
			Concatf(
				` data-field="%s" data-index="%v" data-offset="%d" data-tag="%s"`,
				t.Name, t.Index, t.Offset, t.Tag)
		}
		switch tt.Kind() {
		case reflect.Chan:
			var direction string
			switch tt.ChanDir() {
			case reflect.RecvDir:
				direction = "receive"
			case reflect.SendDir:
				direction = "send"
			case reflect.BothDir:
				direction = "both"
			}
			Concat(` data-direction="` + direction + `"`)
			isParent = true

		case reflect.Func:
			argcnt, retcnt := tt.NumIn(), tt.NumOut()
			if len(expectInFunc) <= depth+1 {
				expectInFunc = append(expectInFunc, [2]int{argcnt, retcnt})
			} else {
				expectInFunc[depth+1][0] = argcnt
				expectInFunc[depth+1][1] = retcnt
			}
			Concatf(` data-argcount="%d" data-retcount="%d"`, argcnt, retcnt)
			isParent = true

		case reflect.Array:
			Concatf(` data-length="%d"`, tt.Len())
			isParent = true

		case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct:
			isParent = true
		}
		if isParent {
			Concat(` class="parent"`)
		}
		Concat(`>`)
		return nil
	}
	// walk the type
	err = mirror.Walk(*t, typeToHtml)
	if err != nil {
		return "", err
	}
	// close all tags
	_ = typeToHtml(nil, 0, 0)
	// write closing code...
	Concat(`
`)
	return out, err
}
<commit_msg>ommitted interfaces<commit_after>// typebrowser - View type information from your program in your browser!
//
// Copyright 2013 Arne Hormann and contributors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package typebrowser

import (
	"fmt"
	"github.com/arnehormann/mirror"
	"reflect"
	"strings"
)

func init() {
	typeWriters["html"] = htmlTypeWriter
}

func htmlTypeWriter(t *reflect.Type) (out string, err error) {
	if t == nil {
		return formWriter(nil)
	}
	lastDepth := 0
	Concat := func(text string) {
		out += text
	}
	Concatf := func(format string, args ...interface{}) {
		out += fmt.Sprintf(format, args...)
	}
	// write leading...
	Concatf(`
<!DOCTYPE html>
<title>Go: '%s'</title>
Next`, *t)
	expectInFunc := [][2]int{}
	typeToHtml := func(t *reflect.StructField, typeIndex, depth int) error {
		// close open tags
		if lastDepth > depth {
			Concat(strings.Repeat("
", lastDepth-depth))
		}
		// close this tag later
		lastDepth = depth + 1
		// if no type is given, return
		if t == nil {
			return nil
		}
		isParent := false
		tt := t.Type
		Concatf(
			`<div data-kind="%s" data-type="%s" data-memsize="%d" data-typeid="%d"`,
			tt.Kind(), tt, tt.Size(), typeIndex)
		if len(expectInFunc) <= depth {
			expectInFunc = append(expectInFunc, [2]int{})
		} else {
			if expectInFunc[depth][0] > 0 {
				expectInFunc[depth][0]--
				Concat(` data-funcval="arg"`)
			} else {
				expectInFunc[depth][1]--
				Concat(` data-funcval="ret"`)
			}
		}
		if len(t.Index) > 0 {
			Concatf(
				` data-field="%s" data-index="%v" data-offset="%d" data-tag="%s"`,
				t.Name, t.Index, t.Offset, t.Tag)
		}
		switch tt.Kind() {
		case reflect.Chan:
			var direction string
			switch tt.ChanDir() {
			case reflect.RecvDir:
				direction = "receive"
			case reflect.SendDir:
				direction = "send"
			case reflect.BothDir:
				direction = "both"
			}
			Concat(` data-direction="` + direction + `"`)
			isParent = true

		case reflect.Func:
			argcnt, retcnt := tt.NumIn(), tt.NumOut()
			if len(expectInFunc) <= depth+1 {
				expectInFunc = append(expectInFunc, [2]int{argcnt, retcnt})
			} else {
				expectInFunc[depth+1][0] = argcnt
				expectInFunc[depth+1][1] = retcnt
			}
			Concatf(` data-argcount="%d" data-retcount="%d"`, argcnt, retcnt)
			isParent = true

		case reflect.Array:
			Concatf(` data-length="%d"`, tt.Len())
			isParent = true

		case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Interface:
			isParent = true
		}
		if isParent {
			Concat(` class="parent"`)
		}
		Concat(`>`)
		return nil
	}
	// walk the type
	err = mirror.Walk(*t, typeToHtml)
	if err != nil {
		return "", err
	}
	// close all tags
	_ = typeToHtml(nil, 0, 0)
	// write closing code...
	Concat(`
`)
	return out, err
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package yaml

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/r3labs/composable/git"

	"gopkg.in/yaml.v2"
)

// Definition of repos
type Definition struct {
	Release struct {
		Version  string
		Org      string
		Registry string
	}
	Template  string
	BuildPath string
	Repos     []Repo `yaml:"repos"`
}

// LoadDefiniton the input definition
func LoadDefinition(path string) (*Definition, error) {
	var d Definition

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return &d, err
	}

	err = yaml.Unmarshal(data, &d)
	if err != nil {
		return &d, err
	}

	return &d, nil
}

func (d *Definition) Environment(environment string) {
	if environment != "" {
		envs := strings.Split(environment, ",")
		for _, repo := range d.Repos {
			for _, env := range envs {
				e := strings.Split(env, "=")
				repo.SetEnv(e[0], e[1])
			}
		}
	}
}

func (d *Definition) Overrides(overrides, excludes, global string) {
	// Ommit/Exclude repos
	for _, repo := range strings.Split(excludes, ",") {
		d.ExcludeRepo(repo)
	}

	// Override branches
	if global != "" {
		for _, repo := range d.Repos {
			d.OverrideBranch(repo.Name(), global)
		}
	}

	for repo, branch := range GetOverrides(overrides) {
		d.OverrideBranch(repo, branch)
	}
}

// OverrideBranch updates a repo's branch
func (d *Definition) OverrideBranch(repo, branch string) {
	for i := 0; i < len(d.Repos); i++ {
		if d.Repos[i].Name() == repo {
			d.Repos[i].SetBranch(branch)
		}
	}
}

// ExcludeRepo removes a repo from a list based on name
func (d *Definition) ExcludeRepo(repo string) {
	wildcard := strings.Contains(repo, "*")
	if wildcard {
		repo = strings.Replace(repo, "*", "", -1)
	}

	for i := len(d.Repos) - 1; i >= 0; i-- {
		if wildcard && strings.Contains(d.Repos[i].Name(), repo) || d.Repos[i].Name() == repo {
			d.Repos = append(d.Repos[:i], d.Repos[i+1:]...)
		}
	}
}

func GetOverrides(overrides string) map[string]string {
	o := make(map[string]string)

	if overrides != "" {
		for _, data := range strings.Split(overrides, ",") {
			x := strings.Split(data, ":")
			if len(data) > 1 {
				// name = repo branch
				o[x[0]] = x[1]
			}
		}
	}

	return o
}

// GenerateOutput creates a file from the definition and template.yml
func (d *Definition) GenerateOutput(output string) error {
	tpl, err := LoadTemplate(d.Template)
	if err != nil {
		return err
	}

	err = d.ParseRepos()
	if err != nil {
		return err
	}

	tpl.Version = "2"

	for _, repo := range d.Repos {
		tpl.Services[repo.Name()] = copyRepo(repo)

		// clean map of any unsupported field
		delete(tpl.Services[repo.Name()], "name")
		delete(tpl.Services[repo.Name()], "path")
		delete(tpl.Services[repo.Name()], "branch")
		if d.Release.Version != "" {
			delete(tpl.Services[repo.Name()], "build")
		}
	}

	err = tpl.WriteFile(output)
	if err != nil {
		return err
	}

	return nil
}

func (d *Definition) ParseRepos() error {
	for i := range d.Repos {
		var image string

		r := git.Repo{
			Repo:        d.Repos[i].Name(),
			Destination: d.BuildPath,
		}

		commit, err := r.CommitID()
		if err != nil {
			return err
		}

		if d.Release.Version != "" {
			image = fmt.Sprintf("%s/%s/%s:%s", d.Release.Registry, d.Release.Org, d.Repos[i].Name(), d.Release.Version)
		} else {
			image = fmt.Sprintf("%s:%s", d.Repos[i].Name(), commit)
		}

		d.Repos[i]["image"] = image
		d.Repos[i]["build"] = r.DeployPath()
	}

	return nil
}

func copyRepo(r Repo) Repo {
	rx := make(Repo)

	for k, v := range r {
		rx[k] = v
	}

	return rx
}
<commit_msg>Add support for third party registries<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package yaml

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/r3labs/composable/git"

	"gopkg.in/yaml.v2"
)

// Definition of repos
type Definition struct {
	Release struct {
		Version  string
		Org      string
		Registry string
	}
	Template  string
	BuildPath string
	Repos     []Repo `yaml:"repos"`
}

// LoadDefiniton the input definition
func LoadDefinition(path string) (*Definition, error) {
	var d Definition

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return &d, err
	}

	err = yaml.Unmarshal(data, &d)
	if err != nil {
		return &d, err
	}

	return &d, nil
}

func (d *Definition) Environment(environment string) {
	if environment != "" {
		envs := strings.Split(environment, ",")
		for _, repo := range d.Repos {
			for _, env := range envs {
				e := strings.Split(env, "=")
				repo.SetEnv(e[0], e[1])
			}
		}
	}
}

func (d *Definition) Overrides(overrides, excludes, global string) {
	// Ommit/Exclude repos
	for _, repo := range strings.Split(excludes, ",") {
		d.ExcludeRepo(repo)
	}

	// Override branches
	if global != "" {
		for _, repo := range d.Repos {
			d.OverrideBranch(repo.Name(), global)
		}
	}

	for repo, branch := range GetOverrides(overrides) {
		d.OverrideBranch(repo, branch)
	}
}

// OverrideBranch updates a repo's branch
func (d *Definition) OverrideBranch(repo, branch string) {
	for i := 0; i < len(d.Repos); i++ {
		if d.Repos[i].Name() == repo {
			d.Repos[i].SetBranch(branch)
		}
	}
}

// ExcludeRepo removes a repo from a list based on name
func (d *Definition) ExcludeRepo(repo string) {
	wildcard := strings.Contains(repo, "*")
	if wildcard {
		repo = strings.Replace(repo, "*", "", -1)
	}

	for i := len(d.Repos) - 1; i >= 0; i-- {
		if wildcard && strings.Contains(d.Repos[i].Name(), repo) || d.Repos[i].Name() == repo {
			d.Repos = append(d.Repos[:i], d.Repos[i+1:]...)
		}
	}
}

func GetOverrides(overrides string) map[string]string {
	o := make(map[string]string)

	if overrides != "" {
		for _, data := range strings.Split(overrides, ",") {
			x := strings.Split(data, ":")
			if len(data) > 1 {
				// name = repo branch
				o[x[0]] = x[1]
			}
		}
	}

	return o
}

// GenerateOutput creates a file from the definition and template.yml
func (d *Definition) GenerateOutput(output string) error {
	tpl, err := LoadTemplate(d.Template)
	if err != nil {
		return err
	}

	err = d.ParseRepos()
	if err != nil {
		return err
	}

	tpl.Version = "2"

	for _, repo := range d.Repos {
		tpl.Services[repo.Name()] = copyRepo(repo)

		// clean map of any unsupported field
		delete(tpl.Services[repo.Name()], "name")
		delete(tpl.Services[repo.Name()], "path")
		delete(tpl.Services[repo.Name()], "branch")
		if d.Release.Version != "" {
			delete(tpl.Services[repo.Name()], "build")
		}
	}

	err = tpl.WriteFile(output)
	if err != nil {
		return err
	}

	return nil
}

func (d *Definition) ParseRepos() error {
	for i := range d.Repos {
		var image string

		r := git.Repo{
			Repo:        d.Repos[i].Name(),
			Destination: d.BuildPath,
		}

		commit, err := r.CommitID()
		if err != nil {
			return err
		}

		switch {
		case d.Release.Registry != "https://index.docker.io/v1/" && d.Release.Version != "":
			image = fmt.Sprintf("%s/%s/%s:%s", d.Release.Registry, d.Release.Org, d.Repos[i].Name(), d.Release.Version)
		case d.Release.Version != "":
			image = fmt.Sprintf("%s/%s:%s", d.Release.Org, d.Repos[i].Name(), d.Release.Version)
		default:
			image = fmt.Sprintf("%s:%s", d.Repos[i].Name(), commit)
		}

		d.Repos[i]["image"] = image
		d.Repos[i]["build"] = r.DeployPath()
	}

	return nil
}

func copyRepo(r Repo) Repo {
	rx := make(Repo)

	for k, v := range r {
		rx[k] = v
	}

	return rx
}
<|endoftext|>


<commit_before>package pollers

import (
	"github.com/freeformz/shh/mm"
	"runtime"
	"time"
)

type Self struct {
	measurements chan<- *mm.Measurement
	stats        runtime.MemStats
}

func NewSelfPoller(measurements chan<- *mm.Measurement) Self {
	return Self{measurements: measurements, stats: runtime.MemStats{}}
}

// See http://golang.org/pkg/runtime/#MemStats
func (poller Self) Poll(tick time.Time) {
	runtime.ReadMemStats(&poller.stats)

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "alloc", "inuse", "bytes"}, float64(poller.stats.Alloc)} // GUAGE
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "alloc", "bytes"}, poller.stats.TotalAlloc}              // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "sys", "bytes"}, float64(poller.stats.Sys)}              // GUAGE
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "pointer", "lookups"}, poller.stats.Lookups}             // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "mallocs"}, poller.stats.Mallocs}                        // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "frees"}, poller.stats.Frees}                            // COUNTER

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "alloc", "bytes"}, float64(poller.stats.HeapAlloc)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "sys", "bytes"}, float64(poller.stats.HeapSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "idle", "bytes"}, float64(poller.stats.HeapIdle)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "inuse", "bytes"}, float64(poller.stats.HeapInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "released", "bytes"}, float64(poller.stats.HeapReleased)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "objects"}, float64(poller.stats.HeapObjects)}

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "stack", "inuse"}, float64(poller.stats.StackInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "stack", "sys"}, float64(poller.stats.StackSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mspan", "inuse"}, float64(poller.stats.MSpanInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mspan", "sys"}, float64(poller.stats.MSpanSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mcache", "inuse"}, float64(poller.stats.MCacheInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mcache", "sys"}, float64(poller.stats.MCacheSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "buckhash", "sys"}, float64(poller.stats.BuckHashSys)}

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "gc", "next"}, float64(poller.stats.NextGC)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "gc", "pause", "ns"}, poller.stats.PauseTotalNs} // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "gc", "num"}, uint64(poller.stats.NumGC)}        // COUNTER

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "goroutines", "num"}, float64(runtime.NumGoroutine())}
}

func (poller Self) Name() string {
	return "self"
}
<commit_msg>Self poller should tell you the depth of the measurements channel<commit_after>package pollers

import (
	"github.com/freeformz/shh/mm"
	"runtime"
	"time"
)

type Self struct {
	measurements chan<- *mm.Measurement
	stats        runtime.MemStats
}

func NewSelfPoller(measurements chan<- *mm.Measurement) Self {
	return Self{measurements: measurements, stats: runtime.MemStats{}}
}

// See http://golang.org/pkg/runtime/#MemStats
func (poller Self) Poll(tick time.Time) {
	runtime.ReadMemStats(&poller.stats)

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"measurements", "length"}, float64(len(poller.measurements))}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "alloc", "inuse", "bytes"}, float64(poller.stats.Alloc)} // GUAGE
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "alloc", "bytes"}, poller.stats.TotalAlloc}              // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "sys", "bytes"}, float64(poller.stats.Sys)}              // GUAGE
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "pointer", "lookups"}, poller.stats.Lookups}             // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "mallocs"}, poller.stats.Mallocs}                        // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "general", "frees"}, poller.stats.Frees}                            // COUNTER

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "alloc", "bytes"}, float64(poller.stats.HeapAlloc)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "sys", "bytes"}, float64(poller.stats.HeapSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "idle", "bytes"}, float64(poller.stats.HeapIdle)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "inuse", "bytes"}, float64(poller.stats.HeapInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "released", "bytes"}, float64(poller.stats.HeapReleased)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "heap", "objects"}, float64(poller.stats.HeapObjects)}

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "stack", "inuse"}, float64(poller.stats.StackInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "stack", "sys"}, float64(poller.stats.StackSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mspan", "inuse"}, float64(poller.stats.MSpanInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mspan", "sys"}, float64(poller.stats.MSpanSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mcache", "inuse"}, float64(poller.stats.MCacheInuse)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "mcache", "sys"}, float64(poller.stats.MCacheSys)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "buckhash", "sys"}, float64(poller.stats.BuckHashSys)}

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "gc", "next"}, float64(poller.stats.NextGC)}
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "gc", "pause", "ns"}, poller.stats.PauseTotalNs} // COUNTER
	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "gc", "num"}, uint64(poller.stats.NumGC)}        // COUNTER

	poller.measurements <- &mm.Measurement{tick, poller.Name(), []string{"memstats", "goroutines", "num"}, float64(runtime.NumGoroutine())}
}

func (poller Self) Name() string {
	return "self"
}
<|endoftext|>


<commit_before>package yellingService

/* Copyright (C) 2015  Gunjan Patel
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os/exec"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
)

func check(e error) error {
	if e != nil {
		fmt.Println(e)
		return e
	}
	return nil
}

// YellingService provides operations on strings.
type YellingServiceI interface {
	Yell(string) error
}

type YellingService struct{}

func (YellingService) Yell(s string) error {
	if s == "" {
		cmd := exec.Command("say", "It's an empty string dummy!")
		err := cmd.Run()
		check(err)
		return ErrEmpty
	}
	cmd := exec.Command("say", s)
	err := cmd.Run()

	return check(err)
}

func MakeYellEndpoint(svc YellingServiceI) endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (interface{}, error) {
		req := request.(yellRequest)
		e := svc.Yell(req.S)
		return yellResponse{string(e)}, nil
	}
}

func DecodeYellRequest(r *http.Request) (interface{}, error) {
	var request yellRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		return nil, err
	}
	return request, nil
}

func EncodeResponse(w http.ResponseWriter, response interface{}) error {
	return json.NewEncoder(w).Encode(response)
}

type yellRequest struct {
	S string `json:"s"`
}

type yellResponse struct {
	E string `json:"e"`
}

// ErrEmpty is returned when an input string is empty.
var ErrEmpty = errors.New("empty string")
<commit_msg>fix for issue #121<commit_after>package yellingService

/* Copyright (C) 2015  Gunjan Patel
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os/exec"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
)

func check(e error) error {
	if e != nil {
		fmt.Println(e)
		return e
	}
	return nil
}

// YellingService provides operations on strings.
type YellingServiceI interface {
	Yell(string) error
}

type YellingService struct{}

func (YellingService) Yell(s string) error {
	if s == "" {
		cmd := exec.Command("say", "It's an empty string dummy!")
		err := cmd.Run()
		check(err)
		return "", ErrEmpty
	}
	cmd := exec.Command("say", s)
	err := cmd.Run()

	return check(err)
}

func MakeYellEndpoint(svc YellingServiceI) endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (interface{}, error) {
		req := request.(yellRequest)
		e := svc.Yell(req.S)
		return yellResponse{e.Error()}, nil
	}
}

func DecodeYellRequest(r *http.Request) (interface{}, error) {
	var request yellRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		return nil, err
	}
	return request, nil
}

func EncodeResponse(w http.ResponseWriter, response interface{}) error {
	return json.NewEncoder(w).Encode(response)
}

type yellRequest struct {
	S string `json:"s"`
}

type yellResponse struct {
	Err string `json:"err,omitempty"`
}

// ErrEmpty is returned when an input string is empty.
var ErrEmpty = errors.New("empty string")
<|endoftext|>


<commit_before>package main

import "fmt"
import "flag"
import "bytes"
import "errors"
import "net"
import "os"
import "time"
import "strings"

type ppingEvent struct {
    Msg         string
    SeqId       int
    EventTime   time.Time
}

type ppingInfo struct {
    SentTime        time.Time
    ReceivedTime    time.Time
    Lost            bool
    Handled         bool
}

var VersionFlag     bool
var HostFlag        string
var SourceFlag      string
var NumberFlag      int
var IntervalFlag    int
var TtlFlag         int
var TimeoutFlag     int
var SizeFlag        int
var Ip6Flag         bool
var Ip6IfFlag       string
var HelpFlag        bool
var InteractiveFlag bool

var LocalAddr       string
var RemoteAddr      string

func init() {
    flag.BoolVar(&InteractiveFlag,  "interactive",  false, "Enable interactive mode")
    flag.BoolVar(&VersionFlag,  "version",  false, "Show version")
    flag.StringVar(&HostFlag,   "host",     "", "Target")
    flag.IntVar(&TimeoutFlag,   "timeout",  5000, "Timeout")
    flag.IntVar(&NumberFlag,    "number",   5, "Number of packets to send")
    flag.IntVar(&IntervalFlag,  "interval", 100, "Send packet interval in millisecond")
    flag.IntVar(&SizeFlag,      "size",     56,"Size of the icmp body in octets")
    flag.BoolVar(&Ip6Flag,      "ipv6",     false, "Enable version 6 icmp")
    flag.StringVar(&Ip6IfFlag,  "ipv6-if",  "", "Required if host is an ipv6 link-local address")
    flag.BoolVar(&HelpFlag,     "help",    false, "Arguments description")
}

func main() {
    parseFlags()
    timeout  := time.Duration(TimeoutFlag)  * time.Millisecond
    interval := time.Duration(IntervalFlag) * time.Millisecond

    var err         error
    var conn        net.Conn
    var ipVersion   int
    var host        string

    testip := net.ParseIP(HostFlag)

    if testip.To4 == nil {
        ipVersion   = 6
        if Ip6IfFlag != "" {
            host = strings.Join([]string{HostFlag,Ip6IfFlag}, "%")
        } else {
            host = HostFlag
        }
        conn, err   = net.Dial("ip6:58", host)
    } else {
        if Ip6Flag == true {
            fmt.Println("Host is not a valid version 6 IP, when it is required")
            os.Exit(1)
        }
        ipVersion   = 4
        host = HostFlag
        conn, err   = net.Dial("ip4:1", host)
    }
    if err != nil { fmt.Println(err); os.Exit(1) }
    LocalAddr   = conn.LocalAddr().String()
    RemoteAddr  = conn.RemoteAddr().String()

    procId := os.Getpid()&0xffff

    var eventchan chan ppingEvent = make(chan ppingEvent)
    go icmpEchoReceiver(conn,eventchan,ipVersion,procId)
    go icmpEchoSender(conn,eventchan,ipVersion,procId,interval,timeout)

    var counter int = NumberFlag
    var eventMsg ppingEvent

    evalReturns := make(map[int]*ppingInfo)
    for i := 0; i < NumberFlag; i++ {
        evalReturns[i + 1] = new(ppingInfo)
    }

    for {
        eventMsg = <- eventchan
        if eventMsg.Msg == "Sent: " {
            evalReturns[eventMsg.SeqId].SentTime = eventMsg.EventTime
        } else if eventMsg.Msg == "Received: " {
            if evalReturns[eventMsg.SeqId].Handled == true {
                continue
            }
            evalReturns[eventMsg.SeqId].ReceivedTime = eventMsg.EventTime
            evalReturns[eventMsg.SeqId].Handled = true
            counter -= 1
            if InteractiveFlag {
                fmt.Println(SizeFlag,
                    "bytes from", RemoteAddr,
                    ": icmp_seq=", eventMsg.SeqId)
            }

        } else if eventMsg.Msg == "Expired: " {
            if evalReturns[eventMsg.SeqId].Handled == true {
                continue
            }
            evalReturns[eventMsg.SeqId].Lost    = true
            evalReturns[eventMsg.SeqId].Handled = true
            counter -= 1
        }
        if counter == 0 {
            break
        }
    }

    conn.Close()
    var packetsLost     int   = 0
    var maxDuration     int64 = 0
    var minDuration     int64 = 0
    var totalDuration   int64 = 0

    for _, val := range evalReturns {
        if val.Lost == true {
            packetsLost ++
            continue
        }
        travelTime := val.ReceivedTime.Sub(val.SentTime).Nanoseconds()
        if maxDuration == 0 || maxDuration < travelTime {
            maxDuration = travelTime
        }
        if minDuration == 0 || minDuration > travelTime {
            minDuration = travelTime
        }
        totalDuration += travelTime
    }

    var packetsArrived  int
    var averageDuration int64 = 0
    var percentPktsLost int

    packetsArrived = NumberFlag - packetsLost

    if totalDuration == 0 || packetsArrived == 0 {
        averageDuration = 0
    } else {
        averageDuration = totalDuration / int64(packetsArrived)
    }

    if NumberFlag == packetsLost {
        percentPktsLost = 100
    } else {
        percentPktsLost = (100 / NumberFlag) * packetsLost
    }

    if InteractiveFlag {
        fmt.Println("---", RemoteAddr, "ping statistics ---")
        fmt.Println(
            NumberFlag, "packets transmitted,",
            packetsArrived , "received,",
            percentPktsLost, "% packet loss")
        fmt.Println("rtt min/avg/max = ",
            minDuration/1000000, "/",
            averageDuration/1000000, "/",
            maxDuration/1000000, " ms")

    } else {
        fmt.Printf("<PPING_RETURN>,%d,%d,%d,%d",
            percentPktsLost,
            minDuration/1000000,
            averageDuration/1000000,
            maxDuration/1000000)
    }

    os.Exit(0)
}

func icmpEchoSender(
    conn        net.Conn,
    eventchan   chan ppingEvent,
    ipVersion   int,
    procId      int,
    interval    time.Duration,
    timeout     time.Duration) {
    var err error

    var requestCode int
    if ipVersion == 4 {
        requestCode = icmpv4EchoRequest
    } else if ipVersion == 6 {
        requestCode = icmpv6EchoRequest
    }
    for i := 0; i < NumberFlag; i++ {
        seqId := i + 1
        pdu, _ := (&icmpMessage{
            Type: requestCode,
            Code: 0,
            Body: &icmpBody{
                ID:     procId,
                Seq:    seqId,
                Data:   bytes.Repeat([]byte("n"), SizeFlag),
            },
        }).Encode(conn)
        _, err = conn.Write(pdu)
        if err != nil { fmt.Println(err); os.Exit(1) }
        eventchan <- ppingEvent{Msg: "Sent: ", SeqId: seqId, EventTime: time.Now()}
        go icmpExpireIn(timeout, seqId, eventchan)
        time.Sleep(interval)
    }
}

func icmpExpireIn(timeout time.Duration, seqId int, eventchan chan ppingEvent) {
    time.Sleep(timeout)
    eventchan <- ppingEvent{Msg: "Expired: ", SeqId: seqId}
}

func icmpEchoReceiver(
        conn        net.Conn,
        eventchan   chan ppingEvent,
        ipVersion   int,
        procId      int) {

    icmpBuffer := make([]byte, icmpPacketMaxSize)

    var seqId   int
    var reply   *icmpMessage
    var icmpPdu []byte
    var read    int
    var ip4Ver  byte
    var err     error
    var counter int = NumberFlag

    // WTF
    // for an unknown reason, conn.Read on version 6 skipp the v6
    // header.
    if ipVersion == 6 {
        for
        {
            read, err = conn.Read(icmpBuffer)

            if icmpBuffer[0] != byte(icmpv6EchoReply) {
                continue
            }

            reply, err = parseICMPMessage(icmpBuffer)
            // TODO validate checksum
            if err != nil { continue }
            if reply.Type == icmpv4EchoRequest { continue }
            if reply.Type == icmpv6EchoRequest { continue }

            body := reply.Body
            if body.GetProcID() != procId { continue }

            seqId = body.GetSequenceID()
            eventchan <- ppingEvent{Msg: "Received: ", SeqId: seqId, EventTime: time.Now()}
            counter -= 1
            if counter == 0 { break }
        }
    } else if ipVersion == 4 {
        for {
            read, err = conn.Read(icmpBuffer)
            if err != nil { break }

            ip4Ver = icmpBuffer[0]>>4
            if ip4Ver != 4 {
                continue
            }
            icmpPdu = icmpBuffer[20:read]

            reply, err = parseICMPMessage(icmpPdu)
            if err != nil { continue }
            if reply.Type == icmpv4EchoRequest { continue }
            if reply.Type == icmpv6EchoRequest { continue }

            body := reply.Body
            if body.GetProcID() != procId { continue }

            seqId = body.GetSequenceID()
            eventchan <- ppingEvent{Msg: "Received: ", SeqId: seqId, EventTime: time.Now()}
            counter -= 1
            if counter == 0 { break }
        }
    }
}

func computeComplementSum(bytes []byte) uint32 {
    checksumLen := len(bytes) - 1
    sum := uint32(0)
    for i := 0; i < checksumLen; i+=2 {
        sum += uint32(bytes[i+1])<<8 | uint32(bytes[i])
    }

    if checksumLen&1 == 0 { sum += uint32(bytes[checksumLen]) }

    sum = sum>>16 + sum&0xffff
    sum = sum + sum>>16
    return sum
}

/* ICMP PART */
const (
    icmpv4EchoRequest = 8
    icmpv4EchoReply   = 0
    icmpv6EchoRequest = 128
    icmpv6EchoReply   = 129
    icmpPacketMaxSize = 65535
)

type icmpMessage struct {
    Type     int             // type
    Code     int             // code
    Checksum int             // checksum
    Body     icmpMessageBody // body
}

type icmpMessageBody interface {
    Len()           int
    Encode()        ([]byte, error)
    GetProcID()     int
    GetSequenceID() int
}

func (msg *icmpMessage) Encode(conn net.Conn) ([]byte, error) {
    var pdu []byte
    if msg.Type == icmpv4EchoRequest {
        pdu = []byte{byte(msg.Type), byte(msg.Code), 0, 0}
        if msg.Body != nil && msg.Body.Len() != 0 {
            messageBody, err := msg.Body.Encode()
            if err != nil { return nil, err }
            pdu = append(pdu, messageBody...)
        }
        s := computeComplementSum(pdu)

        // Place checksum back in header; using ^= avoids the
        // assumption the checksum pdu are zero.
        pdu[2] ^= byte(^s & 0xff)
        pdu[3] ^= byte(^s >> 8)
    } else if msg.Type == icmpv6EchoRequest {
        // build icmp pdu in pdu var
        pdu = []byte{byte(msg.Type), byte(msg.Code), 0, 0}
        if msg.Body != nil && msg.Body.Len() != 0 {
            messageBody, err := msg.Body.Encode()
            if err != nil { return nil, err }
            pdu = append(pdu, messageBody...)
        }

        // BUILD PSEUDO HEADER
        var pseudoHeader []byte
        // append source and dest address
        var localAdd     []byte
        var remoteAdd    []byte
        localAdd    = net.ParseIP(LocalAddr)
        remoteAdd   = net.ParseIP(RemoteAddr)
        pseudoHeader = append(localAdd, remoteAdd...)
        // append ICMPv6 len of pdu
        // !!WTF how can this work when only specifying the last byte?
        pduLenHead := []byte{0,0,0,byte(len(pdu))}
        pseudoHeader = append(pseudoHeader, pduLenHead...)
        // append Zero and Next header(58) field
        var lastHeaderField []byte
        lastHeaderField = []byte{0,0,0,58}
        pseudoHeader = append(pseudoHeader, lastHeaderField...)
        // append icmp packet
        pseudoHeader = append(pseudoHeader, pdu...)
        // compute checksum
        s := computeComplementSum(pseudoHeader)

        // Place checksum in pdu
        pdu[2] ^= byte(^s & 0xff)
        pdu[3] ^= byte(^s >> 8)
    }
    return pdu, nil
}

// parseICMPMessage parses b as an ICMP message.
func parseICMPMessage(b []byte) (*icmpMessage, error) {
    msglen := len(b)
    if msglen < 4 {
        return nil, errors.New("message too short")
    }
    m := &icmpMessage{
        Type: int(b[0]),
        Code: int(b[1]),
        Checksum: int(b[2])<<8 | int(b[3]) }
    if msglen > 4 {
        var err error
        switch m.Type {
        case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply:
            m.Body, err = parseICMPEcho(b[4:])
            if err != nil {
                return nil, err
            }
        }
    }
    return m, nil
}

// parseICMPEcho parses b as an ICMP echo request or reply message body.
func parseICMPEcho(b []byte) (*icmpBody, error) {
    bodylen := len(b)
    p := &icmpBody{
        ID:  int(b[0])<<8 | int(b[1]),
        Seq: int(b[2])<<8 | int(b[3]) }
    if bodylen > 4 {
        p.Data = make([]byte, bodylen-4)
        copy(p.Data, b[4:])
    }
    return p, nil
}

// imcpBody represenets an ICMP echo request or reply message body.
type icmpBody struct {
    ID   int    // identifier
    Seq  int    // sequence number
    Data []byte // data
}

func (p *icmpBody) GetProcID()      int { return p.ID }
func (p *icmpBody) GetSequenceID()  int { return p.Seq }
func (p *icmpBody) Len() int {
    if p == nil { return 0 }
    return 4 + len(p.Data)
}
func (p *icmpBody) Encode()         ([]byte, error) {
    // Encode returns the binary enconding of the ICMP echo request or
    // reply message body p.
    b := make([]byte, 4+len(p.Data))
    b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff)
    b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff)
    copy(b[4:], p.Data)
    return b, nil
}

func parseFlags() {
    flag.Parse()
    if VersionFlag == true {
        printVersion()
        os.Exit(0)
    } else if HostFlag == "" { // MINIMUM MANDATORY
        printHelp()
        os.Exit(2)
    }
}

func printHelp() {
    flag.PrintDefaults()
    fmt.Println("pping return a comma separated value meaning:")
    fmt.Println("<PPING_RETURN>,percent_pkts_loss,min_reply_duration,avg_reply_duration,max_reply_duration")
}
func printVersion() { fmt.Printf("go_check_icmp V0.1\n") }
<commit_msg>pping.go cleanup<commit_after>/*
 * Sysmo NMS Network Management and Monitoring solution (http://www.sysmo.io)
 *
 * Copyright (c) 2012-2015 Sebastien Serre <ssbx@sysmo.io>
 *
 * This file is part of Sysmo NMS.
 *
 * Sysmo NMS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Sysmo NMS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Sysmo.  If not, see <http://www.gnu.org/licenses/>.
 */
package main

import "fmt"
import "flag"
import "bytes"
import "errors"
import "net"
import "os"
import "time"
import "strings"

type ppingEvent struct {
    Msg         string
    SeqId       int
    EventTime   time.Time
}

type ppingInfo struct {
    SentTime        time.Time
    ReceivedTime    time.Time
    Lost            bool
    Handled         bool
}

var VersionFlag     bool
var HostFlag        string
var SourceFlag      string
var NumberFlag      int
var IntervalFlag    int
var TtlFlag         int
var TimeoutFlag     int
var SizeFlag        int
var Ip6Flag         bool
var Ip6IfFlag       string
var HelpFlag        bool
var InteractiveFlag bool

var LocalAddr       string
var RemoteAddr      string

func init() {
    flag.BoolVar(&InteractiveFlag, "interactive", false,
                                "Enable interactive mode")
    flag.BoolVar(&VersionFlag, "version", false, "Show version")
    flag.StringVar(&HostFlag, "host", "", "Target")
    flag.IntVar(&TimeoutFlag, "timeout", 5000, "Timeout")
    flag.IntVar(&NumberFlag, "number", 5, "Number of packets to send")
    flag.IntVar(&IntervalFlag, "interval", 100,
                                "Send packet interval in millisecond")
    flag.IntVar(&SizeFlag, "size", 56,"Size of the icmp body in octets")
    flag.BoolVar(&Ip6Flag, "ipv6", false, "Enable version 6 icmp")
    flag.StringVar(&Ip6IfFlag, "ipv6-if", "",
                               "Required if host is an ipv6 link-local address")
    flag.BoolVar(&HelpFlag, "help", false, "Arguments description")
}

func main() {
    parseFlags()
    timeout  := time.Duration(TimeoutFlag)  * time.Millisecond
    interval := time.Duration(IntervalFlag) * time.Millisecond

    var err         error
    var conn        net.Conn
    var ipVersion   int
    var host        string

    testip := net.ParseIP(HostFlag)

    if testip.To4 == nil {
        ipVersion   = 6
        if Ip6IfFlag != "" {
            host = strings.Join([]string{HostFlag,Ip6IfFlag}, "%")
        } else {
            host = HostFlag
        }
        conn, err   = net.Dial("ip6:58", host)
    } else {
        if Ip6Flag == true {
            fmt.Println(
            "Host is not a valid version 6 IP, when it is required")
            os.Exit(1)
        }
        ipVersion   = 4
        host = HostFlag
        conn, err   = net.Dial("ip4:1", host)
    }
    if err != nil { fmt.Println(err, " when host is: ", host); os.Exit(1) }
    LocalAddr   = conn.LocalAddr().String()
    RemoteAddr  = conn.RemoteAddr().String()

    procId := os.Getpid()&0xffff

    var eventchan chan ppingEvent = make(chan ppingEvent)
    go icmpEchoReceiver(conn,eventchan,ipVersion,procId)
    go icmpEchoSender(conn,eventchan,ipVersion,procId,interval,timeout)

    var counter int = NumberFlag
    var eventMsg ppingEvent

    evalReturns := make(map[int]*ppingInfo)
    for i := 0; i < NumberFlag; i++ {
        evalReturns[i + 1] = new(ppingInfo)
    }

    for {
        eventMsg = <- eventchan
        if eventMsg.Msg == "Sent: " {
            evalReturns[eventMsg.SeqId].SentTime = eventMsg.EventTime
        } else if eventMsg.Msg == "Received: " {
            if evalReturns[eventMsg.SeqId].Handled == true {
                continue
            }
            evalReturns[eventMsg.SeqId].ReceivedTime = eventMsg.EventTime
            evalReturns[eventMsg.SeqId].Handled = true
            counter -= 1
            if InteractiveFlag {
                fmt.Println(SizeFlag,
                    "bytes from", RemoteAddr,
                    ": icmp_seq=", eventMsg.SeqId)
            }

        } else if eventMsg.Msg == "Expired: " {
            if evalReturns[eventMsg.SeqId].Handled == true {
                continue
            }
            evalReturns[eventMsg.SeqId].Lost    = true
            evalReturns[eventMsg.SeqId].Handled = true
            counter -= 1
        }
        if counter == 0 {
            break
        }
    }

    conn.Close()
    var packetsLost     int   = 0
    var maxDuration     int64 = 0
    var minDuration     int64 = 0
    var totalDuration   int64 = 0

    for _, val := range evalReturns {
        if val.Lost == true {
            packetsLost ++
            continue
        }
        travelTime := val.ReceivedTime.Sub(val.SentTime).Nanoseconds()
        if maxDuration == 0 || maxDuration < travelTime {
            maxDuration = travelTime
        }
        if minDuration == 0 || minDuration > travelTime {
            minDuration = travelTime
        }
        totalDuration += travelTime
    }

    var packetsArrived  int
    var averageDuration int64 = 0
    var percentPktsLost int

    packetsArrived = NumberFlag - packetsLost

    if totalDuration == 0 || packetsArrived == 0 {
        averageDuration = 0
    } else {
        averageDuration = totalDuration / int64(packetsArrived)
    }

    if NumberFlag == packetsLost {
        percentPktsLost = 100
    } else {
        percentPktsLost = (100 / NumberFlag) * packetsLost
    }

    if InteractiveFlag {
        fmt.Println("---", RemoteAddr, "ping statistics ---")
        fmt.Println(
            NumberFlag, "packets transmitted,",
            packetsArrived , "received,",
            percentPktsLost, "% packet loss")
        fmt.Println("rtt min/avg/max = ",
            minDuration/1000000, "/",
            averageDuration/1000000, "/",
            maxDuration/1000000, " ms")

    } else {
        fmt.Printf("<PPING_RETURN>,%d,%d,%d,%d",
            percentPktsLost,
            minDuration/1000000,
            averageDuration/1000000,
            maxDuration/1000000)
    }

    os.Exit(0)
}

func icmpEchoSender(
    conn        net.Conn,
    eventchan   chan ppingEvent,
    ipVersion   int,
    procId      int,
    interval    time.Duration,
    timeout     time.Duration) {
    var err error

    var requestCode int
    if ipVersion == 4 {
        requestCode = icmpv4EchoRequest
    } else if ipVersion == 6 {
        requestCode = icmpv6EchoRequest
    }
    for i := 0; i < NumberFlag; i++ {
        seqId := i + 1
        pdu, _ := (&icmpMessage{
            Type: requestCode,
            Code: 0,
            Body: &icmpBody{
                ID:     procId,
                Seq:    seqId,
                Data:   bytes.Repeat([]byte("n"), SizeFlag),
            },
        }).Encode(conn)
        _, err = conn.Write(pdu)
        if err != nil { fmt.Println(err); os.Exit(1) }
        eventchan <-
            ppingEvent{Msg: "Sent: ", SeqId: seqId, EventTime: time.Now()}
        go icmpExpireIn(timeout, seqId, eventchan)
        time.Sleep(interval)
    }
}

func icmpExpireIn(timeout time.Duration, seqId int, eventchan chan ppingEvent) {
    time.Sleep(timeout)
    eventchan <- ppingEvent{Msg: "Expired: ", SeqId: seqId}
}

func icmpEchoReceiver(
        conn        net.Conn,
        eventchan   chan ppingEvent,
        ipVersion   int,
        procId      int) {

    icmpBuffer := make([]byte, icmpPacketMaxSize)

    var seqId   int
    var reply   *icmpMessage
    var icmpPdu []byte
    var read    int
    var ip4Ver  byte
    var err     error
    var counter int = NumberFlag

    // WTF
    // for an unknown reason, conn.Read on version 6 skipp the v6
    // header.
    if ipVersion == 6 {
        for
        {
            read, err = conn.Read(icmpBuffer)

            if icmpBuffer[0] != byte(icmpv6EchoReply) {
                continue
            }

            reply, err = parseICMPMessage(icmpBuffer)
            // TODO validate checksum
            if err != nil { continue }
            if reply.Type == icmpv4EchoRequest { continue }
            if reply.Type == icmpv6EchoRequest { continue }

            body := reply.Body
            if body.GetProcID() != procId { continue }

            seqId = body.GetSequenceID()
            eventchan <-
              ppingEvent{Msg: "Received: ", SeqId: seqId, EventTime: time.Now()}
            counter -= 1
            if counter == 0 { break }
        }
    } else if ipVersion == 4 {
        for {
            read, err = conn.Read(icmpBuffer)
            if err != nil { break }

            ip4Ver = icmpBuffer[0]>>4
            if ip4Ver != 4 {
                continue
            }
            icmpPdu = icmpBuffer[20:read]

            reply, err = parseICMPMessage(icmpPdu)
            if err != nil { continue }
            if reply.Type == icmpv4EchoRequest { continue }
            if reply.Type == icmpv6EchoRequest { continue }

            body := reply.Body
            if body.GetProcID() != procId { continue }

            seqId = body.GetSequenceID()
            eventchan <-
              ppingEvent{Msg: "Received: ", SeqId: seqId, EventTime: time.Now()}
            counter -= 1
            if counter == 0 { break }
        }
    }
}

func computeComplementSum(bytes []byte) uint32 {
    checksumLen := len(bytes) - 1
    sum := uint32(0)
    for i := 0; i < checksumLen; i+=2 {
        sum += uint32(bytes[i+1])<<8 | uint32(bytes[i])
    }

    if checksumLen&1 == 0 { sum += uint32(bytes[checksumLen]) }

    sum = sum>>16 + sum&0xffff
    sum = sum + sum>>16
    return sum
}

/* ICMP PART */
const (
    icmpv4EchoRequest = 8
    icmpv4EchoReply   = 0
    icmpv6EchoRequest = 128
    icmpv6EchoReply   = 129
    icmpPacketMaxSize = 65535
)

type icmpMessage struct {
    Type     int             // type
    Code     int             // code
    Checksum int             // checksum
    Body     icmpMessageBody // body
}

type icmpMessageBody interface {
    Len()           int
    Encode()        ([]byte, error)
    GetProcID()     int
    GetSequenceID() int
}

func (msg *icmpMessage) Encode(conn net.Conn) ([]byte, error) {
    var pdu []byte
    if msg.Type == icmpv4EchoRequest {
        pdu = []byte{byte(msg.Type), byte(msg.Code), 0, 0}
        if msg.Body != nil && msg.Body.Len() != 0 {
            messageBody, err := msg.Body.Encode()
            if err != nil { return nil, err }
            pdu = append(pdu, messageBody...)
        }
        s := computeComplementSum(pdu)

        // Place checksum back in header; using ^= avoids the
        // assumption the checksum pdu are zero.
        pdu[2] ^= byte(^s & 0xff)
        pdu[3] ^= byte(^s >> 8)
    } else if msg.Type == icmpv6EchoRequest {
        // build icmp pdu in pdu var
        pdu = []byte{byte(msg.Type), byte(msg.Code), 0, 0}
        if msg.Body != nil && msg.Body.Len() != 0 {
            messageBody, err := msg.Body.Encode()
            if err != nil { return nil, err }
            pdu = append(pdu, messageBody...)
        }

        // BUILD PSEUDO HEADER
        var pseudoHeader []byte
        // append source and dest address
        var localAdd     []byte
        var remoteAdd    []byte
        localAdd    = net.ParseIP(LocalAddr)
        remoteAdd   = net.ParseIP(RemoteAddr)
        pseudoHeader = append(localAdd, remoteAdd...)
        // append ICMPv6 len of pdu
        // !!WTF how can this work when only specifying the last byte?
        pduLenHead := []byte{0,0,0,byte(len(pdu))}
        pseudoHeader = append(pseudoHeader, pduLenHead...)
        // append Zero and Next header(58) field
        var lastHeaderField []byte
        lastHeaderField = []byte{0,0,0,58}
        pseudoHeader = append(pseudoHeader, lastHeaderField...)
        // append icmp packet
        pseudoHeader = append(pseudoHeader, pdu...)
        // compute checksum
        s := computeComplementSum(pseudoHeader)

        // Place checksum in pdu
        pdu[2] ^= byte(^s & 0xff)
        pdu[3] ^= byte(^s >> 8)
    }
    return pdu, nil
}

// parseICMPMessage parses b as an ICMP message.
func parseICMPMessage(b []byte) (*icmpMessage, error) {
    msglen := len(b)
    if msglen < 4 {
        return nil, errors.New("message too short")
    }
    m := &icmpMessage{
        Type: int(b[0]),
        Code: int(b[1]),
        Checksum: int(b[2])<<8 | int(b[3]) }
    if msglen > 4 {
        var err error
        switch m.Type {
        case icmpv4EchoRequest, icmpv4EchoReply,
             icmpv6EchoRequest, icmpv6EchoReply:
            m.Body, err = parseICMPEcho(b[4:])
            if err != nil {
                return nil, err
            }
        }
    }
    return m, nil
}

// parseICMPEcho parses b as an ICMP echo request or reply message body.
func parseICMPEcho(b []byte) (*icmpBody, error) {
    bodylen := len(b)
    p := &icmpBody{
        ID:  int(b[0])<<8 | int(b[1]),
        Seq: int(b[2])<<8 | int(b[3]) }
    if bodylen > 4 {
        p.Data = make([]byte, bodylen-4)
        copy(p.Data, b[4:])
    }
    return p, nil
}

// imcpBody represenets an ICMP echo request or reply message body.
type icmpBody struct {
    ID   int    // identifier
    Seq  int    // sequence number
    Data []byte // data
}

func (p *icmpBody) GetProcID()      int { return p.ID }
func (p *icmpBody) GetSequenceID()  int { return p.Seq }
func (p *icmpBody) Len() int {
    if p == nil { return 0 }
    return 4 + len(p.Data)
}
func (p *icmpBody) Encode()         ([]byte, error) {
    // Encode returns the binary enconding of the ICMP echo request or
    // reply message body p.
    b := make([]byte, 4+len(p.Data))
    b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff)
    b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff)
    copy(b[4:], p.Data)
    return b, nil
}

func parseFlags() {
    flag.Parse()
    if VersionFlag == true {
        printVersion()
        os.Exit(0)
    } else if HostFlag == "" { // MINIMUM MANDATORY
        printHelp()
        os.Exit(2)
    }
}

func printHelp() {
    flag.PrintDefaults()
    fmt.Println("pping return a comma separated value meaning:")
    fmt.Println("<PPING_RETURN>,percent_pkts_loss,min_reply_duration,avg_reply_duration,max_reply_duration")
}
func printVersion() { fmt.Printf("go_check_icmp V0.1\n") }
<|endoftext|>


<commit_before>package helpers

import "fmt"

const (
	// Major and minor version.
	PrestVersionNumber = 0.1

	// Increment this for bug releases
	PrestPatchVersion = 7
)

var CommitHash string

// PrestReleaseVersion is same as pREST Version.
func PrestReleaseVersion() string {
	return fmt.Sprintf("%.2g.%d", PrestVersionNumber, PrestPatchVersion)
}
<commit_msg>update version<commit_after>package helpers

import (
	"fmt"
)

const (
	// PrestVersionNumber Major and minor version.
	PrestVersionNumber = 0.3

	// PrestPatchVersion Increment this for bug releases
	PrestPatchVersion = 0
)

var (
	// CommitHash for version
	CommitHash string
)

// PrestReleaseVersion is same as pREST Version.
func PrestReleaseVersion() string {
	return fmt.Sprintf("%.2g.%d", PrestVersionNumber, PrestPatchVersion)
}
<|endoftext|>