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

package ogletest

import (
	"bytes"
	"flag"
	"fmt"
	"path"
	"regexp"
	"runtime"
	"sync"
	"testing"
	"time"
)

var testFilter = flag.String("ogletest.run", "", "Regexp for matching tests to run.")

// runTestsOnce protects RunTests from executing multiple times.
var runTestsOnce sync.Once

func isAssertThatError(x interface{}) bool {
	_, ok := x.(*assertThatError)
	return ok
}

// Run a single test function, returning a slice of failure records.
func runTestFunction(tf TestFunction) (failures []*failureRecord) {
	// Set up a clean slate for this test. Make sure to reset it after everything
	// below is finished, so we don't accidentally use it elsewhere.
	currentlyRunningTest = newTestInfo()
	defer func() {
		currentlyRunningTest = nil
	}()

	// Run the SetUp function, if any, paying attention to whether it panics.
	setUpPanicked := false
	if tf.SetUp != nil {
		setUpPanicked = runWithProtection(func() { tf.SetUp(currentlyRunningTest) })
	}

	// Run the test function itself, but only if the SetUp function didn't panic.
	// (This includes AssertThat errors.)
	if !setUpPanicked {
		runWithProtection(tf.Run)
	}

	// Run the TearDown function, if any.
	if tf.TearDown != nil {
		runWithProtection(tf.TearDown)
	}

	// Tell the mock controller for the tests to report any errors it's sitting
	// on.
	currentlyRunningTest.MockController.Finish()

	return currentlyRunningTest.failureRecords
}

// Run everything registered with Register (including via the wrapper
// RegisterTestSuite).
//
// Failures are communicated to the supplied testing.T object. This is the
// bridge between ogletest and the testing package (and `go test`); you should
// ensure that it's called at least once by creating a test function compatible
// with `go test` and calling it there.
//
// For example:
//
//     import (
//       "github.com/jacobsa/ogletest"
//       "testing"
//     )
//
//     func TestOgletest(t *testing.T) {
//       ogletest.RunTests(t)
//     }
//
func RunTests(t *testing.T) {
	runTestsOnce.Do(func() { runTestsInternal(t) })
}

// runTestsInternal does the real work of RunTests, which simply wraps it in a
// sync.Once.
func runTestsInternal(t *testing.T) {
	// Process each registered suite.
	for _, suite := range registeredSuites {
		fmt.Printf("[----------] Running tests from %s\n", suite.Name)

		// Run the SetUp function, if any.
		if suite.SetUp != nil {
			suite.SetUp()
		}

		// Run each test function that the user has not told us to skip.
		for _, tf := range filterTestFunctions(suite) {
			// Print a banner for the start of this test function.
			fmt.Printf("[ RUN      ] %s.%s\n", suite.Name, tf.Name)

			// Run the test function.
			startTime := time.Now()
			failures := runTestFunction(tf)
			runDuration := time.Since(startTime)

			// Print any failures, and mark the test as having failed if there are any.
			for _, record := range failures {
				t.Fail()
				userErrorSection := ""
				if record.UserError != "" {
					userErrorSection = record.UserError + "\n"
				}

				fmt.Printf(
					"%s:%d:\n%s\n%s\n",
					record.FileName,
					record.LineNumber,
					record.GeneratedError,
					userErrorSection)
			}

			// Print a banner for the end of the test.
			bannerMessage := "[       OK ]"
			if len(failures) != 0 {
				bannerMessage = "[  FAILED  ]"
			}

			// Print a summary of the time taken, if long enough.
			var timeMessage string
			if runDuration >= 25*time.Millisecond {
				timeMessage = fmt.Sprintf(" (%s)", runDuration.String())
			}

			fmt.Printf(
				"%s %s.%s%s\n",
				bannerMessage,
				suite.Name,
				tf.Name,
				timeMessage)
		}

		// Run the suite's TearDown function, if any.
		if suite.TearDown != nil {
			suite.TearDown()
		}

		fmt.Printf("[----------] Finished with tests from %s\n", suite.Name)
	}
}

// Return true iff the supplied program counter appears to lie within panic().
func isPanic(pc uintptr) bool {
	f := runtime.FuncForPC(pc)
	if f == nil {
		return false
	}

	return f.Name() == "runtime.gopanic" || f.Name() == "runtime.sigpanic"
}

// Find the deepest stack frame containing something that appears to be a
// panic. Return the 'skip' value that a caller to this function would need
// to supply to runtime.Caller for that frame, or a negative number if not found.
func findPanic() int {
	localSkip := -1
	for i := 0; ; i++ {
		// Stop if we've passed the base of the stack.
		pc, _, _, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Is this a panic?
		if isPanic(pc) {
			localSkip = i
		}
	}

	return localSkip - 1
}

// Attempt to find the file base name and line number for the ultimate source
// of a panic, on the panicking stack. Return a human-readable sentinel if
// unsuccessful.
func findPanicFileLine() (string, int) {
	panicSkip := findPanic()
	if panicSkip < 0 {
		return "(unknown)", 0
	}

	// Find the trigger of the panic.
	_, file, line, ok := runtime.Caller(panicSkip + 1)
	if !ok {
		return "(unknown)", 0
	}

	return path.Base(file), line
}

// Run the supplied function, catching panics (including AssertThat errors) and
// reporting them to the currently-running test as appropriate. Return true iff
// the function panicked.
func runWithProtection(f func()) (panicked bool) {
	defer func() {
		// If the test didn't panic, we're done.
		r := recover()
		if r == nil {
			return
		}

		panicked = true

		// We modify the currently running test below.
		currentlyRunningTest.mutex.Lock()
		defer currentlyRunningTest.mutex.Unlock()

		// If the function panicked (and the panic was not due to an AssertThat
		// failure), add a failure for the panic.
		if !isAssertThatError(r) {
			var panicRecord failureRecord
			panicRecord.FileName, panicRecord.LineNumber = findPanicFileLine()
			panicRecord.GeneratedError = fmt.Sprintf(
				"panic: %v\n\n%s", r, formatPanicStack())

			currentlyRunningTest.failureRecords = append(
				currentlyRunningTest.failureRecords,
				&panicRecord)
		}
	}()

	f()
	return
}

func formatPanicStack() string {
	buf := new(bytes.Buffer)

	// Find the panic. If successful, we'll skip to below it. Otherwise, we'll
	// format everything.
	var initialSkip int
	if panicSkip := findPanic(); panicSkip >= 0 {
		initialSkip = panicSkip + 1
	}

	for i := initialSkip; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Choose a function name to display.
		funcName := "(unknown)"
		if f := runtime.FuncForPC(pc); f != nil {
			funcName = f.Name()
		}

		// Stop if we've gotten as far as the test runner code.
		if funcName == "github.com/jacobsa/ogletest.runTestMethod" ||
			funcName == "github.com/jacobsa/ogletest.runWithProtection" {
			break
		}

		// Add an entry for this frame.
		fmt.Fprintf(buf, "%s\n\t%s:%d\n", funcName, file, line)
	}

	return buf.String()
}

// Filter test functions according to the user-supplied filter flag.
func filterTestFunctions(suite TestSuite) (out []TestFunction) {
	for _, tf := range suite.TestFunctions {
		fullName := fmt.Sprintf("%s.%s", suite.Name, tf.Name)
		matched, err := regexp.MatchString(*testFilter, fullName)
		if err != nil {
			panic("Invalid value for --ogletest.run: " + err.Error())
		}

		if !matched {
			continue
		}

		out = append(out, tf)
	}

	return
}
<commit_msg>Pre-compile the test filter.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ogletest

import (
	"bytes"
	"flag"
	"fmt"
	"path"
	"regexp"
	"runtime"
	"sync"
	"testing"
	"time"
)

var testFilter = flag.String("ogletest.run", "", "Regexp for matching tests to run.")

// runTestsOnce protects RunTests from executing multiple times.
var runTestsOnce sync.Once

func isAssertThatError(x interface{}) bool {
	_, ok := x.(*assertThatError)
	return ok
}

// Run a single test function, returning a slice of failure records.
func runTestFunction(tf TestFunction) (failures []*failureRecord) {
	// Set up a clean slate for this test. Make sure to reset it after everything
	// below is finished, so we don't accidentally use it elsewhere.
	currentlyRunningTest = newTestInfo()
	defer func() {
		currentlyRunningTest = nil
	}()

	// Run the SetUp function, if any, paying attention to whether it panics.
	setUpPanicked := false
	if tf.SetUp != nil {
		setUpPanicked = runWithProtection(func() { tf.SetUp(currentlyRunningTest) })
	}

	// Run the test function itself, but only if the SetUp function didn't panic.
	// (This includes AssertThat errors.)
	if !setUpPanicked {
		runWithProtection(tf.Run)
	}

	// Run the TearDown function, if any.
	if tf.TearDown != nil {
		runWithProtection(tf.TearDown)
	}

	// Tell the mock controller for the tests to report any errors it's sitting
	// on.
	currentlyRunningTest.MockController.Finish()

	return currentlyRunningTest.failureRecords
}

// Run everything registered with Register (including via the wrapper
// RegisterTestSuite).
//
// Failures are communicated to the supplied testing.T object. This is the
// bridge between ogletest and the testing package (and `go test`); you should
// ensure that it's called at least once by creating a test function compatible
// with `go test` and calling it there.
//
// For example:
//
//     import (
//       "github.com/jacobsa/ogletest"
//       "testing"
//     )
//
//     func TestOgletest(t *testing.T) {
//       ogletest.RunTests(t)
//     }
//
func RunTests(t *testing.T) {
	runTestsOnce.Do(func() { runTestsInternal(t) })
}

// runTestsInternal does the real work of RunTests, which simply wraps it in a
// sync.Once.
func runTestsInternal(t *testing.T) {
	// Process each registered suite.
	for _, suite := range registeredSuites {
		fmt.Printf("[----------] Running tests from %s\n", suite.Name)

		// Run the SetUp function, if any.
		if suite.SetUp != nil {
			suite.SetUp()
		}

		// Run each test function that the user has not told us to skip.
		for _, tf := range filterTestFunctions(suite) {
			// Print a banner for the start of this test function.
			fmt.Printf("[ RUN      ] %s.%s\n", suite.Name, tf.Name)

			// Run the test function.
			startTime := time.Now()
			failures := runTestFunction(tf)
			runDuration := time.Since(startTime)

			// Print any failures, and mark the test as having failed if there are any.
			for _, record := range failures {
				t.Fail()
				userErrorSection := ""
				if record.UserError != "" {
					userErrorSection = record.UserError + "\n"
				}

				fmt.Printf(
					"%s:%d:\n%s\n%s\n",
					record.FileName,
					record.LineNumber,
					record.GeneratedError,
					userErrorSection)
			}

			// Print a banner for the end of the test.
			bannerMessage := "[       OK ]"
			if len(failures) != 0 {
				bannerMessage = "[  FAILED  ]"
			}

			// Print a summary of the time taken, if long enough.
			var timeMessage string
			if runDuration >= 25*time.Millisecond {
				timeMessage = fmt.Sprintf(" (%s)", runDuration.String())
			}

			fmt.Printf(
				"%s %s.%s%s\n",
				bannerMessage,
				suite.Name,
				tf.Name,
				timeMessage)
		}

		// Run the suite's TearDown function, if any.
		if suite.TearDown != nil {
			suite.TearDown()
		}

		fmt.Printf("[----------] Finished with tests from %s\n", suite.Name)
	}
}

// Return true iff the supplied program counter appears to lie within panic().
func isPanic(pc uintptr) bool {
	f := runtime.FuncForPC(pc)
	if f == nil {
		return false
	}

	return f.Name() == "runtime.gopanic" || f.Name() == "runtime.sigpanic"
}

// Find the deepest stack frame containing something that appears to be a
// panic. Return the 'skip' value that a caller to this function would need
// to supply to runtime.Caller for that frame, or a negative number if not found.
func findPanic() int {
	localSkip := -1
	for i := 0; ; i++ {
		// Stop if we've passed the base of the stack.
		pc, _, _, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Is this a panic?
		if isPanic(pc) {
			localSkip = i
		}
	}

	return localSkip - 1
}

// Attempt to find the file base name and line number for the ultimate source
// of a panic, on the panicking stack. Return a human-readable sentinel if
// unsuccessful.
func findPanicFileLine() (string, int) {
	panicSkip := findPanic()
	if panicSkip < 0 {
		return "(unknown)", 0
	}

	// Find the trigger of the panic.
	_, file, line, ok := runtime.Caller(panicSkip + 1)
	if !ok {
		return "(unknown)", 0
	}

	return path.Base(file), line
}

// Run the supplied function, catching panics (including AssertThat errors) and
// reporting them to the currently-running test as appropriate. Return true iff
// the function panicked.
func runWithProtection(f func()) (panicked bool) {
	defer func() {
		// If the test didn't panic, we're done.
		r := recover()
		if r == nil {
			return
		}

		panicked = true

		// We modify the currently running test below.
		currentlyRunningTest.mutex.Lock()
		defer currentlyRunningTest.mutex.Unlock()

		// If the function panicked (and the panic was not due to an AssertThat
		// failure), add a failure for the panic.
		if !isAssertThatError(r) {
			var panicRecord failureRecord
			panicRecord.FileName, panicRecord.LineNumber = findPanicFileLine()
			panicRecord.GeneratedError = fmt.Sprintf(
				"panic: %v\n\n%s", r, formatPanicStack())

			currentlyRunningTest.failureRecords = append(
				currentlyRunningTest.failureRecords,
				&panicRecord)
		}
	}()

	f()
	return
}

func formatPanicStack() string {
	buf := new(bytes.Buffer)

	// Find the panic. If successful, we'll skip to below it. Otherwise, we'll
	// format everything.
	var initialSkip int
	if panicSkip := findPanic(); panicSkip >= 0 {
		initialSkip = panicSkip + 1
	}

	for i := initialSkip; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Choose a function name to display.
		funcName := "(unknown)"
		if f := runtime.FuncForPC(pc); f != nil {
			funcName = f.Name()
		}

		// Stop if we've gotten as far as the test runner code.
		if funcName == "github.com/jacobsa/ogletest.runTestMethod" ||
			funcName == "github.com/jacobsa/ogletest.runWithProtection" {
			break
		}

		// Add an entry for this frame.
		fmt.Fprintf(buf, "%s\n\t%s:%d\n", funcName, file, line)
	}

	return buf.String()
}

// Filter test functions according to the user-supplied filter flag.
func filterTestFunctions(suite TestSuite) (out []TestFunction) {
	re, err := regexp.Compile(*testFilter)
	if err != nil {
		panic("Invalid value for --ogletest.run: " + err.Error())
	}

	for _, tf := range suite.TestFunctions {
		fullName := fmt.Sprintf("%s.%s", suite.Name, tf.Name)
		if !re.MatchString(fullName) {
			continue
		}

		out = append(out, tf)
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2017 Mathew Robinson <mrobinson@praelatus.io>. All rights reserved.
// Use of this source code is governed by the AGPLv3 license that can be found in
// the LICENSE file.

// Package repo provides definitions for abstracting away database interaction
// in Praelatus
package repo

import (
	"errors"

	"github.com/praelatus/praelatus/models"
)

// Errors
var (
	ErrLoginRequired          = errors.New("you must be logged in")
	ErrAdminRequired          = errors.New("you must be an administrator")
	ErrUnauthorized           = errors.New("permission denied")
	ErrNotFound               = errors.New("not found")
	ErrInvalidTicketType      = errors.New("invalid ticket type for project")
	ErrInvalidFieldsForTicket = errors.New("invalid fields for ticket of that type for project")
)

// TicketRepo handles storing, retrieving, updating, and creating tickets.
type TicketRepo interface {
	Get(u *models.User, uid string) (models.Ticket, error)
	Search(u *models.User, query string) ([]models.Ticket, error)
	Update(u *models.User, uid string, updated models.Ticket) error
	Create(u *models.User, ticket models.Ticket) (models.Ticket, error)
	Delete(u *models.User, uid string) error

	AddComment(u *models.User, uid string, comment models.Comment) (models.Ticket, error)
	NextTicketKey(u *models.User, projectKey string) (string, error)
}

// FieldSchemeRepo handles storing, retrieving, updating, and creating field schemes.
type FieldSchemeRepo interface {
	Get(u *models.User, uid string) (models.FieldScheme, error)
	Search(u *models.User, query string) ([]models.FieldScheme, error)
	Update(u *models.User, uid string, updated models.FieldScheme) error
	Create(u *models.User, fieldScheme models.FieldScheme) (models.FieldScheme, error)
	Delete(u *models.User, uid string) error
}

// ProjectRepo handles storing, retrieving, updating, and creating projects.
type ProjectRepo interface {
	Get(u *models.User, uid string) (models.Project, error)
	Search(u *models.User, query string) ([]models.Project, error)
	Update(u *models.User, uid string, updated models.Project) error
	Create(u *models.User, project models.Project) (models.Project, error)
	Delete(u *models.User, uid string) error
}

// UserRepo handles storing, retrieving, updating, and creating users.
type UserRepo interface {
	Get(u *models.User, uid string) (models.User, error)
	Search(u *models.User, query string) ([]models.User, error)
	Update(u *models.User, uid string, updated models.User) error
	Create(u *models.User, user models.User) (models.User, error)
	Delete(u *models.User, uid string) error
}

// WorkflowRepo handles storing, retrieving, updating, and creating workflows.
type WorkflowRepo interface {
	Get(u *models.User, uid string) (models.Workflow, error)
	Search(u *models.User, query string) ([]models.Workflow, error)
	Update(u *models.User, uid string, updated models.Workflow) error
	Create(u *models.User, workflow models.Workflow) (models.Workflow, error)
	Delete(u *models.User, uid string) error
}

// Repo is a container interface for combining all the other repos.
type Repo interface {
	Tickets() TicketRepo
	Projects() ProjectRepo
	Users() UserRepo
	Fields() FieldSchemeRepo
	Workflows() WorkflowRepo

	Clean() error
	Test() error
}

// Cache is used for storing temporary resources. Usually backed by Mongo, Bolt
// or Redis
type Cache interface {
	Get(key string) (interface{}, error)
	Set(key string, value interface{}) error
	Remove(key string) error
	GetSession(key string) (models.Session, error)
	SetSession(key string, user models.Session) error
	RemoveSession(key string) error
}
<commit_msg>Add explicit type<commit_after>// Copyright 2017 Mathew Robinson <mrobinson@praelatus.io>. All rights reserved.
// Use of this source code is governed by the AGPLv3 license that can be found in
// the LICENSE file.

// Package repo provides definitions for abstracting away database interaction
// in Praelatus
package repo

import (
	"errors"

	"github.com/praelatus/praelatus/models"
)

// Errors
var (
	ErrLoginRequired          error = errors.New("you must be logged in")
	ErrAdminRequired                = errors.New("you must be an administrator")
	ErrUnauthorized                 = errors.New("permission denied")
	ErrNotFound                     = errors.New("not found")
	ErrInvalidTicketType            = errors.New("invalid ticket type for project")
	ErrInvalidFieldsForTicket       = errors.New("invalid fields for ticket of that type for project")
)

// TicketRepo handles storing, retrieving, updating, and creating tickets.
type TicketRepo interface {
	Get(u *models.User, uid string) (models.Ticket, error)
	Search(u *models.User, query string) ([]models.Ticket, error)
	Update(u *models.User, uid string, updated models.Ticket) error
	Create(u *models.User, ticket models.Ticket) (models.Ticket, error)
	Delete(u *models.User, uid string) error

	AddComment(u *models.User, uid string, comment models.Comment) (models.Ticket, error)
	NextTicketKey(u *models.User, projectKey string) (string, error)
}

// FieldSchemeRepo handles storing, retrieving, updating, and creating field schemes.
type FieldSchemeRepo interface {
	Get(u *models.User, uid string) (models.FieldScheme, error)
	Search(u *models.User, query string) ([]models.FieldScheme, error)
	Update(u *models.User, uid string, updated models.FieldScheme) error
	Create(u *models.User, fieldScheme models.FieldScheme) (models.FieldScheme, error)
	Delete(u *models.User, uid string) error
}

// ProjectRepo handles storing, retrieving, updating, and creating projects.
type ProjectRepo interface {
	Get(u *models.User, uid string) (models.Project, error)
	Search(u *models.User, query string) ([]models.Project, error)
	Update(u *models.User, uid string, updated models.Project) error
	Create(u *models.User, project models.Project) (models.Project, error)
	Delete(u *models.User, uid string) error
}

// UserRepo handles storing, retrieving, updating, and creating users.
type UserRepo interface {
	Get(u *models.User, uid string) (models.User, error)
	Search(u *models.User, query string) ([]models.User, error)
	Update(u *models.User, uid string, updated models.User) error
	Create(u *models.User, user models.User) (models.User, error)
	Delete(u *models.User, uid string) error
}

// WorkflowRepo handles storing, retrieving, updating, and creating workflows.
type WorkflowRepo interface {
	Get(u *models.User, uid string) (models.Workflow, error)
	Search(u *models.User, query string) ([]models.Workflow, error)
	Update(u *models.User, uid string, updated models.Workflow) error
	Create(u *models.User, workflow models.Workflow) (models.Workflow, error)
	Delete(u *models.User, uid string) error
}

// Repo is a container interface for combining all the other repos.
type Repo interface {
	Tickets() TicketRepo
	Projects() ProjectRepo
	Users() UserRepo
	Fields() FieldSchemeRepo
	Workflows() WorkflowRepo

	Clean() error
	Test() error
}

// Cache is used for storing temporary resources. Usually backed by Mongo, Bolt
// or Redis
type Cache interface {
	Get(key string) (interface{}, error)
	Set(key string, value interface{}) error
	Remove(key string) error
	GetSession(key string) (models.Session, error)
	SetSession(key string, user models.Session) error
	RemoveSession(key string) error
}
<|endoftext|>


<commit_before>// The command line tool for running Revel apps.
package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
	"text/template"
)

// Cribbed from the genius organization of the "go" command.
type Command struct {
	Run                    func(args []string)
	UsageLine, Short, Long string
}

func (cmd *Command) Name() string {
	name := cmd.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

var commands = []*Command{
	cmdNew,
	cmdRun,
	cmdBuild,
	cmdPackage,
	cmdClean,
	cmdTest,
}

func main() {
	fmt.Fprintf(os.Stdout, header)
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 || args[0] == "help" {
		if len(args) == 1 {
			usage(0)
		}
		if len(args) > 1 {
			for _, cmd := range commands {
				if cmd.Name() == args[1] {
					tmpl(os.Stdout, helpTemplate, cmd)
					return
				}
			}
		}
		usage(2)
	}

	// Commands use panic to abort execution when something goes wrong.
	// Panics are logged at the point of error.  Ignore those.
	defer func() {
		if err := recover(); err != nil {
			if _, ok := err.(LoggedError); !ok {
				// This panic was not expected / logged.
				panic(err)
			}
			os.Exit(1)
		}
	}()

	for _, cmd := range commands {
		if cmd.Name() == args[0] {
			cmd.Run(args[1:])
			return
		}
	}

	errorf("unknown command %q\nRun 'revel help' for usage.\n", args[0])
}

func errorf(format string, args ...interface{}) {
	// Ensure the user's command prompt starts on the next line.
	if !strings.HasSuffix(format, "\n") {
		format += "\n"
	}
	fmt.Fprintf(os.Stderr, format, args...)
	panic(LoggedError{}) // Panic instead of os.Exit so that deferred will run.
}

const header = `~
~ revel! http://robfig.github.com/revel
~
`

const usageTemplate = `usage: revel command [arguments]

The commands are:
{{range .}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}

Use "revel help [command]" for more information.
`

var helpTemplate = `usage: revel {{.UsageLine}}
{{.Long}}
`

func usage(exitCode int) {
	tmpl(os.Stderr, usageTemplate, commands)
	os.Exit(exitCode)
}

func tmpl(w io.Writer, text string, data interface{}) {
	t := template.New("top")
	template.Must(t.Parse(text))
	if err := t.Execute(w, data); err != nil {
		panic(err)
	}
}
<commit_msg>Removed unnecessary flag.Usage assignment<commit_after>// The command line tool for running Revel apps.
package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
	"text/template"
)

// Cribbed from the genius organization of the "go" command.
type Command struct {
	Run                    func(args []string)
	UsageLine, Short, Long string
}

func (cmd *Command) Name() string {
	name := cmd.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

var commands = []*Command{
	cmdNew,
	cmdRun,
	cmdBuild,
	cmdPackage,
	cmdClean,
	cmdTest,
}

func main() {
	fmt.Fprintf(os.Stdout, header)
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 || args[0] == "help" {
		if len(args) == 1 {
			usage(0)
		}
		if len(args) > 1 {
			for _, cmd := range commands {
				if cmd.Name() == args[1] {
					tmpl(os.Stdout, helpTemplate, cmd)
					return
				}
			}
		}
		usage(2)
	}

	// Commands use panic to abort execution when something goes wrong.
	// Panics are logged at the point of error.  Ignore those.
	defer func() {
		if err := recover(); err != nil {
			if _, ok := err.(LoggedError); !ok {
				// This panic was not expected / logged.
				panic(err)
			}
			os.Exit(1)
		}
	}()

	for _, cmd := range commands {
		if cmd.Name() == args[0] {
			cmd.Run(args[1:])
			return
		}
	}

	errorf("unknown command %q\nRun 'revel help' for usage.\n", args[0])
}

func errorf(format string, args ...interface{}) {
	// Ensure the user's command prompt starts on the next line.
	if !strings.HasSuffix(format, "\n") {
		format += "\n"
	}
	fmt.Fprintf(os.Stderr, format, args...)
	panic(LoggedError{}) // Panic instead of os.Exit so that deferred will run.
}

const header = `~
~ revel! http://robfig.github.com/revel
~
`

const usageTemplate = `usage: revel command [arguments]

The commands are:
{{range .}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}

Use "revel help [command]" for more information.
`

var helpTemplate = `usage: revel {{.UsageLine}}
{{.Long}}
`

func usage(exitCode int) {
	tmpl(os.Stderr, usageTemplate, commands)
	os.Exit(exitCode)
}

func tmpl(w io.Writer, text string, data interface{}) {
	t := template.New("top")
	template.Must(t.Parse(text))
	if err := t.Execute(w, data); err != nil {
		panic(err)
	}
}
<|endoftext|>


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

package ristretto

import (
	"testing"
	"time"
)

const (
	// LOSSLESS number of stripes to test with
	RING_STRIPES = 16
	// LOSSY/LOSSLESS size of individual stripes
	RING_CAPACITY = 128
)

type BaseConsumer struct{}

func (c *BaseConsumer) Push(items []uint64) bool { return true }

type TestConsumer struct {
	push func([]uint64)
}

func (c *TestConsumer) Push(items []uint64) bool { c.push(items); return true }

func TestRingLossy(t *testing.T) {
	drainCount := 0
	buffer := newRingBuffer(ringLossy, &ringConfig{
		Consumer: &TestConsumer{
			push: func(items []uint64) {
				drainCount++
			},
		},
		Capacity: 4,
	})
	buffer.Push(1)
	buffer.Push(2)
	buffer.Push(3)
	buffer.Push(4)
	time.Sleep(5 * time.Millisecond)
	if drainCount == 0 {
		t.Fatal("drain error")
	}
}

func TestRingLossless(t *testing.T) {
	drainCount := 0
	found := make(map[uint64]struct{})
	buffer := newRingBuffer(ringLossless, &ringConfig{
		Consumer: &TestConsumer{
			push: func(items []uint64) {
				drainCount++
				for _, item := range items {
					found[item] = struct{}{}
				}
			},
		},
		Capacity: 4,
		Stripes:  2,
	})
	buffer.Push(1)
	buffer.Push(2)
	buffer.Push(3)
	buffer.Push(4)
	buffer.Push(5)
	buffer.Push(6)
	buffer.Push(7)
	buffer.Push(8)
	time.Sleep(5 * time.Millisecond)
	if drainCount != 2 || len(found) != 8 {
		t.Fatal("drain error")
	}
}

func BenchmarkRingLossy(b *testing.B) {
	buffer := newRingBuffer(ringLossy, &ringConfig{
		Consumer: &BaseConsumer{},
		Capacity: RING_CAPACITY,
	})
	item := uint64(1)
	b.Run("single", func(b *testing.B) {
		b.SetBytes(1)
		for n := 0; n < b.N; n++ {
			buffer.Push(item)
		}
	})
	b.Run("multiple", func(b *testing.B) {
		b.SetBytes(1)
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				buffer.Push(item)
			}
		})
	})
}

func BenchmarkRingLossless(b *testing.B) {
	buffer := newRingBuffer(ringLossless, &ringConfig{
		Consumer: &BaseConsumer{},
		Stripes:  RING_STRIPES,
		Capacity: RING_CAPACITY,
	})
	item := uint64(1)
	b.Run("single", func(b *testing.B) {
		b.SetBytes(1)
		for n := 0; n < b.N; n++ {
			buffer.Push(item)
		}
	})
	b.Run("multiple", func(b *testing.B) {
		b.SetBytes(1)
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				buffer.Push(item)
			}
		})
	})
}
<commit_msg>fix test (#64)<commit_after>/*
 * Copyright 2019 Dgraph Labs, Inc. and Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ristretto

import (
	"testing"
	"time"
)

const (
	// LOSSLESS number of stripes to test with
	RING_STRIPES = 16
	// LOSSY/LOSSLESS size of individual stripes
	RING_CAPACITY = 128
)

type BaseConsumer struct{}

func (c *BaseConsumer) Push(items []uint64) bool { return true }

type TestConsumer struct {
	push func([]uint64)
}

func (c *TestConsumer) Push(items []uint64) bool { c.push(items); return true }

func TestRingLossy(t *testing.T) {
	drainCount := 0
	buffer := newRingBuffer(ringLossy, &ringConfig{
		Consumer: &TestConsumer{
			push: func(items []uint64) {
				drainCount++
			},
		},
		Capacity: 4,
	})
	for i := 0; i < 100; i++ {
		buffer.Push(uint64(i))
	}
	// ideally we'd be able to check for a certain "drop percentage" here, but
	// that may vary per platform and testing configuration. for example: if
	// drainCount == 20 then we have 100% accuracy, but it's most likely around
	// 13-20 due to dropping and unfilled rings.
	if drainCount == 0 {
		t.Fatal("drain error")
	}
}

func TestRingLossless(t *testing.T) {
	drainCount := 0
	found := make(map[uint64]struct{})
	buffer := newRingBuffer(ringLossless, &ringConfig{
		Consumer: &TestConsumer{
			push: func(items []uint64) {
				drainCount++
				for _, item := range items {
					found[item] = struct{}{}
				}
			},
		},
		Capacity: 4,
		Stripes:  2,
	})
	buffer.Push(1)
	buffer.Push(2)
	buffer.Push(3)
	buffer.Push(4)
	buffer.Push(5)
	buffer.Push(6)
	buffer.Push(7)
	buffer.Push(8)
	time.Sleep(5 * time.Millisecond)
	if drainCount != 2 || len(found) != 8 {
		t.Fatal("drain error")
	}
}

func BenchmarkRingLossy(b *testing.B) {
	buffer := newRingBuffer(ringLossy, &ringConfig{
		Consumer: &BaseConsumer{},
		Capacity: RING_CAPACITY,
	})
	item := uint64(1)
	b.Run("single", func(b *testing.B) {
		b.SetBytes(1)
		for n := 0; n < b.N; n++ {
			buffer.Push(item)
		}
	})
	b.Run("multiple", func(b *testing.B) {
		b.SetBytes(1)
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				buffer.Push(item)
			}
		})
	})
}

func BenchmarkRingLossless(b *testing.B) {
	buffer := newRingBuffer(ringLossless, &ringConfig{
		Consumer: &BaseConsumer{},
		Stripes:  RING_STRIPES,
		Capacity: RING_CAPACITY,
	})
	item := uint64(1)
	b.Run("single", func(b *testing.B) {
		b.SetBytes(1)
		for n := 0; n < b.N; n++ {
			buffer.Push(item)
		}
	})
	b.Run("multiple", func(b *testing.B) {
		b.SetBytes(1)
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				buffer.Push(item)
			}
		})
	})
}
<|endoftext|>


<commit_before>package load

import (
	"go/ast"
	"go/types"
	"tu/symbols"
	"xast"
	"xtypes"
)

func declSignature(ti *types.Info, decl *ast.FuncDecl) *types.Signature {
	return ti.Defs[decl.Name].Type().(*types.Signature)
}

func resultTuple(sig *types.Signature) *types.Tuple {
	if results := sig.Results(); results != nil {
		return results
	}
	return xtypes.EmptyTuple
}

func funcDeclIndex(env *symbols.Env, p *xast.Package) map[string]*ast.FuncDecl {
	res := make(map[string]*ast.FuncDecl, 32)
	for _, f := range p.AstPkg.Files {
		for _, decl := range f.Decls {
			if decl, ok := decl.(*ast.FuncDecl); ok {
				res[env.InternVar(p.TypPkg, decl.Name.Name)] = decl
			}
		}
	}
	return res
}

func collectParamNames(params []string, decl *ast.FuncDecl) []string {
	for _, p := range decl.Type.Params.List {
		for _, ident := range p.Names {
			params = append(params, ident.Name)
		}
	}
	return params
}
<commit_msg>remove unused func 'funcDeclIndex'<commit_after>package load

import (
	"go/ast"
	"go/types"
	"xtypes"
)

func declSignature(ti *types.Info, decl *ast.FuncDecl) *types.Signature {
	return ti.Defs[decl.Name].Type().(*types.Signature)
}

func resultTuple(sig *types.Signature) *types.Tuple {
	if results := sig.Results(); results != nil {
		return results
	}
	return xtypes.EmptyTuple
}

func collectParamNames(params []string, decl *ast.FuncDecl) []string {
	for _, p := range decl.Type.Params.List {
		for _, ident := range p.Names {
			params = append(params, ident.Name)
		}
	}
	return params
}
<|endoftext|>


<commit_before>package reports

import (
	"net/url"
	"strconv"
)

const (
	// ReportGetSBOMEndpoint is the endpoint for generating SBOMs
	ReportGetSBOMEndpoint = "v1/report/getSBOM"
)

// SBOMFormat is a string enum for the accepted SBOM formats that we can export
type SBOMFormat string

const (
	// SBOMFormatSPDX is the enum value for the SPDX SBOM format
	SBOMFormatSPDX SBOMFormat = "SPDX"
	// SBOMFormatCycloneDX is the enum value for the CycloneDX SBOM format
	SBOMFormatCycloneDX SBOMFormat = "CycloneDX"
)

// SBOMExportOptions represents all the different settings a user can specify for how the SBOM is exported.
// Specify only one of the following data sources to generate the SBOM from:
//  * ProjectIDs (a slice of one or more project IDs)
//  * TeamID (the ID of a team containing one or more projects; this will use all the team's projects)
//  * SBOMID (the ID of a software list containing one or more components; this will use all the list's components)
//  * SBOMEntryIDs (a slice of one or more software list component IDs)
// Format (required) specifies which format/standard the SBOM will be exported in.
// IncludeDependencies will include all the direct and transitive dependencies of each item in the SBOM if true,
// or exclude all the dependencies if false, leaving only the items themselves.
// TeamIsTopLevel applies only to SBOMs generated using the TeamID field. If true, the top-level item in the SBOM's
// hierarchy will be the team. If false, all the team's projects will be on the top level of the hierarchy.
type SBOMExportOptions struct {
	ProjectIDs   []string
	TeamID       string
	SBOMID       string
	SBOMEntryIDs []string

	Format              SBOMFormat
	IncludeDependencies bool
	TeamIsTopLevel      bool
}

// Params converts an SBOMExportOptions object into a URL param object for use in making an API request
func (options SBOMExportOptions) Params() url.Values {
	params := url.Values{}
	params.Set("sbom_type", string(options.Format))
	params.Set("include_dependencies", strconv.FormatBool(options.IncludeDependencies))
	params.Set("team_top_level", strconv.FormatBool(options.TeamIsTopLevel))

	return params
}
<commit_msg>[ION-1292] Add JSON struct tags to SBOMExportOptions<commit_after>package reports

import (
	"net/url"
	"strconv"
)

const (
	// ReportGetSBOMEndpoint is the endpoint for generating SBOMs
	ReportGetSBOMEndpoint = "v1/report/getSBOM"
)

// SBOMFormat is a string enum for the accepted SBOM formats that we can export
type SBOMFormat string

const (
	// SBOMFormatSPDX is the enum value for the SPDX SBOM format
	SBOMFormatSPDX SBOMFormat = "SPDX"
	// SBOMFormatCycloneDX is the enum value for the CycloneDX SBOM format
	SBOMFormatCycloneDX SBOMFormat = "CycloneDX"
)

// SBOMExportOptions represents all the different settings a user can specify for how the SBOM is exported.
// Specify only one of the following data sources to generate the SBOM from:
//  * ProjectIDs (a slice of one or more project IDs)
//  * TeamID (the ID of a team containing one or more projects; this will use all the team's projects)
//  * SBOMID (the ID of a software list containing one or more components; this will use all the list's components)
//  * SBOMEntryIDs (a slice of one or more software list component IDs)
// Format (required) specifies which format/standard the SBOM will be exported in.
// IncludeDependencies will include all the direct and transitive dependencies of each item in the SBOM if true,
// or exclude all the dependencies if false, leaving only the items themselves.
// TeamIsTopLevel applies only to SBOMs generated using the TeamID field. If true, the top-level item in the SBOM's
// hierarchy will be the team. If false, all the team's projects will be on the top level of the hierarchy.
type SBOMExportOptions struct {
	ProjectIDs   []string `json:"ids"`
	TeamID       string   `json:"team_id"`
	SBOMID       string   `json:"sbom_id"`
	SBOMEntryIDs []string `json:"sbom_entry_ids"`

	Format              SBOMFormat `json:"sbom_type"`
	IncludeDependencies bool       `json:"include_dependencies"`
	TeamIsTopLevel      bool       `json:"team_top_level"`
}

// Params converts an SBOMExportOptions object into a URL param object for use in making an API request
func (options SBOMExportOptions) Params() url.Values {
	params := url.Values{}
	params.Set("sbom_type", string(options.Format))
	params.Set("include_dependencies", strconv.FormatBool(options.IncludeDependencies))
	params.Set("team_top_level", strconv.FormatBool(options.TeamIsTopLevel))

	return params
}
<|endoftext|>


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

package scan

import (
	"errors"
	"fmt"
	. "github.com/etix/mirrorbits/config"
	"github.com/etix/mirrorbits/database"
	"github.com/etix/mirrorbits/filesystem"
	"github.com/etix/mirrorbits/utils"
	"github.com/garyburd/redigo/redis"
	"github.com/op/go-logging"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	ScanAborted    = errors.New("scan aborted")
	ScanInProgress = errors.New("scan already in progress")

	log = logging.MustGetLogger("main")
)

type ScannerType int8

const (
	RSYNC ScannerType = iota
	FTP
)

type Scanner interface {
	Scan(url, identifier string, conn redis.Conn, stop chan bool) error
}

type filedata struct {
	path    string
	sha1    string
	sha256  string
	md5     string
	size    int64
	modTime time.Time
}

type scan struct {
	redis *database.Redis

	conn        redis.Conn
	identifier  string
	filesKey    string
	filesTmpKey string
	count       uint
}

func IsScanning(conn redis.Conn, identifier string) (bool, error) {
	return redis.Bool(conn.Do("EXISTS", fmt.Sprintf("SCANNING_%s", identifier)))
}

func Scan(typ ScannerType, r *database.Redis, url, identifier string, stop chan bool) error {
	s := &scan{
		redis:      r,
		identifier: identifier,
	}

	var scanner Scanner
	switch typ {
	case RSYNC:
		scanner = &RsyncScanner{
			scan: s,
		}
	case FTP:
		scanner = &FTPScanner{
			scan: s,
		}
	default:
		panic(fmt.Sprintf("Unknown scanner"))
	}

	// Connect to the database
	conn := s.redis.Get()
	defer conn.Close()

	s.conn = conn

	lockKey := fmt.Sprintf("SCANNING_%s", identifier)

	// Try to aquire a lock so we don't have a scanning race
	// from different nodes.
	// Also make the key expire automatically in case our process
	// gets killed.
	lock, err := redis.Bool(conn.Do("SET", lockKey, 1, "NX", "EX", 600))
	if err != nil {
		return err
	}
	if lock {
		// Lock aquired, delete the lock once done
		defer conn.Do("DEL", lockKey)
	} else {
		return ScanInProgress
	}

	s.setLastSync(conn, identifier, false)

	conn.Send("MULTI")

	s.filesKey = fmt.Sprintf("MIRROR_%s_FILES", identifier)
	s.filesTmpKey = fmt.Sprintf("MIRROR_%s_FILES_TMP", identifier)

	// Remove any left over
	conn.Send("DEL", s.filesTmpKey)

	err = scanner.Scan(url, identifier, conn, stop)
	if err != nil {
		// Discard MULTI
		s.ScannerDiscard()

		// Remove the temporary key
		conn.Do("DEL", s.filesTmpKey)

		log.Errorf("[%s] %s", identifier, err.Error())
		return err
	}

	// Exec multi
	s.ScannerCommit()

	// Get the list of files no more present on this mirror
	toremove, err := redis.Values(conn.Do("SDIFF", s.filesKey, s.filesTmpKey))
	if err != nil {
		return err
	}

	// Remove this mirror from the given file SET
	if len(toremove) > 0 {
		conn.Send("MULTI")
		for _, e := range toremove {
			log.Debugf("[%s] Removing %s from mirror", identifier, e)
			conn.Send("SREM", fmt.Sprintf("FILEMIRRORS_%s", e), identifier)
			conn.Send("DEL", fmt.Sprintf("FILEINFO_%s_%s", identifier, e))
			// Publish update
			database.SendPublish(conn, database.MIRROR_FILE_UPDATE, fmt.Sprintf("%s %s", identifier, e))

		}
		_, err = conn.Do("EXEC")
		if err != nil {
			return err
		}
	}

	// Finally rename the temporary sets containing the list
	// of files for this mirror to the production key
	_, err = conn.Do("RENAME", s.filesTmpKey, s.filesKey)
	if err != nil {
		return err
	}

	sinterKey := fmt.Sprintf("HANDLEDFILES_%s", identifier)

	// Count the number of files known on the remote end
	common, _ := redis.Int64(conn.Do("SINTERSTORE", sinterKey, "FILES", s.filesKey))

	if err != nil {
		return err
	}

	s.setLastSync(conn, identifier, true)
	log.Infof("[%s] Indexed %d files (%d known), %d removed", identifier, s.count, common, len(toremove))
	return nil
}

func (s *scan) ScannerAddFile(f filedata) {
	s.count++

	// Add all the files to a temporary key
	s.conn.Send("SADD", s.filesTmpKey, f.path)

	// Mark the file as being supported by this mirror
	rk := fmt.Sprintf("FILEMIRRORS_%s", f.path)
	s.conn.Send("SADD", rk, s.identifier)

	// Save the size of the current file found on this mirror
	ik := fmt.Sprintf("FILEINFO_%s_%s", s.identifier, f.path)
	s.conn.Send("HSET", ik, "size", f.size)

	// Publish update
	database.SendPublish(s.conn, database.MIRROR_FILE_UPDATE, fmt.Sprintf("%s %s", s.identifier, f.path))
}

func (s *scan) ScannerDiscard() {
	s.conn.Do("DISCARD")
}

func (s *scan) ScannerCommit() error {
	_, err := s.conn.Do("EXEC")
	return err
}

func (s *scan) setLastSync(conn redis.Conn, identifier string, successful bool) error {
	now := time.Now().UTC().Unix()

	conn.Send("MULTI")

	// Set the last sync time
	conn.Send("HSET", fmt.Sprintf("MIRROR_%s", identifier), "lastSync", now)

	// Set the last successful sync time
	if successful {
		conn.Send("HSET", fmt.Sprintf("MIRROR_%s", identifier), "lastSuccessfulSync", now)
	}

	_, err := conn.Do("EXEC")

	// Publish an update on redis
	database.Publish(conn, database.MIRROR_UPDATE, identifier)

	return err
}

// Walk inside the source/reference repository
func (s *scan) walkSource(conn redis.Conn, path string, f os.FileInfo, err error) (*filedata, error) {
	if f == nil || f.IsDir() || f.Mode()&os.ModeSymlink != 0 {
		return nil, nil
	}

	d := new(filedata)
	d.path = path[len(GetConfig().Repository):]
	d.size = f.Size()
	d.modTime = f.ModTime()

	// Get the previous file properties
	properties, err := redis.Strings(conn.Do("HMGET", fmt.Sprintf("FILE_%s", d.path), "size", "modTime", "sha1", "sha256", "md5"))
	if err != nil && err != redis.ErrNil {
		return nil, err
	} else if len(properties) < 5 {
		// This will force a rehash
		properties = make([]string, 5)
	}

	size, _ := strconv.ParseInt(properties[0], 10, 64)
	modTime, _ := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", properties[1])
	sha1 := properties[2]
	sha256 := properties[3]
	md5 := properties[4]

	rehash := (GetConfig().Hashes.SHA1 && len(sha1) == 0) ||
		(GetConfig().Hashes.SHA256 && len(sha256) == 0) ||
		(GetConfig().Hashes.MD5 && len(md5) == 0)

	if rehash || size != d.size || !modTime.Equal(d.modTime) {
		h, err := filesystem.HashFile(GetConfig().Repository + d.path)
		if err != nil {
			log.Warningf("%s: hashing failed: %s", d.path, err.Error())
		} else {
			d.sha1 = h.Sha1
			d.sha256 = h.Sha256
			d.md5 = h.Md5
			if len(d.sha1) > 0 {
				log.Infof("%s: SHA1 %s", d.path, d.sha1)
			}
			if len(d.sha256) > 0 {
				log.Infof("%s: SHA256 %s", d.path, d.sha256)
			}
			if len(d.md5) > 0 {
				log.Infof("%s: MD5 %s", d.path, d.md5)
			}
		}
	} else {
		d.sha1 = sha1
		d.sha256 = sha256
		d.md5 = md5
	}

	return d, nil
}

func ScanSource(r *database.Redis, stop chan bool) (err error) {
	s := &scan{
		redis: r,
	}

	conn := s.redis.Get()
	defer conn.Close()

	if conn.Err() != nil {
		return conn.Err()
	}

	sourceFiles := make([]*filedata, 0, 1000)

	//TODO lock atomically inside redis to avoid two simultanous scan

	if _, err := os.Stat(GetConfig().Repository); os.IsNotExist(err) {
		return fmt.Errorf("%s: No such file or directory", GetConfig().Repository)
	}

	log.Info("[source] Scanning the filesystem...")
	err = filepath.Walk(GetConfig().Repository, func(path string, f os.FileInfo, err error) error {
		fd, err := s.walkSource(conn, path, f, err)
		if err != nil {
			return err
		}
		if fd != nil {
			sourceFiles = append(sourceFiles, fd)
		}
		return nil
	})

	if utils.IsStopped(stop) {
		return ScanAborted
	}
	if err != nil {
		return err
	}
	log.Info("[source] Indexing the files...")

	conn.Send("MULTI")

	// Remove any left over
	conn.Send("DEL", "FILES_TMP")

	// Add all the files to a temporary key
	count := 0
	for _, e := range sourceFiles {
		conn.Send("SADD", "FILES_TMP", e.path)
		count++
	}

	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}

	// Do a diff between the sets to get the removed files
	toremove, err := redis.Values(conn.Do("SDIFF", "FILES", "FILES_TMP"))

	// Create/Update the files' hash keys with the fresh infos
	conn.Send("MULTI")
	for _, e := range sourceFiles {
		conn.Send("HMSET", fmt.Sprintf("FILE_%s", e.path),
			"size", e.size,
			"modTime", e.modTime,
			"sha1", e.sha1,
			"sha256", e.sha256,
			"md5", e.md5)

		// Publish update
		database.SendPublish(conn, database.FILE_UPDATE, e.path)
	}

	// Remove old keys
	if len(toremove) > 0 {
		for _, e := range toremove {
			conn.Send("DEL", fmt.Sprintf("FILE_%s", e))

			// Publish update
			database.SendPublish(conn, database.FILE_UPDATE, fmt.Sprintf("%s", e))
		}
	}

	// Finally rename the temporary sets containing the list
	// of files to the production key
	conn.Send("RENAME", "FILES_TMP", "FILES")

	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}

	log.Infof("[source] Scanned %d files", count)

	return nil
}
<commit_msg>scan: use short-lived locks and use a lock renewal mechanism<commit_after>// Copyright (c) 2014-2017 Ludovic Fauvet
// Licensed under the MIT license

package scan

import (
	"errors"
	"fmt"
	. "github.com/etix/mirrorbits/config"
	"github.com/etix/mirrorbits/database"
	"github.com/etix/mirrorbits/filesystem"
	"github.com/etix/mirrorbits/utils"
	"github.com/garyburd/redigo/redis"
	"github.com/op/go-logging"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	ScanAborted    = errors.New("scan aborted")
	ScanInProgress = errors.New("scan already in progress")

	log = logging.MustGetLogger("main")
)

type ScannerType int8

const (
	RSYNC ScannerType = iota
	FTP
)

type Scanner interface {
	Scan(url, identifier string, conn redis.Conn, stop chan bool) error
}

type filedata struct {
	path    string
	sha1    string
	sha256  string
	md5     string
	size    int64
	modTime time.Time
}

type scan struct {
	redis *database.Redis

	conn        redis.Conn
	identifier  string
	filesKey    string
	filesTmpKey string
	count       uint
}

func IsScanning(conn redis.Conn, identifier string) (bool, error) {
	return redis.Bool(conn.Do("EXISTS", fmt.Sprintf("SCANNING_%s", identifier)))
}

func Scan(typ ScannerType, r *database.Redis, url, identifier string, stop chan bool) error {
	s := &scan{
		redis:      r,
		identifier: identifier,
	}

	var scanner Scanner
	switch typ {
	case RSYNC:
		scanner = &RsyncScanner{
			scan: s,
		}
	case FTP:
		scanner = &FTPScanner{
			scan: s,
		}
	default:
		panic(fmt.Sprintf("Unknown scanner"))
	}

	// Connect to the database
	conn := s.redis.Get()
	defer conn.Close()

	s.conn = conn

	// Try to aquire a lock so we don't have a scanning race
	// from different nodes.
	// Also make the key expire automatically in case our process
	// gets killed.
	lockKey := fmt.Sprintf("SCANNING_%s", identifier)
	_, err := redis.String(conn.Do("SET", lockKey, 1, "NX", "EX", 10))
	if err == redis.ErrNil {
		return ScanInProgress
	} else if err != nil {
		return err
	}

	// Lock acquired, delete the lock once done
	defer conn.Do("DEL", lockKey)

	// Maintain the lock active while the scan is in progress
	done := make(chan struct{})
	defer close(done)
	go func() {
		conn := s.redis.Get()
		defer conn.Close()

		for {
			select {
			case <-done:
				return
			case <-stop:
				return
			case <-time.After(5 * time.Second):
				conn.Do("EXPIRE", lockKey, 10)
				log.Debugf("[%s] Lock renewed", identifier)
			}
		}
	}()

	s.setLastSync(conn, identifier, false)

	conn.Send("MULTI")

	s.filesKey = fmt.Sprintf("MIRROR_%s_FILES", identifier)
	s.filesTmpKey = fmt.Sprintf("MIRROR_%s_FILES_TMP", identifier)

	// Remove any left over
	conn.Send("DEL", s.filesTmpKey)

	err = scanner.Scan(url, identifier, conn, stop)
	if err != nil {
		// Discard MULTI
		s.ScannerDiscard()

		// Remove the temporary key
		conn.Do("DEL", s.filesTmpKey)

		log.Errorf("[%s] %s", identifier, err.Error())
		return err
	}

	// Exec multi
	s.ScannerCommit()

	// Get the list of files no more present on this mirror
	toremove, err := redis.Values(conn.Do("SDIFF", s.filesKey, s.filesTmpKey))
	if err != nil {
		return err
	}

	// Remove this mirror from the given file SET
	if len(toremove) > 0 {
		conn.Send("MULTI")
		for _, e := range toremove {
			log.Debugf("[%s] Removing %s from mirror", identifier, e)
			conn.Send("SREM", fmt.Sprintf("FILEMIRRORS_%s", e), identifier)
			conn.Send("DEL", fmt.Sprintf("FILEINFO_%s_%s", identifier, e))
			// Publish update
			database.SendPublish(conn, database.MIRROR_FILE_UPDATE, fmt.Sprintf("%s %s", identifier, e))

		}
		_, err = conn.Do("EXEC")
		if err != nil {
			return err
		}
	}

	// Finally rename the temporary sets containing the list
	// of files for this mirror to the production key
	_, err = conn.Do("RENAME", s.filesTmpKey, s.filesKey)
	if err != nil {
		return err
	}

	sinterKey := fmt.Sprintf("HANDLEDFILES_%s", identifier)

	// Count the number of files known on the remote end
	common, _ := redis.Int64(conn.Do("SINTERSTORE", sinterKey, "FILES", s.filesKey))

	if err != nil {
		return err
	}

	s.setLastSync(conn, identifier, true)
	log.Infof("[%s] Indexed %d files (%d known), %d removed", identifier, s.count, common, len(toremove))
	return nil
}

func (s *scan) ScannerAddFile(f filedata) {
	s.count++

	// Add all the files to a temporary key
	s.conn.Send("SADD", s.filesTmpKey, f.path)

	// Mark the file as being supported by this mirror
	rk := fmt.Sprintf("FILEMIRRORS_%s", f.path)
	s.conn.Send("SADD", rk, s.identifier)

	// Save the size of the current file found on this mirror
	ik := fmt.Sprintf("FILEINFO_%s_%s", s.identifier, f.path)
	s.conn.Send("HSET", ik, "size", f.size)

	// Publish update
	database.SendPublish(s.conn, database.MIRROR_FILE_UPDATE, fmt.Sprintf("%s %s", s.identifier, f.path))
}

func (s *scan) ScannerDiscard() {
	s.conn.Do("DISCARD")
}

func (s *scan) ScannerCommit() error {
	_, err := s.conn.Do("EXEC")
	return err
}

func (s *scan) setLastSync(conn redis.Conn, identifier string, successful bool) error {
	now := time.Now().UTC().Unix()

	conn.Send("MULTI")

	// Set the last sync time
	conn.Send("HSET", fmt.Sprintf("MIRROR_%s", identifier), "lastSync", now)

	// Set the last successful sync time
	if successful {
		conn.Send("HSET", fmt.Sprintf("MIRROR_%s", identifier), "lastSuccessfulSync", now)
	}

	_, err := conn.Do("EXEC")

	// Publish an update on redis
	database.Publish(conn, database.MIRROR_UPDATE, identifier)

	return err
}

// Walk inside the source/reference repository
func (s *scan) walkSource(conn redis.Conn, path string, f os.FileInfo, err error) (*filedata, error) {
	if f == nil || f.IsDir() || f.Mode()&os.ModeSymlink != 0 {
		return nil, nil
	}

	d := new(filedata)
	d.path = path[len(GetConfig().Repository):]
	d.size = f.Size()
	d.modTime = f.ModTime()

	// Get the previous file properties
	properties, err := redis.Strings(conn.Do("HMGET", fmt.Sprintf("FILE_%s", d.path), "size", "modTime", "sha1", "sha256", "md5"))
	if err != nil && err != redis.ErrNil {
		return nil, err
	} else if len(properties) < 5 {
		// This will force a rehash
		properties = make([]string, 5)
	}

	size, _ := strconv.ParseInt(properties[0], 10, 64)
	modTime, _ := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", properties[1])
	sha1 := properties[2]
	sha256 := properties[3]
	md5 := properties[4]

	rehash := (GetConfig().Hashes.SHA1 && len(sha1) == 0) ||
		(GetConfig().Hashes.SHA256 && len(sha256) == 0) ||
		(GetConfig().Hashes.MD5 && len(md5) == 0)

	if rehash || size != d.size || !modTime.Equal(d.modTime) {
		h, err := filesystem.HashFile(GetConfig().Repository + d.path)
		if err != nil {
			log.Warningf("%s: hashing failed: %s", d.path, err.Error())
		} else {
			d.sha1 = h.Sha1
			d.sha256 = h.Sha256
			d.md5 = h.Md5
			if len(d.sha1) > 0 {
				log.Infof("%s: SHA1 %s", d.path, d.sha1)
			}
			if len(d.sha256) > 0 {
				log.Infof("%s: SHA256 %s", d.path, d.sha256)
			}
			if len(d.md5) > 0 {
				log.Infof("%s: MD5 %s", d.path, d.md5)
			}
		}
	} else {
		d.sha1 = sha1
		d.sha256 = sha256
		d.md5 = md5
	}

	return d, nil
}

func ScanSource(r *database.Redis, stop chan bool) (err error) {
	s := &scan{
		redis: r,
	}

	conn := s.redis.Get()
	defer conn.Close()

	if conn.Err() != nil {
		return conn.Err()
	}

	sourceFiles := make([]*filedata, 0, 1000)

	//TODO lock atomically inside redis to avoid two simultanous scan

	if _, err := os.Stat(GetConfig().Repository); os.IsNotExist(err) {
		return fmt.Errorf("%s: No such file or directory", GetConfig().Repository)
	}

	log.Info("[source] Scanning the filesystem...")
	err = filepath.Walk(GetConfig().Repository, func(path string, f os.FileInfo, err error) error {
		fd, err := s.walkSource(conn, path, f, err)
		if err != nil {
			return err
		}
		if fd != nil {
			sourceFiles = append(sourceFiles, fd)
		}
		return nil
	})

	if utils.IsStopped(stop) {
		return ScanAborted
	}
	if err != nil {
		return err
	}
	log.Info("[source] Indexing the files...")

	conn.Send("MULTI")

	// Remove any left over
	conn.Send("DEL", "FILES_TMP")

	// Add all the files to a temporary key
	count := 0
	for _, e := range sourceFiles {
		conn.Send("SADD", "FILES_TMP", e.path)
		count++
	}

	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}

	// Do a diff between the sets to get the removed files
	toremove, err := redis.Values(conn.Do("SDIFF", "FILES", "FILES_TMP"))

	// Create/Update the files' hash keys with the fresh infos
	conn.Send("MULTI")
	for _, e := range sourceFiles {
		conn.Send("HMSET", fmt.Sprintf("FILE_%s", e.path),
			"size", e.size,
			"modTime", e.modTime,
			"sha1", e.sha1,
			"sha256", e.sha256,
			"md5", e.md5)

		// Publish update
		database.SendPublish(conn, database.FILE_UPDATE, e.path)
	}

	// Remove old keys
	if len(toremove) > 0 {
		for _, e := range toremove {
			conn.Send("DEL", fmt.Sprintf("FILE_%s", e))

			// Publish update
			database.SendPublish(conn, database.FILE_UPDATE, fmt.Sprintf("%s", e))
		}
	}

	// Finally rename the temporary sets containing the list
	// of files to the production key
	conn.Send("RENAME", "FILES_TMP", "FILES")

	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}

	log.Infof("[source] Scanned %d files", count)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package testdata

import (
	"bufio"
	"io"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/google/mtail/metrics"
	"github.com/google/mtail/metrics/datum"
)

var var_re = regexp.MustCompile(`^(counter|gauge|timer) ([^ ]+)(?: {([^}]+)})?(?: ([+-]?\d+(?:\.\d+(?:[eE]-?\d+)?)?))?(?: (.+))?`)

// Find a metric in a store
func FindMetricOrNil(store *metrics.Store, name string) *metrics.Metric {
	store.RLock()
	defer store.RUnlock()
	for _, m := range store.Metrics {
		if m.Name == name {
			return m
		}
	}
	return nil
}

func ReadTestData(file io.Reader, programfile string, store *metrics.Store) {
	prog := filepath.Base(programfile)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		glog.V(2).Infof("'%s'\n", scanner.Text())
		match := var_re.FindStringSubmatch(scanner.Text())
		glog.V(2).Infof("len match: %d\n", len(match))
		if len(match) == 0 {
			continue
		}
		var keys, vals []string
		if match[3] != "" {
			for _, pair := range strings.Split(match[3], ",") {
				glog.V(2).Infof("pair: %s\n", pair)
				kv := strings.Split(pair, "=")
				keys = append(keys, kv[0])
				if kv[1] != "" {
					vals = append(vals, kv[1])
				}
			}
		}
		var kind metrics.Kind
		switch match[1] {
		case "counter":
			kind = metrics.Counter
		case "gauge":
			kind = metrics.Gauge
		case "timer":
			kind = metrics.Timer
		}
		glog.V(2).Infof("match[4]: %q", match[4])
		typ := datum.Int
		var (
			ival int64
			fval float64
			err  error
		)
		if match[4] != "" {
			ival, err = strconv.ParseInt(match[4], 10, 64)
			if err != nil {
				fval, err = strconv.ParseFloat(match[4], 64)
				if err != nil {
					glog.Fatalf("parse failed for '%s': %s", match[4], err)
				}
				typ = datum.Float
			}
		}
		var timestamp time.Time
		glog.V(2).Infof("match 5: %q\n", match[5])
		if match[5] != "" {
			timestamp, _ = time.Parse(time.RFC3339, match[5])
		}
		glog.V(2).Infof("timestamp is %s which is %v in unix", timestamp.Format(time.RFC3339), timestamp.Unix())

		// Now we have enough information to get orcreate a metric.
		m := FindMetricOrNil(store, match[2])
		if m != nil {
			if m.Type != typ {
				glog.V(2).Infof("The type of the fetched metric is not %s: %s", typ, m)
				continue
			}
		} else {
			m = metrics.NewMetric(match[2], prog, kind, typ, keys...)
			glog.V(2).Infof("making a new %v\n", m)
			store.Add(m)
		}

		if match[4] != "" {
			d, err := m.GetDatum(vals...)
			if err != nil {
				glog.V(2).Infof("Failed to get datum: %s", err)
				continue
			}
			glog.V(2).Infof("got datum %v", d)

			if typ == metrics.Int {
				glog.V(2).Infof("setting %v with vals %v to %v at %v\n", d, vals, ival, timestamp)
				datum.SetInt(d, ival, timestamp)
			} else {
				glog.V(2).Infof("setting %v with vals %v to %v at %v\n", d, vals, fval, timestamp)
				datum.SetFloat(d, fval, timestamp)
			}
		} else {
			if kind == metrics.Counter && len(keys) == 0 {
				d, err := m.GetDatum()
				if err != nil {
					glog.Fatal(err)
				}
				// Initialize to zero at the zero time.
				if typ == metrics.Int {
					datum.SetInt(d, 0, time.Unix(0, 0))
				} else {
					datum.SetFloat(d, 0, time.Unix(0, 0))
				}
			}
			glog.V(2).Infof("making a new %v\n", m)
		}
		glog.V(2).Infof("Metric is now %s", m)
	}
}
<commit_msg>Make non-nil slices to ensure equality of metric store as per the behaviour of dload, which allocates a non-nil slice for keys and labelvalues.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package testdata

import (
	"bufio"
	"io"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/google/mtail/metrics"
	"github.com/google/mtail/metrics/datum"
)

var var_re = regexp.MustCompile(`^(counter|gauge|timer) ([^ ]+)(?: {([^}]+)})?(?: ([+-]?\d+(?:\.\d+(?:[eE]-?\d+)?)?))?(?: (.+))?`)

// Find a metric in a store
func FindMetricOrNil(store *metrics.Store, name string) *metrics.Metric {
	store.RLock()
	defer store.RUnlock()
	for _, m := range store.Metrics {
		if m.Name == name {
			return m
		}
	}
	return nil
}

func ReadTestData(file io.Reader, programfile string, store *metrics.Store) {
	prog := filepath.Base(programfile)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		glog.V(2).Infof("'%s'\n", scanner.Text())
		match := var_re.FindStringSubmatch(scanner.Text())
		glog.V(2).Infof("len match: %d\n", len(match))
		if len(match) == 0 {
			continue
		}
		keys := make([]string, 0)
		vals := make([]string, 0)
		if match[3] != "" {
			for _, pair := range strings.Split(match[3], ",") {
				glog.V(2).Infof("pair: %s\n", pair)
				kv := strings.Split(pair, "=")
				keys = append(keys, kv[0])
				if kv[1] != "" {
					vals = append(vals, kv[1])
				}
			}
		}
		var kind metrics.Kind
		switch match[1] {
		case "counter":
			kind = metrics.Counter
		case "gauge":
			kind = metrics.Gauge
		case "timer":
			kind = metrics.Timer
		}
		glog.V(2).Infof("match[4]: %q", match[4])
		typ := datum.Int
		var (
			ival int64
			fval float64
			err  error
		)
		if match[4] != "" {
			ival, err = strconv.ParseInt(match[4], 10, 64)
			if err != nil {
				fval, err = strconv.ParseFloat(match[4], 64)
				if err != nil {
					glog.Fatalf("parse failed for '%s': %s", match[4], err)
				}
				typ = datum.Float
			}
		}
		var timestamp time.Time
		glog.V(2).Infof("match 5: %q\n", match[5])
		if match[5] != "" {
			timestamp, _ = time.Parse(time.RFC3339, match[5])
		}
		glog.V(2).Infof("timestamp is %s which is %v in unix", timestamp.Format(time.RFC3339), timestamp.Unix())

		// Now we have enough information to get orcreate a metric.
		m := FindMetricOrNil(store, match[2])
		if m != nil {
			if m.Type != typ {
				glog.V(2).Infof("The type of the fetched metric is not %s: %s", typ, m)
				continue
			}
		} else {
			m = metrics.NewMetric(match[2], prog, kind, typ, keys...)
			glog.V(2).Infof("making a new %v\n", m)
			store.Add(m)
		}

		if match[4] != "" {
			d, err := m.GetDatum(vals...)
			if err != nil {
				glog.V(2).Infof("Failed to get datum: %s", err)
				continue
			}
			glog.V(2).Infof("got datum %v", d)

			if typ == metrics.Int {
				glog.V(2).Infof("setting %v with vals %v to %v at %v\n", d, vals, ival, timestamp)
				datum.SetInt(d, ival, timestamp)
			} else {
				glog.V(2).Infof("setting %v with vals %v to %v at %v\n", d, vals, fval, timestamp)
				datum.SetFloat(d, fval, timestamp)
			}
		} else {
			if kind == metrics.Counter && len(keys) == 0 {
				d, err := m.GetDatum()
				if err != nil {
					glog.Fatal(err)
				}
				// Initialize to zero at the zero time.
				if typ == metrics.Int {
					datum.SetInt(d, 0, time.Unix(0, 0))
				} else {
					datum.SetFloat(d, 0, time.Unix(0, 0))
				}
			}
			glog.V(2).Infof("making a new %v\n", m)
		}
		glog.V(2).Infof("Metric is now %s", m)
	}
}
<|endoftext|>


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

package testing

import (
	"encoding/gob"
	"fmt"
	"go/build"
	"os"
	"path"
)

// EncryptTestCase represents a test case for Encrypt() generated using a
// reference implementation.
type EncryptTestCase struct {
	Key []byte
	Plaintext []byte
	Associated [][]byte
	Output []byte
}

func (c EncryptTestCase) String() string {
	return fmt.Sprintf(
		"Encrypt(%x, %x, %v) = %x",
		c.Key,
		c.Plaintext,
		c.Associated,
		c.Output)
}

// EncryptTestCases returns test cases for Encrypt.
func EncryptCases() []EncryptTestCase {
	// Find the source package.
	pkg, err := build.Import(
		"github.com/jacobsa/aes/testing/cases",
		"",
		build.FindOnly)

	if err != nil {
		panic(fmt.Sprintf("Finding package: %v", err))
	}

	// Load the appropriate gob file.
	gobPath := path.Join(pkg.Dir, "encrypt.gob")
	f, err := os.Open(gobPath)
	if err != nil {
		panic(fmt.Sprintf("Opening %s: %v", gobPath, err))
	}

	defer f.Close()

	// Parse it.
	var cases []EncryptTestCase
	if err = gob.NewDecoder(f).Decode(&cases); err != nil {
		panic(fmt.Sprintf("Decoding: %v", err))
	}

	return cases
}
<commit_msg>Fixed test bugs.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package testing

import (
	"encoding/gob"
	"fmt"
	"go/build"
	"os"
	"path"
)

// EncryptTestCase represents a test case for Encrypt() generated using a
// reference implementation.
type EncryptTestCase struct {
	Key []byte
	Plaintext []byte
	Associated [][]byte
	Output []byte
}

func (c EncryptTestCase) String() string {
	return fmt.Sprintf(
		"Encrypt(%x, %x, %v) = %x",
		c.Key,
		c.Plaintext,
		c.Associated,
		c.Output)
}

// EncryptTestCases returns test cases for Encrypt.
func EncryptCases() []EncryptTestCase {
	// Find the source package.
	pkg, err := build.Import(
		"github.com/jacobsa/aes/testing/cases",
		"",
		build.FindOnly)

	if err != nil {
		panic(fmt.Sprintf("Finding package: %v", err))
	}

	// Load the appropriate gob file.
	gobPath := path.Join(pkg.Dir, "encrypt.gob")
	f, err := os.Open(gobPath)
	if err != nil {
		panic(fmt.Sprintf("Opening %s: %v", gobPath, err))
	}

	defer f.Close()

	// Parse it.
	var cases []EncryptTestCase
	if err = gob.NewDecoder(f).Decode(&cases); err != nil {
		panic(fmt.Sprintf("Decoding: %v", err))
	}

	// Make sure the plaintext field is non-nil for all cases so that it can
	// easily be used with a DeepEquals matcher. Gob decoding does not seem to
	// preserve this.
	for i, _ := range cases {
		c := &cases[i]
		if c.Plaintext == nil {
			c.Plaintext = []byte{}
		}
	}

	return cases
}
<|endoftext|>


<commit_before>// Package chronos is a scheduling tool for Go based on:
//  https://github.com/carlescere/scheduler

package chronos

import (
	"errors"
	"time"
)

// Enum of scheduler kind
const (
	periodicKind = iota
	monthlyKind  = iota
	yearlyKind   = iota
)

const (
	Day  = 24 * time.Hour
	Week = 7 * Day
)

type scheduler interface {
	// Returns wether there is another event scheduled and the remaining time
	next() (bool, time.Duration)
}

// Auxiliar type that holds the information needed to build the scheduler
type auxiliar struct {
	kind, // Enum of scheduler kind
	ammount int
	notInmediately bool
	start,
	end time.Time
	unit time.Duration
}

// Accepts periods in every time unit from ns to weeks, months and years need to
// be considered separately as their length is not constant
type periodic struct {
	start, // Start time
	end time.Time // End time, zero value means no end
	started bool          // Internal flag to handle first executions
	ammount time.Duration // Period
	n       int           // Number of already executed events
}

// Constructor
func newPeriodic(start, end time.Time, ammount int, unit time.Duration, notInmediately bool) (*periodic, error) {
	// Check the input is valid
	if ammount == 0 || unit == 0 {
		return nil, errors.New("0 is not a valid period")
	}
	// If no start time was assigned, use current time
	if start.IsZero() {
		start = time.Now()
	}
	// If notInmediately was called, the starting date should not be returned
	// by periodic.next() call, so we add 1 to the event count to avoid it
	var n int
	if notInmediately {
		n = 1
	}

	return &periodic{start: start, end: end, started: notInmediately,
			ammount: time.Duration(ammount * int(unit)), n: n}, nil
}

// Auxiliar function that returns the execution time candidate
func (s *periodic) getCandidate() time.Time {
	return s.start.Add(time.Duration(s.n * int(s.ammount)))
}

// Implements scheduler.next()
func (s *periodic) next() (bool, time.Duration) {
	// Calculate the next iteration
	next := s.getCandidate()
	for next.Before(time.Now()) {
		if !s.started {
			break
		}
		s.n++
		next = s.getCandidate()
	}
	if !s.started {
		s.started = true
	}

	// Check if the end date has arrived
	return s.end.IsZero() || next.Before(s.end), next.Sub(time.Now())
}

// Monthly periods need to be considered separately as their length is not
// constant (28-31 days)
type monthly struct {
	start, // Start time
	end time.Time // End time, zero value means no end
	started  bool // Internal flag to handle first executions
	ammount, // Ammount of months that made up a period
	n int // Number of already executed events
}

// Constructor
func newMonthly(start, end time.Time, ammount int, notInmediately bool) (*monthly, error) {
	// Check the input is valid
	if ammount == 0 {
		return nil, errors.New("0 months is not a valid period")
	}
	// If no start time was assigned, use current time
	if start.IsZero() {
		start = time.Now()
	}
	// If notInmediately was called, the starting date should not be returned
	// by periodic.next() call, so we add 1 to the event count to avoid it
	var n int
	if notInmediately {
		n = 1
	}

	return &monthly{start: start, end: end, started: notInmediately,
			ammount: ammount, n: n}, nil
}

func (s *monthly) getCandidate() time.Time {
	res := s.start.AddDate(0, s.n*s.ammount, 0)
	if res.Day() != s.start.Day() {
		res = res.AddDate(0, 0, -res.Day())
	}
	return res
}

// Implements scheduler.next()
func (s *monthly) next() (bool, time.Duration) {
	// Calculate the next iteration
	next := s.getCandidate()
	for next.Before(time.Now()) {
		if !s.started {
			break
		}
		s.n++
		next = s.getCandidate()
	}
	if !s.started {
		s.started = true
	}

	// Check if the end date has arrived
	return s.end.IsZero() || next.Before(s.end), next.Sub(time.Now())
}

// Yearly periods need to be considered separately as
// their length is not constant (365-366 days)
type yearly struct {
	start, // Start time
	end time.Time // End time, zero value means no end
	started  bool // Internal flag to handle first executions
	ammount, // Ammount of years that made up a period
	n int // Number of already executed events
}

// Constructor
func newYearly(start, end time.Time, ammount int, notInmediately bool) (*yearly, error) {
	// Check the input is valid
	if ammount == 0 {
		return nil, errors.New("0 years is not a valid period")
	}
	// If no start time was assigned, use current time
	if start.IsZero() {
		start = time.Now()
	}
	// If notInmediately was called, the starting date should not be returned
	// by periodic.next() call, so we add 1 to the event count to avoid it
	var n int
	if notInmediately {
		n = 1
	}

	return &yearly{start: start, end: end, started: notInmediately,
			ammount: ammount, n: n}, nil
}

func (s *yearly) getCandidate() time.Time {
	res := s.start.AddDate(s.n*s.ammount, 0, 0)
	if res.Day() != s.start.Day() {
		res = res.AddDate(0, 0, -res.Day())
	}
	return res
}

// implements scheduler.next()
func (s *yearly) next() (bool, time.Duration) {
	// Calculate the next iteration
	next := s.getCandidate()
	for next.Before(time.Now()) {
		if !s.started {
			break
		}
		s.n++
		next = s.getCandidate()
	}
	if !s.started {
		s.started = true
	}

	// Check if the end date has arrived
	return s.end.IsZero() || next.Before(s.end), next.Sub(time.Now())
}
<commit_msg>Fix: do not return repeated events<commit_after>// Package chronos is a scheduling tool for Go based on:
//  https://github.com/carlescere/scheduler

package chronos

import (
	"errors"
	"time"
)

// Enum of scheduler kind
const (
	periodicKind = iota
	monthlyKind  = iota
	yearlyKind   = iota
)

const (
	Day  = 24 * time.Hour
	Week = 7 * Day
)

type scheduler interface {
	// Returns wether there is another event scheduled and the remaining time
	next() (bool, time.Duration)
}

// Auxiliar type that holds the information needed to build the scheduler
type auxiliar struct {
	kind, // Enum of scheduler kind
	ammount int
	notInmediately bool
	start,
	end time.Time
	unit time.Duration
}

// Accepts periods in every time unit from ns to weeks, months and years need to
// be considered separately as their length is not constant
type periodic struct {
	start, // Start time
	end time.Time // End time, zero value means no end
	started bool          // Internal flag to handle first executions
	ammount time.Duration // Period
	n       int           // Number of already executed events
}

// Constructor
func newPeriodic(start, end time.Time, ammount int, unit time.Duration, notInmediately bool) (*periodic, error) {
	// Check the input is valid
	if ammount == 0 || unit == 0 {
		return nil, errors.New("0 is not a valid period")
	}
	// If no start time was assigned, use current time
	if start.IsZero() {
		start = time.Now()
	}
	// If notInmediately was called, the starting date should not be returned
	// by periodic.next() call, so we add 1 to the event count to avoid it
	var n int
	if notInmediately {
		n = 1
	}

	return &periodic{start: start, end: end, started: notInmediately,
			ammount: time.Duration(ammount * int(unit)), n: n}, nil
}

// Auxiliar function that returns the execution time candidate
func (s *periodic) getCandidate() time.Time {
	return s.start.Add(time.Duration(s.n * int(s.ammount)))
}

// Implements scheduler.next()
func (s *periodic) next() (bool, time.Duration) {
	// Calculate the next iteration
	next := s.getCandidate()
	for next.Before(time.Now()) {
		if !s.started {
			break
		}
		s.n++
		next = s.getCandidate()
	}
	s.n++
	if !s.started {
		s.started = true
	}

	// Check if the end date has arrived
	return s.end.IsZero() || next.Before(s.end), next.Sub(time.Now())
}

// Monthly periods need to be considered separately as their length is not
// constant (28-31 days)
type monthly struct {
	start, // Start time
	end time.Time // End time, zero value means no end
	started  bool // Internal flag to handle first executions
	ammount, // Ammount of months that made up a period
	n int // Number of already executed events
}

// Constructor
func newMonthly(start, end time.Time, ammount int, notInmediately bool) (*monthly, error) {
	// Check the input is valid
	if ammount == 0 {
		return nil, errors.New("0 months is not a valid period")
	}
	// If no start time was assigned, use current time
	if start.IsZero() {
		start = time.Now()
	}
	// If notInmediately was called, the starting date should not be returned
	// by periodic.next() call, so we add 1 to the event count to avoid it
	var n int
	if notInmediately {
		n = 1
	}

	return &monthly{start: start, end: end, started: notInmediately,
			ammount: ammount, n: n}, nil
}

func (s *monthly) getCandidate() time.Time {
	res := s.start.AddDate(0, s.n*s.ammount, 0)
	if res.Day() != s.start.Day() {
		res = res.AddDate(0, 0, -res.Day())
	}
	return res
}

// Implements scheduler.next()
func (s *monthly) next() (bool, time.Duration) {
	// Calculate the next iteration
	next := s.getCandidate()
	for next.Before(time.Now()) {
		if !s.started {
			break
		}
		s.n++
		next = s.getCandidate()
	}
	s.n++
	if !s.started {
		s.started = true
	}

	// Check if the end date has arrived
	return s.end.IsZero() || next.Before(s.end), next.Sub(time.Now())
}

// Yearly periods need to be considered separately as
// their length is not constant (365-366 days)
type yearly struct {
	start, // Start time
	end time.Time // End time, zero value means no end
	started  bool // Internal flag to handle first executions
	ammount, // Ammount of years that made up a period
	n int // Number of already executed events
}

// Constructor
func newYearly(start, end time.Time, ammount int, notInmediately bool) (*yearly, error) {
	// Check the input is valid
	if ammount == 0 {
		return nil, errors.New("0 years is not a valid period")
	}
	// If no start time was assigned, use current time
	if start.IsZero() {
		start = time.Now()
	}
	// If notInmediately was called, the starting date should not be returned
	// by periodic.next() call, so we add 1 to the event count to avoid it
	var n int
	if notInmediately {
		n = 1
	}

	return &yearly{start: start, end: end, started: notInmediately,
			ammount: ammount, n: n}, nil
}

func (s *yearly) getCandidate() time.Time {
	res := s.start.AddDate(s.n*s.ammount, 0, 0)
	if res.Day() != s.start.Day() {
		res = res.AddDate(0, 0, -res.Day())
	}
	return res
}

// implements scheduler.next()
func (s *yearly) next() (bool, time.Duration) {
	// Calculate the next iteration
	next := s.getCandidate()
	for next.Before(time.Now()) {
		if !s.started {
			break
		}
		s.n++
		next = s.getCandidate()
	}
	s.n++
	if !s.started {
		s.started = true
	}

	// Check if the end date has arrived
	return s.end.IsZero() || next.Before(s.end), next.Sub(time.Now())
}
<|endoftext|>