<commit_before>package template

var filesTemplate = `{{buildTags .Tags}}// Code generated by fileb0x at "{{.Now}}" from config file "{{.ConfigFile}}" DO NOT EDIT.
// modification hash({{.ModificationHash}})

package {{.Pkg}}
{{$Compression := .Compression}}

import (
  "bytes"
  {{if not .Spread}}{{if and $Compression.Compress (not .Debug)}}{{if not $Compression.Keep}}"compress/gzip"{{end}}{{end}}{{end}}
  "context"
  "io"
  "net/http"
  "os"
  "path"
{{if or .Updater.Enabled .Debug}}
  "strings"
{{end}}

  "golang.org/x/net/webdav"

{{if .Updater.Enabled}}
  "crypto/sha256"
	"encoding/hex"
  "log"
  "path/filepath"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
{{end}}
)

var (
  // CTX is a context for webdav vfs
  {{exported "CTX"}} = context.Background()

  {{if .Debug}}
  {{exported "FS"}} = webdav.Dir(".")
  {{else}}
  // FS is a virtual memory file system
  {{exported "FS"}} = webdav.NewMemFS()
  {{end}}

  // Handler is used to server files through a http handler
  {{exportedTitle "Handler"}} *webdav.Handler

  // HTTP is the http file system
  {{exportedTitle "HTTP"}} http.FileSystem = new({{exported "HTTPFS"}})
)

// HTTPFS implements http.FileSystem
type {{exported "HTTPFS"}} struct {}

{{if (and (not .Spread) (not .Debug))}}
{{range .Files}}
// {{exportedTitle "File"}}{{buildSafeVarName .Path}} is "{{.Path}}"
var {{exportedTitle "File"}}{{buildSafeVarName .Path}} = {{.Data}}
{{end}}
{{end}}

func init() {
  err := {{exported "CTX"}}.Err()
  if err != nil {
		panic(err)
	}

{{ $length := len .DirList }}
{{ $fLength := len .Files }}
{{ $noDirsButFiles := (and (not .Spread) (eq $length 0) (gt $fLength 0)) }}
{{if not .Debug}}
{{range $index, $dir := .DirList}}
  {{if and (ne $dir "./") (ne $dir "/") (ne $dir ".") (ne $dir "")}}
  err = {{exported "FS"}}.Mkdir({{exported "CTX"}}, "{{$dir}}", 0777)
  if err != nil && err != os.ErrExist {
    panic(err)
  }
  {{end}}
{{end}}
{{end}}

{{if (and (not .Spread) (not .Debug))}}
  {{if not .Updater.Empty}}
  var f webdav.File
  {{end}}

  {{if $Compression.Compress}}
  {{if not $Compression.Keep}}
  var rb *bytes.Reader
  var r *gzip.Reader
  {{end}}
  {{end}}

  {{range .Files}}
  {{if $Compression.Compress}}
  {{if not $Compression.Keep}}
  rb = bytes.NewReader({{exportedTitle "File"}}{{buildSafeVarName .Path}})
  r, err = gzip.NewReader(rb)
  if err != nil {
    panic(err)
  }

  err = r.Close()
  if err != nil {
    panic(err)
  }
  {{end}}
  {{end}}

  f, err = {{exported "FS"}}.OpenFile({{exported "CTX"}}, "{{.Path}}", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
  if err != nil {
    panic(err)
  }

  {{if $Compression.Compress}}
  {{if not $Compression.Keep}}
  _, err = io.Copy(f, r)
  if err != nil {
    panic(err)
  }
  {{end}}
  {{else}}
  _, err = f.Write({{exportedTitle "File"}}{{buildSafeVarName .Path}})
  if err != nil {
    panic(err)
  }
  {{end}}

  err = f.Close()
  if err != nil {
    panic(err)
  }
  {{end}}
{{end}}

  {{exportedTitle "Handler"}} = &webdav.Handler{
    FileSystem: FS,
    LockSystem: webdav.NewMemLS(),
  }

{{if .Updater.Enabled}}
  go func() {
    svr := &{{exportedTitle "Server"}}{}
    svr.Init()
  }()
{{end}}
}

{{if .Debug}}
var remap = map[string]map[string]string{
  {{.Remap}}
}
{{end}}

// Open a file
func (hfs *{{exported "HTTPFS"}}) Open(path string) (http.File, error) {
{{if .Debug}}
  path = strings.TrimPrefix(path, "/")

  for current, f := range remap {
    if path == current {
      path = f["base"] + strings.TrimPrefix(path, f["prefix"])
      break
    }
  }
{{end}}

  f, err := {{if .Debug}}os{{else}}{{exported "FS"}}{{end}}.OpenFile({{if not .Debug}}{{exported "CTX"}}, {{end}}path, os.O_RDONLY, 0644)
  if err != nil {
    return nil, err
  }

  return f, nil
}

// ReadFile is adapTed from ioutil
func {{exportedTitle "ReadFile"}}(path string) ([]byte, error) {
  f, err := {{if .Debug}}os{{else}}{{exported "FS"}}{{end}}.OpenFile({{if not .Debug}}{{exported "CTX"}}, {{end}}path, os.O_RDONLY, 0644)
  if err != nil {
    return nil, err
  }

  buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))

  // If the buffer overflows, we will get bytes.ErrTooLarge.
  // Return that as an error. Any other panic remains.
  defer func() {
    e := recover()
    if e == nil {
      return
    }
    if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
      err = panicErr
    } else {
      panic(e)
    }
  }()
  _, err = buf.ReadFrom(f)
  return buf.Bytes(), err
}

// WriteFile is adapTed from ioutil
func {{exportedTitle "WriteFile"}}(filename string, data []byte, perm os.FileMode) error {
  f, err := {{exported "FS"}}.OpenFile({{exported "CTX"}}, filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
  if err != nil {
    return err
  }
  n, err := f.Write(data)
  if err == nil && n < len(data) {
    err = io.ErrShortWrite
  }
  if err1 := f.Close(); err == nil {
    err = err1
  }
  return err
}

// WalkDirs looks for files in the given dir and returns a list of files in it
// usage for all files in the b0x: WalkDirs("", false)
func {{exportedTitle "WalkDirs"}}(name string, includeDirsInList bool, files ...string) ([]string, error) {
	f, err := {{exported "FS"}}.OpenFile({{exported "CTX"}}, name, os.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}

	fileInfos, err := f.Readdir(0)
	if err != nil {
    return nil, err
  }

  err = f.Close()
  if err != nil {
		return nil, err
	}

	for _, info := range fileInfos {
		filename := path.Join(name, info.Name())

		if includeDirsInList || !info.IsDir() {
			files = append(files, filename)
		}

		if info.IsDir() {
			files, err = {{exportedTitle "WalkDirs"}}(filename, includeDirsInList, files...)
			if err != nil {
				return nil, err
			}
		}
	}

	return files, nil
}

{{if .Updater.Enabled}}
// Auth holds information for a http basic auth
type {{exportedTitle "Auth"}} struct {
  Username string
  Password string
}

// ResponseInit holds a list of hashes from the server
// to be sent to the client so it can check if there
// is a new file or a changed file
type {{exportedTitle "ResponseInit"}} struct {
  Success bool
  Hashes  map[string]string
}

// Server holds information about the http server
// used to update files remotely
type {{exportedTitle "Server"}} struct {
  Auth {{exportedTitle "Auth"}}
  Files []string
}

// Init sets the routes and basic http auth
// before starting the http server
func (s *{{exportedTitle "Server"}}) Init() {
  s.Auth = {{exportedTitle "Auth"}}{
    Username: "{{.Updater.Username}}",
    Password: "{{.Updater.Password}}",
  }

  e := echo.New()
  e.Use(middleware.Recover())
  e.Use(s.BasicAuth())
  e.POST("/", s.Post)
  e.GET("/", s.Get)

  log.Println("fileb0x updater server is running at port 0.0.0.0:{{.Updater.Port}}")
  if err := e.Start(":{{.Updater.Port}}"); err != nil {
    panic(err)
  }
}

// Get gives a list of file names and hashes
func (s *{{exportedTitle "Server"}}) Get(c echo.Context) error {
  log.Println("[fileb0x.Server]: Hashing server files...")

  // file:hash
  hashes := map[string]string{}

  // get all files in the virtual memory file system
  var err error
  s.Files, err = {{exportedTitle "WalkDirs"}}("", false)
  if err != nil {
    return err
  }

  // get a hash for each file
  for _, filePath := range s.Files {
    f, err := FS.OpenFile(CTX, filePath, os.O_RDONLY, 0644)
    if err != nil {
      return err
    }

    hash := sha256.New()
    _, err = io.Copy(hash, f)
    if err != nil {
      return err
    }

    hashes[filePath] = hex.EncodeToString(hash.Sum(nil))
  }

  log.Println("[fileb0x.Server]: Done hashing files")
  return c.JSON(http.StatusOK, &ResponseInit{
    Success: true,
    Hashes: hashes,
  })
}

// Post is used to upload a file and replace
// it in the virtual memory file system
func (s *{{exportedTitle "Server"}}) Post(c echo.Context) error {
  file, err := c.FormFile("file")
	if err != nil {
		return err
	}

  log.Println("[fileb0x.Server]:", file.Filename, "Found request to upload a file")

	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

  newDir := filepath.Dir(file.Filename)
  _, err = {{exported "FS"}}.Stat({{exported "CTX"}}, newDir)
  if err != nil && strings.HasSuffix(err.Error(), os.ErrNotExist.Error()) {
    log.Println("[fileb0x.Server]: Creating dir tree", newDir)
    list := strings.Split(newDir, "/")
    var tree string

    for _, dir := range list {
      if dir == "" || dir == "." || dir == "/" || dir == "./" {
        continue
      }

      tree += dir + "/"
      err = {{exported "FS"}}.Mkdir({{exported "CTX"}}, tree, 0777)
      if err != nil && err != os.ErrExist {
        log.Println("failed", err)
        return err
      }
    }
  }

  log.Println("[fileb0x.Server]:", file.Filename, "Opening file...")
  f, err := {{exported "FS"}}.OpenFile({{exported "CTX"}}, file.Filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
  if err != nil && !strings.HasSuffix(err.Error(), os.ErrNotExist.Error()) {
    return err
  }

  log.Println("[fileb0x.Server]:", file.Filename, "Writing file into Virutal Memory FileSystem...")
  if _, err = io.Copy(f, src); err != nil {
		return err
	}

  if err = f.Close(); err != nil {
    return err
  }

  log.Println("[fileb0x.Server]:", file.Filename, "Done writing file")
  return c.String(http.StatusOK, "ok")
}

// BasicAuth is a middleware to check if
// the username and password are valid
// echo's middleware isn't used because of golint issues
func (s *{{exportedTitle "Server"}}) BasicAuth() echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			u, p, _ := c.Request().BasicAuth()
			if u != s.Auth.Username || p != s.Auth.Password {
				return echo.ErrUnauthorized
			}

			return next(c)
		}
	}
}
{{end}}
`
<commit_msg>Add ability to set a prefix for HTTPFS<commit_after>package template

var filesTemplate = `{{buildTags .Tags}}// Code generated by fileb0x at "{{.Now}}" from config file "{{.ConfigFile}}" DO NOT EDIT.
// modification hash({{.ModificationHash}})

package {{.Pkg}}
{{$Compression := .Compression}}

import (
  "bytes"
  {{if not .Spread}}{{if and $Compression.Compress (not .Debug)}}{{if not $Compression.Keep}}"compress/gzip"{{end}}{{end}}{{end}}
  "context"
  "io"
  "net/http"
  "os"
  "path"
{{if or .Updater.Enabled .Debug}}
  "strings"
{{end}}

  "golang.org/x/net/webdav"

{{if .Updater.Enabled}}
  "crypto/sha256"
	"encoding/hex"
  "log"
  "path/filepath"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
{{end}}
)

var (
  // CTX is a context for webdav vfs
  {{exported "CTX"}} = context.Background()

  {{if .Debug}}
  {{exported "FS"}} = webdav.Dir(".")
  {{else}}
  // FS is a virtual memory file system
  {{exported "FS"}} = webdav.NewMemFS()
  {{end}}

  // Handler is used to server files through a http handler
  {{exportedTitle "Handler"}} *webdav.Handler

  // HTTP is the http file system
  {{exportedTitle "HTTP"}} http.FileSystem = new({{exported "HTTPFS"}})
)

// HTTPFS implements http.FileSystem
type {{exported "HTTPFS"}} struct {
	// Prefix allows to limit the path of all requests. F.e. a prefix "css" would allow only calls to /css/*
	Prefix string
}

{{if (and (not .Spread) (not .Debug))}}
{{range .Files}}
// {{exportedTitle "File"}}{{buildSafeVarName .Path}} is "{{.Path}}"
var {{exportedTitle "File"}}{{buildSafeVarName .Path}} = {{.Data}}
{{end}}
{{end}}

func init() {
  err := {{exported "CTX"}}.Err()
  if err != nil {
		panic(err)
	}

{{ $length := len .DirList }}
{{ $fLength := len .Files }}
{{ $noDirsButFiles := (and (not .Spread) (eq $length 0) (gt $fLength 0)) }}
{{if not .Debug}}
{{range $index, $dir := .DirList}}
  {{if and (ne $dir "./") (ne $dir "/") (ne $dir ".") (ne $dir "")}}
  err = {{exported "FS"}}.Mkdir({{exported "CTX"}}, "{{$dir}}", 0777)
  if err != nil && err != os.ErrExist {
    panic(err)
  }
  {{end}}
{{end}}
{{end}}

{{if (and (not .Spread) (not .Debug))}}
  {{if not .Updater.Empty}}
  var f webdav.File
  {{end}}

  {{if $Compression.Compress}}
  {{if not $Compression.Keep}}
  var rb *bytes.Reader
  var r *gzip.Reader
  {{end}}
  {{end}}

  {{range .Files}}
  {{if $Compression.Compress}}
  {{if not $Compression.Keep}}
  rb = bytes.NewReader({{exportedTitle "File"}}{{buildSafeVarName .Path}})
  r, err = gzip.NewReader(rb)
  if err != nil {
    panic(err)
  }

  err = r.Close()
  if err != nil {
    panic(err)
  }
  {{end}}
  {{end}}

  f, err = {{exported "FS"}}.OpenFile({{exported "CTX"}}, "{{.Path}}", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
  if err != nil {
    panic(err)
  }

  {{if $Compression.Compress}}
  {{if not $Compression.Keep}}
  _, err = io.Copy(f, r)
  if err != nil {
    panic(err)
  }
  {{end}}
  {{else}}
  _, err = f.Write({{exportedTitle "File"}}{{buildSafeVarName .Path}})
  if err != nil {
    panic(err)
  }
  {{end}}

  err = f.Close()
  if err != nil {
    panic(err)
  }
  {{end}}
{{end}}

  {{exportedTitle "Handler"}} = &webdav.Handler{
    FileSystem: FS,
    LockSystem: webdav.NewMemLS(),
  }

{{if .Updater.Enabled}}
  go func() {
    svr := &{{exportedTitle "Server"}}{}
    svr.Init()
  }()
{{end}}
}

{{if .Debug}}
var remap = map[string]map[string]string{
  {{.Remap}}
}
{{end}}

// Open a file
func (hfs *{{exported "HTTPFS"}}) Open(path string) (http.File, error) {
  path = hfs.Prefix + path

{{if .Debug}}
  path = strings.TrimPrefix(path, "/")

  for current, f := range remap {
    if path == current {
      path = f["base"] + strings.TrimPrefix(path, f["prefix"])
      break
    }
  }

{{end}}
  f, err := {{if .Debug}}os{{else}}{{exported "FS"}}{{end}}.OpenFile({{if not .Debug}}{{exported "CTX"}}, {{end}}path, os.O_RDONLY, 0644)
  if err != nil {
    return nil, err
  }

  return f, nil
}

// ReadFile is adapTed from ioutil
func {{exportedTitle "ReadFile"}}(path string) ([]byte, error) {
  f, err := {{if .Debug}}os{{else}}{{exported "FS"}}{{end}}.OpenFile({{if not .Debug}}{{exported "CTX"}}, {{end}}path, os.O_RDONLY, 0644)
  if err != nil {
    return nil, err
  }

  buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))

  // If the buffer overflows, we will get bytes.ErrTooLarge.
  // Return that as an error. Any other panic remains.
  defer func() {
    e := recover()
    if e == nil {
      return
    }
    if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
      err = panicErr
    } else {
      panic(e)
    }
  }()
  _, err = buf.ReadFrom(f)
  return buf.Bytes(), err
}

// WriteFile is adapTed from ioutil
func {{exportedTitle "WriteFile"}}(filename string, data []byte, perm os.FileMode) error {
  f, err := {{exported "FS"}}.OpenFile({{exported "CTX"}}, filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
  if err != nil {
    return err
  }
  n, err := f.Write(data)
  if err == nil && n < len(data) {
    err = io.ErrShortWrite
  }
  if err1 := f.Close(); err == nil {
    err = err1
  }
  return err
}

// WalkDirs looks for files in the given dir and returns a list of files in it
// usage for all files in the b0x: WalkDirs("", false)
func {{exportedTitle "WalkDirs"}}(name string, includeDirsInList bool, files ...string) ([]string, error) {
	f, err := {{exported "FS"}}.OpenFile({{exported "CTX"}}, name, os.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}

	fileInfos, err := f.Readdir(0)
	if err != nil {
    return nil, err
  }

  err = f.Close()
  if err != nil {
		return nil, err
	}

	for _, info := range fileInfos {
		filename := path.Join(name, info.Name())

		if includeDirsInList || !info.IsDir() {
			files = append(files, filename)
		}

		if info.IsDir() {
			files, err = {{exportedTitle "WalkDirs"}}(filename, includeDirsInList, files...)
			if err != nil {
				return nil, err
			}
		}
	}

	return files, nil
}

{{if .Updater.Enabled}}
// Auth holds information for a http basic auth
type {{exportedTitle "Auth"}} struct {
  Username string
  Password string
}

// ResponseInit holds a list of hashes from the server
// to be sent to the client so it can check if there
// is a new file or a changed file
type {{exportedTitle "ResponseInit"}} struct {
  Success bool
  Hashes  map[string]string
}

// Server holds information about the http server
// used to update files remotely
type {{exportedTitle "Server"}} struct {
  Auth {{exportedTitle "Auth"}}
  Files []string
}

// Init sets the routes and basic http auth
// before starting the http server
func (s *{{exportedTitle "Server"}}) Init() {
  s.Auth = {{exportedTitle "Auth"}}{
    Username: "{{.Updater.Username}}",
    Password: "{{.Updater.Password}}",
  }

  e := echo.New()
  e.Use(middleware.Recover())
  e.Use(s.BasicAuth())
  e.POST("/", s.Post)
  e.GET("/", s.Get)

  log.Println("fileb0x updater server is running at port 0.0.0.0:{{.Updater.Port}}")
  if err := e.Start(":{{.Updater.Port}}"); err != nil {
    panic(err)
  }
}

// Get gives a list of file names and hashes
func (s *{{exportedTitle "Server"}}) Get(c echo.Context) error {
  log.Println("[fileb0x.Server]: Hashing server files...")

  // file:hash
  hashes := map[string]string{}

  // get all files in the virtual memory file system
  var err error
  s.Files, err = {{exportedTitle "WalkDirs"}}("", false)
  if err != nil {
    return err
  }

  // get a hash for each file
  for _, filePath := range s.Files {
    f, err := FS.OpenFile(CTX, filePath, os.O_RDONLY, 0644)
    if err != nil {
      return err
    }

    hash := sha256.New()
    _, err = io.Copy(hash, f)
    if err != nil {
      return err
    }

    hashes[filePath] = hex.EncodeToString(hash.Sum(nil))
  }

  log.Println("[fileb0x.Server]: Done hashing files")
  return c.JSON(http.StatusOK, &ResponseInit{
    Success: true,
    Hashes: hashes,
  })
}

// Post is used to upload a file and replace
// it in the virtual memory file system
func (s *{{exportedTitle "Server"}}) Post(c echo.Context) error {
  file, err := c.FormFile("file")
	if err != nil {
		return err
	}

  log.Println("[fileb0x.Server]:", file.Filename, "Found request to upload a file")

	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

  newDir := filepath.Dir(file.Filename)
  _, err = {{exported "FS"}}.Stat({{exported "CTX"}}, newDir)
  if err != nil && strings.HasSuffix(err.Error(), os.ErrNotExist.Error()) {
    log.Println("[fileb0x.Server]: Creating dir tree", newDir)
    list := strings.Split(newDir, "/")
    var tree string

    for _, dir := range list {
      if dir == "" || dir == "." || dir == "/" || dir == "./" {
        continue
      }

      tree += dir + "/"
      err = {{exported "FS"}}.Mkdir({{exported "CTX"}}, tree, 0777)
      if err != nil && err != os.ErrExist {
        log.Println("failed", err)
        return err
      }
    }
  }

  log.Println("[fileb0x.Server]:", file.Filename, "Opening file...")
  f, err := {{exported "FS"}}.OpenFile({{exported "CTX"}}, file.Filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
  if err != nil && !strings.HasSuffix(err.Error(), os.ErrNotExist.Error()) {
    return err
  }

  log.Println("[fileb0x.Server]:", file.Filename, "Writing file into Virutal Memory FileSystem...")
  if _, err = io.Copy(f, src); err != nil {
		return err
	}

  if err = f.Close(); err != nil {
    return err
  }

  log.Println("[fileb0x.Server]:", file.Filename, "Done writing file")
  return c.String(http.StatusOK, "ok")
}

// BasicAuth is a middleware to check if
// the username and password are valid
// echo's middleware isn't used because of golint issues
func (s *{{exportedTitle "Server"}}) BasicAuth() echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			u, p, _ := c.Request().BasicAuth()
			if u != s.Auth.Username || p != s.Auth.Password {
				return echo.ErrUnauthorized
			}

			return next(c)
		}
	}
}
{{end}}
`
<|endoftext|>


<commit_before>package amqputil

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/fsouza/go-dockerclient"
)

var (
	dockerClient *docker.Client
)

func TestMain(m *testing.M) {
	var err error

	// Start the amqp backend on test startup
	endpoint := "unix:///var/run/docker.sock"
	dockerClient, err = docker.NewClient(endpoint)

	if err != nil {
		fmt.Printf("Error connecting to docker daemon...")
		os.Exit(1)
	}

	// remove any previously executed rabbitmq container with name
	// rabbitmq-tests
	rmRabbit()

	// Execute the tests
	status := m.Run()

	// remove the backend container
	rmRabbit()

	os.Exit(status)
}
func rmRabbit() {
	rmCtnOpt := docker.RemoveContainerOptions{
		ID:            "rabbitmq-tests",
		RemoveVolumes: true,
		Force:         true,
	}

	_ = dockerClient.RemoveContainer(rmCtnOpt)
}

func pullRabbit() error {
	pullOpts := docker.PullImageOptions{
		Repository:   "rabbitmq",
		Tag:          "latest",
		OutputStream: os.Stdout,
	}

	err := dockerClient.PullImage(pullOpts, docker.AuthConfiguration{})

	if err != nil {
		return err
	}

	return nil
}

// runRabbit execute a container with rabbitmq daemon
// If docker fail to create the container, then try to pull the
// last version of rabbitmq image from registry. If fail again,
// then returns error.
// When rabbitmq is successfully started, block until it finish the
// setup and is ready to accept connections.
func runRabbit() (*docker.Container, error) {
	var rabbitCtn *docker.Container

	createContainer := func() (*docker.Container, error) {
		opts := docker.CreateContainerOptions{
			Name: "rabbitmq-tests",
			Config: &docker.Config{
				Image: "rabbitmq",
				ExposedPorts: map[docker.Port]struct{}{
					"5672/tcp": {},
				},
			},
			HostConfig: &docker.HostConfig{
				PortBindings: map[docker.Port][]docker.PortBinding{
					"5672/tcp": []docker.PortBinding{docker.PortBinding{HostPort: "5672"}}},
				PublishAllPorts: true,
				Privileged:      false,
			},
		}

		rabbitCtn, err := dockerClient.CreateContainer(opts)

		return rabbitCtn, err
	}

	rabbitCtn, err := createContainer()

	if err != nil {
		err = pullRabbit()

		if err != nil {
			return nil, err
		}

		rabbitCtn, err = createContainer()

		if err != nil {
			return nil, err
		}
	}

	err = dockerClient.StartContainer(rabbitCtn.ID, nil)

	if err != nil {
		return nil, err
	}

	// block until rabbitmq can accept connections on port 5672
	waitRabbit()

	return rabbitCtn, nil
}

// waitRabbit blocks until rabbitmq can accept connections on
// localhost:5672
func waitRabbit() {
dial:
	conn, err := net.Dial("tcp", "localhost:5672")
	if err != nil {
		time.Sleep(500 * time.Millisecond)
		goto dial
	}

	fmt.Fprintf(conn, "AMQP%00091")
	_, err = bufio.NewReader(conn).ReadString('\n')

	if err != nil && err.Error() != "EOF" {
		conn.Close()
		time.Sleep(500 * time.Millisecond)
		goto dial
	}
}

// TestDial test a simple connection to rabbitmq.
// If the rabbitmq disconnects will not be tested here!
func TestDial(t *testing.T) {
	// Should fail
	conn := New()
	err := conn.Dial("amqp://guest:guest@localhost:5672/%2f")

	if err == nil {
		t.Error("No backend started... Should fail")
		return
	}

	_, err = runRabbit()
	if err != nil {
		t.Error(err)
	}

	err = conn.Dial("amqp://guest:guest@localhost:5672/%2f")

	if err != nil {
		t.Error(err)
		return
	}

	rmRabbit()
}

func TestAutoRedial(t *testing.T) {
	rabbitCtn, err := runRabbit()

	if err != nil {
		t.Errorf("Failed to start rabbitmq: %s", err.Error())
		return
	}

	conn := New()
	err = conn.Dial("amqp://guest:guest@localhost:5672/%2f")

	if err != nil {
		t.Error(err)
		return
	}

	var success = false
	redialErrors := make(chan error)
	conn.AutoRedial(redialErrors, func () {
		// this function should be executed if successfully reconnected
		// here we can recover the application state after reconnect  (if needed)
		success = true
	})

	// required goroutine to consume connection error messages
	go func() {
		for {
			// discards the connection errors
			<-redialErrors
		}
	}()

	dockerClient.StopContainer(rabbitCtn.ID, 3)

	// concurrently starts the rabbitmq after 1 second
	go func() {
		time.Sleep(1 * time.Second)

		err := dockerClient.StartContainer(rabbitCtn.ID, nil)

		if err != nil {
			t.Error(err)
			return
		}
	}()

	// Wait 3 seconds to reconnect
	for i := 0; i < 3; i++ {
		runtime.Gosched()
		if success {
			break
		}

		time.Sleep(time.Second)
	}

	if success == false {
		t.Errorf("Client doesn't reconnect in 3 seconds")
		return
	}

	conn.Close()
}
<commit_msg>[CHANGE] update tests to use conjure library<commit_after>package amqputil

import (
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/tiago4orion/conjure"
)

var (
	rabbitctl *conjure.RabbitMQ
)

func TestMain(m *testing.M) {
	var err error

	ctnClient, err := conjure.NewClient()

	if err != nil || ctnClient == nil {
		panic(err.Error())
	}

	rabbitctl = conjure.NewRabbitMQ(ctnClient)

	// Execute the tests
	status := m.Run()

	// remove the backend container
	rabbitctl.Remove()
	os.Exit(status)
}

// TestDial test a simple connection to rabbitmq.
// If the rabbitmq disconnects will not be tested here!
func TestDial(t *testing.T) {
	// Should fail
	conn := New()
	err := conn.Dial("amqp://guest:guest@localhost:5672/%2f")

	if err == nil {
		t.Error("No backend started... Should fail")
		return
	}

	err = rabbitctl.Run()
	if err != nil {
		t.Error(err)
	}

	err = conn.Dial("amqp://guest:guest@localhost:5672/%2f")

	if err != nil {
		t.Error(err)
		return
	}

	rabbitctl.Remove()
}

func TestAutoRedial(t *testing.T) {
	err :=rabbitctl.Run()

	if err != nil {
		t.Errorf("Failed to start rabbitmq: %s", err.Error())
		return
	}

	conn := New()
	err = conn.Dial("amqp://guest:guest@localhost:5672/%2f")

	if err != nil {
		t.Error(err)
		return
	}

	var success = false
	redialErrors := make(chan error)
	conn.AutoRedial(redialErrors, func() {
		// this function should be executed if successfully reconnected
		// here we can recover the application state after reconnect  (if needed)
		success = true
	})

	// required goroutine to consume connection error messages
	go func() {
		for {
			// discards the connection errors
			<-redialErrors
		}
	}()

	rabbitctl.Stop()

	// concurrently starts the rabbitmq after 1 second
	go func() {
		time.Sleep(1 * time.Second)

		err := rabbitctl.Start()

		if err != nil {
			t.Error(err)
			return
		}
	}()

	// Wait 3 seconds to reconnect
	for i := 0; i < 3; i++ {
		runtime.Gosched()
		if success {
			break
		}

		time.Sleep(time.Second)
	}

	if success == false {
		t.Errorf("Client doesn't reconnect in 3 seconds")
		return
	}

	conn.Close()
}
<|endoftext|>


<commit_before>// Package disk provides utilities for working directly with a disk
//
// Most of the provided functions are intelligent wrappers around implementations of
// github.com/diskfs/go-diskfs/partition and github.com/diskfs/go-diskfs/filesystem
package disk

import (
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/diskfs/go-diskfs/filesystem"
	"github.com/diskfs/go-diskfs/filesystem/fat32"
	"github.com/diskfs/go-diskfs/filesystem/iso9660"
	"github.com/diskfs/go-diskfs/partition"
)

// Disk is a reference to a single disk block device or image that has been Create() or Open()
type Disk struct {
	File              *os.File
	Info              os.FileInfo
	Type              Type
	Size              int64
	LogicalBlocksize  int64
	PhysicalBlocksize int64
	Table             partition.Table
	Writable          bool
}

// Type represents the type of disk this is
type Type int

const (
	// File is a file-based disk image
	File Type = iota
	// Device is an OS-managed block device
	Device
	// blkRRPart is the ioctl request to re-read the partition table
	blkRRPart = 0x0000125F
)

var (
	errIncorrectOpenMode = errors.New("disk file or device not open for write")
)

// GetPartitionTable retrieves a PartitionTable for a Disk
//
// returns an error if the Disk is invalid or does not exist, or the partition table is unknown
func (d *Disk) GetPartitionTable() (partition.Table, error) {
	return partition.Read(d.File, int(d.LogicalBlocksize), int(d.PhysicalBlocksize))
}

// Partition applies a partition.Table implementation to a Disk
//
// The Table can have zero, one or more Partitions, each of which is unique to its
// implementation. E.g. MBR partitions in mbr.Table look different from GPT partitions in gpt.Table
//
// Actual writing of the table is delegated to the individual implementation
func (d *Disk) Partition(table partition.Table) error {
	if !d.Writable {
		return errIncorrectOpenMode
	}
	// fill in the uuid
	err := table.Write(d.File, d.Size)
	if err != nil {
		return fmt.Errorf("Failed to write partition table: %v", err)
	}
	d.Table = table
	// the partition table needs to be re-read only if
	// the disk file is an actual block device
	if d.Type == Device {
		err = d.ReReadPartitionTable()
		if err != nil {
			return fmt.Errorf("Unable to re-read the partition table. Kernel still uses old partition table: %v", err)
		}
	}
	return nil
}

// WritePartitionContents writes the contents of an io.Reader to a given partition
//
// if successful, returns the number of bytes written
//
// returns an error if there was an error writing to the disk, reading from the reader, the table
// is invalid, or the partition is invalid
func (d *Disk) WritePartitionContents(partition int, reader io.Reader) (int64, error) {
	if !d.Writable {
		return -1, errIncorrectOpenMode
	}
	if d.Table == nil {
		return -1, fmt.Errorf("cannot write contents of a partition on a disk without a partition table")
	}
	if partition < 0 {
		return -1, fmt.Errorf("cannot write contents of a partition without specifying a partition")
	}
	written, err := d.Table.WritePartitionContents(partition, d.File, reader)
	return int64(written), err
}

// ReadPartitionContents reads the contents of a partition to an io.Writer
//
// if successful, returns the number of bytes read
//
// returns an error if there was an error reading from the disk, writing to the writer, the table
// is invalid, or the partition is invalid
func (d *Disk) ReadPartitionContents(partition int, writer io.Writer) (int64, error) {
	if d.Table == nil {
		return -1, fmt.Errorf("cannot read contents of a partition on a disk without a partition table")
	}
	if partition < 0 {
		return -1, fmt.Errorf("cannot read contents of a partition without specifying a partition")
	}
	return d.Table.ReadPartitionContents(partition, d.File, writer)
}

// FilesystemSpec represents the specification of a filesystem to be created
type FilesystemSpec struct {
	Partition   int
	FSType      filesystem.Type
	VolumeLabel string
}

// CreateFilesystem creates a filesystem on a disk image, the equivalent of mkfs.
//
// Required:
// * desired partition number, or 0 to create the filesystem on the entire block device or
//   disk image,
// * the filesystem type from github.com/diskfs/go-diskfs/filesystem
//
// Optional:
// * volume label for those filesystems that support it; under Linux this shows
//   in '/dev/disks/by-label/<label>'
//
// if successful, returns a filesystem-implementing structure for the given filesystem type
//
// returns error if there was an error creating the filesystem, or the partition table is invalid and did not
// request the entire disk.
func (d *Disk) CreateFilesystem(spec FilesystemSpec) (filesystem.FileSystem, error) {
	// find out where the partition starts and ends, or if it is the entire disk
	var (
		size, start int64
		err         error
	)
	switch {
	case !d.Writable:
		return nil, errIncorrectOpenMode
	case spec.Partition == 0:
		size = d.Size
		start = 0
	case d.Table == nil:
		return nil, fmt.Errorf("cannot create filesystem on a partition without a partition table")
	default:
		size, err = d.Table.GetPartitionSize(spec.Partition)
		if err != nil {
			return nil, fmt.Errorf("error getting size of partition %d: %v", spec.Partition, err)
		}
		start, err = d.Table.GetPartitionStart(spec.Partition)
		if err != nil {
			return nil, fmt.Errorf("error getting start of partition %d: %v", spec.Partition, err)
		}
	}

	switch spec.FSType {
	case filesystem.TypeFat32:
		return fat32.Create(d.File, size, start, d.LogicalBlocksize, spec.VolumeLabel)
	case filesystem.TypeISO9660:
		return iso9660.Create(d.File, size, start, d.LogicalBlocksize)
	default:
		return nil, errors.New("Unknown filesystem type requested")
	}
}

// GetFilesystem gets the filesystem that already exists on a disk image
//
// pass the desired partition number, or 0 to create the filesystem on the entire block device / disk image,
//
// if successful, returns a filesystem-implementing structure for the given filesystem type
//
// returns error if there was an error reading the filesystem, or the partition table is invalid and did not
// request the entire disk.
func (d *Disk) GetFilesystem(partition int) (filesystem.FileSystem, error) {
	// find out where the partition starts and ends, or if it is the entire disk
	var (
		size, start int64
		err         error
	)

	switch {
	case partition == 0:
		size = d.Size
		start = 0
	case d.Table == nil:
		return nil, fmt.Errorf("cannot read filesystem on a partition without a partition table")
	default:
		size, err = d.Table.GetPartitionSize(partition)
		if err != nil {
			return nil, fmt.Errorf("error getting size of partition %d: %v", partition, err)
		}
		start, err = d.Table.GetPartitionStart(partition)
		if err != nil {
			return nil, fmt.Errorf("error getting start of partition %d: %v", partition, err)
		}
	}

	// just try each type
	fat32FS, err := fat32.Read(d.File, size, start, d.LogicalBlocksize)
	if err == nil {
		return fat32FS, nil
	}
	iso9660FS, err := iso9660.Read(d.File, size, start, d.LogicalBlocksize)
	if err == nil {
		return iso9660FS, nil
	}
	return nil, fmt.Errorf("Unknown filesystem on partition %d", partition)
}
<commit_msg>use unix.BLKRRPART and remove custom key for ioctl request<commit_after>// Package disk provides utilities for working directly with a disk
//
// Most of the provided functions are intelligent wrappers around implementations of
// github.com/diskfs/go-diskfs/partition and github.com/diskfs/go-diskfs/filesystem
package disk

import (
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/diskfs/go-diskfs/filesystem"
	"github.com/diskfs/go-diskfs/filesystem/fat32"
	"github.com/diskfs/go-diskfs/filesystem/iso9660"
	"github.com/diskfs/go-diskfs/partition"
)

// Disk is a reference to a single disk block device or image that has been Create() or Open()
type Disk struct {
	File              *os.File
	Info              os.FileInfo
	Type              Type
	Size              int64
	LogicalBlocksize  int64
	PhysicalBlocksize int64
	Table             partition.Table
	Writable          bool
}

// Type represents the type of disk this is
type Type int

const (
	// File is a file-based disk image
	File Type = iota
	// Device is an OS-managed block device
	Device
)

var (
	errIncorrectOpenMode = errors.New("disk file or device not open for write")
)

// GetPartitionTable retrieves a PartitionTable for a Disk
//
// returns an error if the Disk is invalid or does not exist, or the partition table is unknown
func (d *Disk) GetPartitionTable() (partition.Table, error) {
	return partition.Read(d.File, int(d.LogicalBlocksize), int(d.PhysicalBlocksize))
}

// Partition applies a partition.Table implementation to a Disk
//
// The Table can have zero, one or more Partitions, each of which is unique to its
// implementation. E.g. MBR partitions in mbr.Table look different from GPT partitions in gpt.Table
//
// Actual writing of the table is delegated to the individual implementation
func (d *Disk) Partition(table partition.Table) error {
	if !d.Writable {
		return errIncorrectOpenMode
	}
	// fill in the uuid
	err := table.Write(d.File, d.Size)
	if err != nil {
		return fmt.Errorf("Failed to write partition table: %v", err)
	}
	d.Table = table
	// the partition table needs to be re-read only if
	// the disk file is an actual block device
	if d.Type == Device {
		err = d.ReReadPartitionTable()
		if err != nil {
			return fmt.Errorf("Unable to re-read the partition table. Kernel still uses old partition table: %v", err)
		}
	}
	return nil
}

// WritePartitionContents writes the contents of an io.Reader to a given partition
//
// if successful, returns the number of bytes written
//
// returns an error if there was an error writing to the disk, reading from the reader, the table
// is invalid, or the partition is invalid
func (d *Disk) WritePartitionContents(partition int, reader io.Reader) (int64, error) {
	if !d.Writable {
		return -1, errIncorrectOpenMode
	}
	if d.Table == nil {
		return -1, fmt.Errorf("cannot write contents of a partition on a disk without a partition table")
	}
	if partition < 0 {
		return -1, fmt.Errorf("cannot write contents of a partition without specifying a partition")
	}
	written, err := d.Table.WritePartitionContents(partition, d.File, reader)
	return int64(written), err
}

// ReadPartitionContents reads the contents of a partition to an io.Writer
//
// if successful, returns the number of bytes read
//
// returns an error if there was an error reading from the disk, writing to the writer, the table
// is invalid, or the partition is invalid
func (d *Disk) ReadPartitionContents(partition int, writer io.Writer) (int64, error) {
	if d.Table == nil {
		return -1, fmt.Errorf("cannot read contents of a partition on a disk without a partition table")
	}
	if partition < 0 {
		return -1, fmt.Errorf("cannot read contents of a partition without specifying a partition")
	}
	return d.Table.ReadPartitionContents(partition, d.File, writer)
}

// FilesystemSpec represents the specification of a filesystem to be created
type FilesystemSpec struct {
	Partition   int
	FSType      filesystem.Type
	VolumeLabel string
}

// CreateFilesystem creates a filesystem on a disk image, the equivalent of mkfs.
//
// Required:
// * desired partition number, or 0 to create the filesystem on the entire block device or
//   disk image,
// * the filesystem type from github.com/diskfs/go-diskfs/filesystem
//
// Optional:
// * volume label for those filesystems that support it; under Linux this shows
//   in '/dev/disks/by-label/<label>'
//
// if successful, returns a filesystem-implementing structure for the given filesystem type
//
// returns error if there was an error creating the filesystem, or the partition table is invalid and did not
// request the entire disk.
func (d *Disk) CreateFilesystem(spec FilesystemSpec) (filesystem.FileSystem, error) {
	// find out where the partition starts and ends, or if it is the entire disk
	var (
		size, start int64
		err         error
	)
	switch {
	case !d.Writable:
		return nil, errIncorrectOpenMode
	case spec.Partition == 0:
		size = d.Size
		start = 0
	case d.Table == nil:
		return nil, fmt.Errorf("cannot create filesystem on a partition without a partition table")
	default:
		size, err = d.Table.GetPartitionSize(spec.Partition)
		if err != nil {
			return nil, fmt.Errorf("error getting size of partition %d: %v", spec.Partition, err)
		}
		start, err = d.Table.GetPartitionStart(spec.Partition)
		if err != nil {
			return nil, fmt.Errorf("error getting start of partition %d: %v", spec.Partition, err)
		}
	}

	switch spec.FSType {
	case filesystem.TypeFat32:
		return fat32.Create(d.File, size, start, d.LogicalBlocksize, spec.VolumeLabel)
	case filesystem.TypeISO9660:
		return iso9660.Create(d.File, size, start, d.LogicalBlocksize)
	default:
		return nil, errors.New("Unknown filesystem type requested")
	}
}

// GetFilesystem gets the filesystem that already exists on a disk image
//
// pass the desired partition number, or 0 to create the filesystem on the entire block device / disk image,
//
// if successful, returns a filesystem-implementing structure for the given filesystem type
//
// returns error if there was an error reading the filesystem, or the partition table is invalid and did not
// request the entire disk.
func (d *Disk) GetFilesystem(partition int) (filesystem.FileSystem, error) {
	// find out where the partition starts and ends, or if it is the entire disk
	var (
		size, start int64
		err         error
	)

	switch {
	case partition == 0:
		size = d.Size
		start = 0
	case d.Table == nil:
		return nil, fmt.Errorf("cannot read filesystem on a partition without a partition table")
	default:
		size, err = d.Table.GetPartitionSize(partition)
		if err != nil {
			return nil, fmt.Errorf("error getting size of partition %d: %v", partition, err)
		}
		start, err = d.Table.GetPartitionStart(partition)
		if err != nil {
			return nil, fmt.Errorf("error getting start of partition %d: %v", partition, err)
		}
	}

	// just try each type
	fat32FS, err := fat32.Read(d.File, size, start, d.LogicalBlocksize)
	if err == nil {
		return fat32FS, nil
	}
	iso9660FS, err := iso9660.Read(d.File, size, start, d.LogicalBlocksize)
	if err == nil {
		return iso9660FS, nil
	}
	return nil, fmt.Errorf("Unknown filesystem on partition %d", partition)
}
<|endoftext|>


<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package components

import (
	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/utils/pointer"
	. "github.com/TheThingsNetwork/ttn/utils/testing"
	"github.com/brocaar/lorawan"
	"reflect"
	"testing"
	"time"
)

func TestStoragePartition(t *testing.T) {
	// CONVENTION below -> first DevAddr byte will be used as falue for FPort
	setup := []handlerEntry{
		{ // App #1, Dev #1
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
			NwkSKey: lorawan.AES128Key([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{0, 0, 0, 1}),
		},
		{ // App #1, Dev #2
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
			NwkSKey: lorawan.AES128Key([16]byte{0, 0xa, 0xb, 1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{14, 14, 14, 14, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{10, 0, 0, 2}),
		},
		{ // App #1, Dev #3
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
			NwkSKey: lorawan.AES128Key([16]byte{12, 0xa, 0xb, 1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{0xb, 15, 14, 14, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{14, 0, 0, 3}),
		},
		{ // App #2, Dev #1
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 2}),
			NwkSKey: lorawan.AES128Key([16]byte{0, 0xa, 0xb, 5, 12, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{14, 14, 14, 14, 1, 11, 10, 0xc, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{0, 0, 0, 1}),
		},
		{ // App #2, Dev #2
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 2}),
			NwkSKey: lorawan.AES128Key([16]byte{0, 0xa, 0xb, 5, 12, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{14, 14, 14, 14, 1, 11, 10, 0xc, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{23, 0xaf, 0x14, 1}),
		},
	}

	unknown := handlerEntry{ // App #1, Dev #4
		AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
		NwkSKey: lorawan.AES128Key([16]byte{1, 2, 3, 4, 23, 6, 7, 8, 9, 0x19, 11, 12, 13, 14, 15, 16}),
		AppSKey: lorawan.AES128Key([16]byte{16, 0xba, 14, 13, 2, 11, 58, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
		DevAddr: lorawan.DevAddr([4]byte{0, 0, 0, 4}),
	}

	tests := []struct {
		Desc           string
		PacketsShape   []handlerEntry
		WantPartitions []partitionShape
		WantError      error
	}{
		{
			Desc:           "1 packet -> 1 partition | 1 packet",
			PacketsShape:   []handlerEntry{setup[0]},
			WantPartitions: []partitionShape{{setup[0], 1}},
			WantError:      nil,
		},
		{
			Desc:           "1 unknown packet -> error not found",
			PacketsShape:   []handlerEntry{unknown},
			WantPartitions: nil,
			WantError:      ErrNotFound,
		},
		{
			Desc:           "2 packets | diff DevAddr & diff AppEUI -> 2 partitions | 1 packet",
			PacketsShape:   []handlerEntry{setup[0], setup[4]},
			WantPartitions: []partitionShape{{setup[0], 1}, {setup[4], 1}},
			WantError:      nil,
		},
		{
			Desc:           "2 packets | same DevAddr & diff AppEUI -> 2 partitions | 1 packet",
			PacketsShape:   []handlerEntry{setup[0], setup[3]},
			WantPartitions: []partitionShape{{setup[0], 1}, {setup[3], 1}},
			WantError:      nil,
		},
		{
			Desc:           "3 packets | diff DevAddr & same AppEUI -> 3 partitions | 1 packet",
			PacketsShape:   []handlerEntry{setup[0], setup[1], setup[2]},
			WantPartitions: []partitionShape{{setup[0], 1}, {setup[1], 1}, {setup[2], 1}},
			WantError:      nil,
		},
		{
			Desc:           "3 packets | same DevAddr & same AppEUI -> 1 partitions | 3 packets",
			PacketsShape:   []handlerEntry{setup[0], setup[0], setup[0]},
			WantPartitions: []partitionShape{{setup[0], 3}},
			WantError:      nil,
		},
		{
			Desc:           "5 packets | same DevAddr & various AppEUI -> 2 partitions | 3 packets & 2 packets",
			PacketsShape:   []handlerEntry{setup[0], setup[0], setup[0], setup[3], setup[3]},
			WantPartitions: []partitionShape{{setup[0], 3}, {setup[3], 2}},
			WantError:      nil,
		},
	}

	for _, test := range tests {
		// Describe
		Desc(t, test.Desc)

		// Build
		db := genFilledHandlerStorage(setup)
		packets := genPacketsFromHandlerEntries(test.PacketsShape)

		// Operate
		partitions, err := db.partition(packets)

		// Check
		checkErrors(t, test.WantError, err)
		checkPartitions(t, test.WantPartitions, partitions)
	}
}

type partitionShape struct {
	handlerEntry
	PacketNb int
}

// ----- BUILD utilities

func genFilledHandlerStorage(setup []handlerEntry) (db handlerStorage) {
	db = newHandlerDB()

	for _, entry := range setup {
		if err := db.store(entry.DevAddr, entry); err != nil {
			panic(err)
		}
	}

	return db
}

func genPacketsFromHandlerEntries(shapes []handlerEntry) []core.Packet {
	var packets []core.Packet
	for _, entry := range shapes {

		// Build the macPayload
		macPayload := lorawan.NewMACPayload(true)
		macPayload.FHDR = lorawan.FHDR{DevAddr: entry.DevAddr}
		macPayload.FRMPayload = []lorawan.Payload{&lorawan.DataPayload{
			Bytes: []byte(time.Now().String()),
		}}
		macPayload.FPort = uint8(entry.DevAddr[0])
		key := entry.AppSKey
		if macPayload.FPort == 0 {
			key = entry.NwkSKey
		}
		if err := macPayload.EncryptFRMPayload(key); err != nil {
			panic(err)
		}

		// Build the physicalPayload
		phyPayload := lorawan.NewPHYPayload(true)
		phyPayload.MHDR = lorawan.MHDR{
			MType: lorawan.ConfirmedDataUp,
			Major: lorawan.LoRaWANR1,
		}
		phyPayload.MACPayload = macPayload
		if err := phyPayload.SetMIC(entry.NwkSKey); err != nil {
			panic(err)
		}

		// Finally build the packet
		packets = append(packets, core.Packet{
			Metadata: core.Metadata{
				Rssi: pointer.Int(-20),
				Datr: pointer.String("SF7BW125"),
				Modu: pointer.String("Lora"),
			},
			Payload: phyPayload,
		})
	}
	return packets
}

// ----- CHECK utilities

func checkErrors(t *testing.T, want error, got error) {
	if want == got {
		Ok(t, "Check errors")
		return
	}
	Ko(t, "Expected error to be %v, but got %v", want, got)
}

func checkPartitions(t *testing.T, want []partitionShape, got []handlerPartition) {
	if len(want) != len(got) {
		Ko(t, "Expected %d partitions, but got %d", len(want), len(got))
		return
	}

browseGot:
	for _, gotPartition := range got {
		for _, wantPartition := range want { // Find right wanted partition
			if reflect.DeepEqual(wantPartition.handlerEntry, gotPartition.handlerEntry) {
				if len(gotPartition.Packets) == wantPartition.PacketNb {
					continue browseGot
				}
				Ko(t, "Partition don't match expectations.\nWant: %v\nGot:  %v", wantPartition, gotPartition)
				return
			}
		}
		Ko(t, "Got a partition that wasn't expected: %v", gotPartition)
		return
	}
	Ok(t, "Check partitions")
}
<commit_msg>[handler] Format packages import in test<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package components

import (
	"reflect"
	"testing"
	"time"

	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/utils/pointer"
	. "github.com/TheThingsNetwork/ttn/utils/testing"
	"github.com/brocaar/lorawan"
)

func TestStoragePartition(t *testing.T) {
	// CONVENTION below -> first DevAddr byte will be used as falue for FPort
	setup := []handlerEntry{
		{ // App #1, Dev #1
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
			NwkSKey: lorawan.AES128Key([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{0, 0, 0, 1}),
		},
		{ // App #1, Dev #2
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
			NwkSKey: lorawan.AES128Key([16]byte{0, 0xa, 0xb, 1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{14, 14, 14, 14, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{10, 0, 0, 2}),
		},
		{ // App #1, Dev #3
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
			NwkSKey: lorawan.AES128Key([16]byte{12, 0xa, 0xb, 1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{0xb, 15, 14, 14, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{14, 0, 0, 3}),
		},
		{ // App #2, Dev #1
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 2}),
			NwkSKey: lorawan.AES128Key([16]byte{0, 0xa, 0xb, 5, 12, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{14, 14, 14, 14, 1, 11, 10, 0xc, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{0, 0, 0, 1}),
		},
		{ // App #2, Dev #2
			AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 2}),
			NwkSKey: lorawan.AES128Key([16]byte{0, 0xa, 0xb, 5, 12, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
			AppSKey: lorawan.AES128Key([16]byte{14, 14, 14, 14, 1, 11, 10, 0xc, 8, 7, 6, 5, 4, 3, 2, 1}),
			DevAddr: lorawan.DevAddr([4]byte{23, 0xaf, 0x14, 1}),
		},
	}

	unknown := handlerEntry{ // App #1, Dev #4
		AppEUI:  lorawan.EUI64([8]byte{0, 0, 0, 0, 0, 0, 0, 1}),
		NwkSKey: lorawan.AES128Key([16]byte{1, 2, 3, 4, 23, 6, 7, 8, 9, 0x19, 11, 12, 13, 14, 15, 16}),
		AppSKey: lorawan.AES128Key([16]byte{16, 0xba, 14, 13, 2, 11, 58, 9, 8, 7, 6, 5, 4, 3, 2, 1}),
		DevAddr: lorawan.DevAddr([4]byte{1, 0, 0, 4}),
	}

	tests := []struct {
		Desc           string
		PacketsShape   []handlerEntry
		WantPartitions []partitionShape
		WantError      error
	}{
		{
			Desc:           "1 packet -> 1 partition | 1 packet",
			PacketsShape:   []handlerEntry{setup[0]},
			WantPartitions: []partitionShape{{setup[0], 1}},
			WantError:      nil,
		},
		{
			Desc:           "1 unknown packet -> error not found",
			PacketsShape:   []handlerEntry{unknown},
			WantPartitions: nil,
			WantError:      ErrNotFound,
		},
		{
			Desc:           "2 packets | diff DevAddr & diff AppEUI -> 2 partitions | 1 packet",
			PacketsShape:   []handlerEntry{setup[0], setup[4]},
			WantPartitions: []partitionShape{{setup[0], 1}, {setup[4], 1}},
			WantError:      nil,
		},
		{
			Desc:           "2 packets | same DevAddr & diff AppEUI -> 2 partitions | 1 packet",
			PacketsShape:   []handlerEntry{setup[0], setup[3]},
			WantPartitions: []partitionShape{{setup[0], 1}, {setup[3], 1}},
			WantError:      nil,
		},
		{
			Desc:           "3 packets | diff DevAddr & same AppEUI -> 3 partitions | 1 packet",
			PacketsShape:   []handlerEntry{setup[0], setup[1], setup[2]},
			WantPartitions: []partitionShape{{setup[0], 1}, {setup[1], 1}, {setup[2], 1}},
			WantError:      nil,
		},
		{
			Desc:           "3 packets | same DevAddr & same AppEUI -> 1 partitions | 3 packets",
			PacketsShape:   []handlerEntry{setup[0], setup[0], setup[0]},
			WantPartitions: []partitionShape{{setup[0], 3}},
			WantError:      nil,
		},
		{
			Desc:           "5 packets | same DevAddr & various AppEUI -> 2 partitions | 3 packets & 2 packets",
			PacketsShape:   []handlerEntry{setup[0], setup[0], setup[0], setup[3], setup[3]},
			WantPartitions: []partitionShape{{setup[0], 3}, {setup[3], 2}},
			WantError:      nil,
		},
	}

	for _, test := range tests {
		// Describe
		Desc(t, test.Desc)

		// Build
		db := genFilledHandlerStorage(setup)
		packets := genPacketsFromHandlerEntries(test.PacketsShape)

		// Operate
		partitions, err := db.partition(packets)

		// Check
		checkErrors(t, test.WantError, err)
		checkPartitions(t, test.WantPartitions, partitions)
	}
}

type partitionShape struct {
	handlerEntry
	PacketNb int
}

// ----- BUILD utilities

func genFilledHandlerStorage(setup []handlerEntry) (db handlerStorage) {
	db = newHandlerDB()

	for _, entry := range setup {
		if err := db.store(entry.DevAddr, entry); err != nil {
			panic(err)
		}
	}

	return db
}

func genPacketsFromHandlerEntries(shapes []handlerEntry) []core.Packet {
	var packets []core.Packet
	for _, entry := range shapes {

		// Build the macPayload
		macPayload := lorawan.NewMACPayload(true)
		macPayload.FHDR = lorawan.FHDR{DevAddr: entry.DevAddr}
		macPayload.FRMPayload = []lorawan.Payload{&lorawan.DataPayload{
			Bytes: []byte(time.Now().String()),
		}}
		macPayload.FPort = uint8(entry.DevAddr[0])
		key := entry.AppSKey
		if macPayload.FPort == 0 {
			key = entry.NwkSKey
		}
		if err := macPayload.EncryptFRMPayload(key); err != nil {
			panic(err)
		}

		// Build the physicalPayload
		phyPayload := lorawan.NewPHYPayload(true)
		phyPayload.MHDR = lorawan.MHDR{
			MType: lorawan.ConfirmedDataUp,
			Major: lorawan.LoRaWANR1,
		}
		phyPayload.MACPayload = macPayload
		if err := phyPayload.SetMIC(entry.NwkSKey); err != nil {
			panic(err)
		}

		// Finally build the packet
		packets = append(packets, core.Packet{
			Metadata: core.Metadata{
				Rssi: pointer.Int(-20),
				Datr: pointer.String("SF7BW125"),
				Modu: pointer.String("Lora"),
			},
			Payload: phyPayload,
		})
	}
	return packets
}

// ----- CHECK utilities

func checkErrors(t *testing.T, want error, got error) {
	if want == got {
		Ok(t, "Check errors")
		return
	}
	Ko(t, "Expected error to be %v, but got %v", want, got)
}

func checkPartitions(t *testing.T, want []partitionShape, got []handlerPartition) {
	if len(want) != len(got) {
		Ko(t, "Expected %d partitions, but got %d", len(want), len(got))
		return
	}

browseGot:
	for _, gotPartition := range got {
		for _, wantPartition := range want { // Find right wanted partition
			if reflect.DeepEqual(wantPartition.handlerEntry, gotPartition.handlerEntry) {
				if len(gotPartition.Packets) == wantPartition.PacketNb {
					continue browseGot
				}
				Ko(t, "Partition don't match expectations.\nWant: %v\nGot:  %v", wantPartition, gotPartition)
				return
			}
		}
		Ko(t, "Got a partition that wasn't expected: %v", gotPartition)
		return
	}
	Ok(t, "Check partitions")
}
<|endoftext|>


<commit_before>package conf

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"reflect"
	"sync"

	"github.com/Cepave/open-falcon-backend/modules/query/utils"
)

type Gconfig struct {
	Funcations []FunConfig
}

type FunConfig struct {
	FuncationName string   `json:"funcation_name"`
	FilePath      string   `json:"file_path"`
	Params        []string `json:"params"`
	Description   string   `json:"description"`
	Codes         string   `json:"-"`
}

var (
	gconfig     []*FunConfig
	configLock  = new(sync.RWMutex)
	confpath    *string
	FunctionMap map[string]*FunConfig
)

func Config() map[string]*FunConfig {
	configLock.RLock()
	defer configLock.RUnlock()
	return FunctionMap
}

func functionMapGen() {
	f := "./"
	currentPath, _ := os.Getwd()
	if _, err := os.Stat(fmt.Sprintf("%s/js", currentPath)); err == nil {
		f = fmt.Sprintf("%s/js", currentPath)
	} else if _, err := os.Stat(fmt.Sprintf("%s/../js", currentPath)); err == nil {
		f = fmt.Sprintf("%s/../js", currentPath)
	} else if _, err := os.Stat(fmt.Sprintf("%s/../config/js", currentPath)); err == nil {
		f = fmt.Sprintf("%s/../config/js", currentPath)
	} else {
		log.Fatalf("load js files got error, currentPaht: %s , please check your code tree and make is correct!", currentPath)
	}
	FunctionMap = map[string]*FunConfig{}
	for _, v := range gconfig {
		contain := jsFileReader(fmt.Sprintf("%s/%s", f, v.FilePath))
		v.Codes = contain
		FunctionMap[v.FuncationName] = v
	}
}

func ReadConf() {
	var f string
	currentPath, _ := os.Getwd()
	if _, err := os.Stat(fmt.Sprintf("%s/conf/lambdaSetup.json", currentPath)); err == nil {
		f = fmt.Sprintf("%s/conf/lambdaSetup.json", currentPath)
	} else if _, err := os.Stat(fmt.Sprintf("%s/../config/lambdaSetup.json", currentPath)); err == nil {
		f = fmt.Sprintf("%s/../config/lambdaSetup.json", currentPath)
	} else {
		log.Fatalf("lambdaSetup.json not found, currentPaht: %s", currentPath)
	}
	confpath = &f
	dat, err := ioutil.ReadFile(f)
	if err != nil {
		log.Println(err)
	}
	var myconf []*FunConfig
	json.Unmarshal(dat, &myconf)
	if len(myconf) == 0 {
		log.Println("conf file is empty or format is wrong, please check it!")
	}
	gconfig = myconf
	functionMapGen()
}

func Reload() {
	configLock.RLock()
	ReadConf()
	defer configLock.RUnlock()
}

func GetFunc(key string) *FunConfig {
	return FunctionMap[key]
}

func GetAvaibleFun() []string {
	return utils.GetMapKeys(reflect.ValueOf(FunctionMap).MapKeys())
}
<commit_msg>changed lambda / js conf reading way for query<commit_after>package conf

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"sync"

	log "github.com/Sirupsen/logrus"

	"github.com/Cepave/open-falcon-backend/modules/query/g"
	"github.com/Cepave/open-falcon-backend/modules/query/utils"
)

type Gconfig struct {
	Funcations []FunConfig
}

type FunConfig struct {
	FuncationName string   `json:"funcation_name"`
	FilePath      string   `json:"file_path"`
	Params        []string `json:"params"`
	Description   string   `json:"description"`
	Codes         string   `json:"-"`
}

var (
	gconfig     []*FunConfig
	configLock  = new(sync.RWMutex)
	confpath    *string
	FunctionMap map[string]*FunConfig
)

func Config() map[string]*FunConfig {
	configLock.RLock()
	defer configLock.RUnlock()
	return FunctionMap
}

func functionMapGen() {
	currentPath := g.Config().RootDir
	possiblePath := []string{"conf/js", "js"}
	f := ""
	for _, pa := range possiblePath {
		paf := fmt.Sprintf("%s/%s", currentPath, pa)
		if _, err := os.Stat(paf); err != nil {
			log.Debugf("can't not load file from: %s", paf)
		} else {
			f = paf
			break
		}
	}
	if f == "" {
		log.Fatalf("load js files got error, currentPaht: %s , please check your code tree and make is correct!", currentPath)
	}

	FunctionMap = map[string]*FunConfig{}
	for _, v := range gconfig {
		contain := jsFileReader(fmt.Sprintf("%s/%s", f, v.FilePath))
		v.Codes = contain
		FunctionMap[v.FuncationName] = v
	}
}

func ReadConf() {
	currentPath := g.Config().RootDir
	possiblePath := []string{"conf/lambdaSetup.json", "config/lambdaSetup.json"}
	f := ""
	for _, pa := range possiblePath {
		paf := fmt.Sprintf("%s/%s", currentPath, pa)
		if _, err := os.Stat(paf); err != nil {
			log.Debugf("can't not load file from: %s", paf)
		} else {
			f = paf
			break
		}
	}
	if f == "" {
		log.Fatalf("lambdaSetup.json not found, currentPaht: %s", currentPath)
	}

	confpath = &f
	dat, err := ioutil.ReadFile(f)
	if err != nil {
		log.Println(err)
	}
	var myconf []*FunConfig
	json.Unmarshal(dat, &myconf)
	if len(myconf) == 0 {
		log.Println("conf file is empty or format is wrong, please check it!")
	}
	gconfig = myconf
	functionMapGen()
}

func Reload() {
	configLock.RLock()
	ReadConf()
	defer configLock.RUnlock()
}

func GetFunc(key string) *FunConfig {
	return FunctionMap[key]
}

func GetAvaibleFun() []string {
	return utils.GetMapKeys(reflect.ValueOf(FunctionMap).MapKeys())
}
<|endoftext|>


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

package xbo

import (
	"math/rand"
	"testing"
	"time"
)

func TestBoundaryErrors(t *testing.T) {
	// Testing each of the bounded BackOff types
	offs := []BackOff{
		MaxAttempts(NewStop(), 0, false),
		MaxAttempts(NewStop(), 0, true),
		Ceiling(NewStop(), 0),
		Elapsed(NewStop(), 0),
	}
	resets := []bool{true, false}

	for ct, off := range offs {
		for _, reset := range resets {
			dur, err := off.Next(reset)
			if dur != ZeroDuration {
				t.Errorf("%d expected %s: %s", ct, ZeroDuration, dur)
			}
			if err != ErrLowBound {
				t.Errorf("%d expected %v: %v", ct, ErrLowBound, err)
			}
		}
	}
}

func TestMaxAttempts(t *testing.T) {
	max := 4 + rand.Intn(10)

	expected := time.Minute
	safes := []bool{true, false}
	for _, safe := range safes {
		bo := MaxAttempts(NewConstant(expected), uint32(max), safe)

		// Have to do it at least 2 times to make sure the
		// reset takes
		cycles := 2 + rand.Intn(3)
		for ix := 0; ix < cycles; ix++ {
			// Get us up to, but not over the max
			for jx := 0; jx < max; jx++ {
				dur, err := bo.Next(false)
				if dur != expected {
					t.Errorf("expected %v: %v", expected, dur)
				}
				if err != nil {
					t.Errorf("unexpected: %v", err)
				}
			}

			// We expect to be at the max, even for several attempts
			for jx := 0; jx < 3; jx++ {
				dur, err := bo.Next(false)
				if dur != ZeroDuration {
					t.Errorf("expected %v: %v", ZeroDuration, dur)
				}
				if err != ErrStop {
					t.Errorf("expected %v: %v", ErrStop, err)
				}
			}

			// Call for a reset, even a couple of times. This
			// sets us up for the next cycle to start fresh
			for jx := 0; jx < 3; jx++ {
				dur, err := bo.Next(true)
				if dur != ZeroDuration {
					t.Errorf("expected %v: %v", ZeroDuration, dur)
				}
				if err != nil {
					t.Errorf("unexpected: %v", err)
				}
			}
		}
	}
}

func TestCeiling(t *testing.T) {
	under, err := NewExponential(time.Millisecond, 1.0)
	if err != nil {
		t.Fatalf("unexpected: %v", err)
	}

	top := time.Millisecond * 10
	bo := Ceiling(under, top)

	// Several cycles to prove reset works
	cycles := 2 + rand.Intn(3)
	for ix := 0; ix < cycles; ix++ {

		// This should get us 1ms, 2ms, 4ms, 8ms
		for jx := 0; jx < 4; jx++ {
			dur, err := bo.Next(false)
			if dur >= top {
				t.Errorf("unexpected: %v", dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}

		// Next in the sequence should be
		// 16ms, 32ms, 64ms, but these should all be
		// governed down to the 10ms ceiling
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(false)
			if dur != top {
				t.Errorf("expected %v: %v", top, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}

		// Call for a reset, even a couple of times. This
		// sets us up for the next cycle to start fresh
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(true)
			if dur != ZeroDuration {
				t.Errorf("expected %v: %v", ZeroDuration, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}
	}
}

func TestElapsed(t *testing.T) {
	expected := time.Minute
	under := NewConstant(expected)

	window := time.Millisecond * 200
	bo := Elapsed(under, window)

	// Several cycles to prove reset works
	cycles := 2 + rand.Intn(3)
	for ix := 0; ix < cycles; ix++ {
		// Since we're not over time, we should just get
		// what the underlying says
		for jx := 0; jx < 10; jx++ {
			dur, err := bo.Next(false)
			if dur != expected {
				t.Errorf("expected %v: %v", expected, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}

		// Wait until we've gone over time
		time.Sleep(window)

		// We expect to be told to stop, even for several attempts
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(false)
			if dur != ZeroDuration {
				t.Errorf("expected %v: %v", ZeroDuration, dur)
			}
			if err != ErrStop {
				t.Errorf("expected %v: %v", ErrStop, err)
			}
		}

		// Call for a reset, even a couple of times. This
		// sets us up for the next cycle to start fresh
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(true)
			if dur != ZeroDuration {
				t.Errorf("expected %v: %v", ZeroDuration, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}
	}
}

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

package xbo

import (
	"math/rand"
	"testing"
	"time"
)

func TestBoundaryErrors(t *testing.T) {
	// Testing each of the bounded BackOff types
	offs := []BackOff{
		MaxAttempts(NewStop(), 0, false),
		MaxAttempts(NewStop(), 0, true),
		Ceiling(NewStop(), 0),
		Elapsed(NewStop(), 0),
	}
	resets := []bool{true, false}

	for ct, off := range offs {
		for _, reset := range resets {
			dur, err := off.Next(reset)
			if dur != ZeroDuration {
				t.Errorf("%d expected %s: %s", ct, ZeroDuration, dur)
			}
			if err != ErrLowBound {
				t.Errorf("%d expected %v: %v", ct, ErrLowBound, err)
			}
		}
	}
}

func TestMaxAttempts(t *testing.T) {
	max := 4 + rand.Intn(10)

	expected := time.Minute
	safes := []bool{true, false}
	for _, safe := range safes {
		bo := MaxAttempts(NewConstant(expected), uint32(max), safe)

		// Have to do it at least 2 times to make sure the
		// reset takes
		cycles := 2 + rand.Intn(3)
		for ix := 0; ix < cycles; ix++ {
			// Get us up to, but not over the max
			for jx := 0; jx < max; jx++ {
				dur, err := bo.Next(false)
				if dur != expected {
					t.Errorf("expected %v: %v", expected, dur)
				}
				if err != nil {
					t.Errorf("unexpected: %v", err)
				}
			}

			// We expect to be at the max, even for several attempts
			for jx := 0; jx < 3; jx++ {
				dur, err := bo.Next(false)
				if dur != ZeroDuration {
					t.Errorf("expected %v: %v", ZeroDuration, dur)
				}
				if err != ErrStop {
					t.Errorf("expected %v: %v", ErrStop, err)
				}
			}

			// Call for a reset, even a couple of times. This
			// sets us up for the next cycle to start fresh
			for jx := 0; jx < 3; jx++ {
				dur, err := bo.Next(true)
				if dur != ZeroDuration {
					t.Errorf("expected %v: %v", ZeroDuration, dur)
				}
				if err != nil {
					t.Errorf("unexpected: %v", err)
				}
			}
		}
	}
}

func TestCeiling(t *testing.T) {
	under, err := NewExponential(time.Millisecond, 1.0)
	if err != nil {
		t.Fatalf("unexpected: %v", err)
	}

	top := time.Millisecond * 10
	bo := Ceiling(under, top)

	// Several cycles to prove reset works
	cycles := 2 + rand.Intn(3)
	for ix := 0; ix < cycles; ix++ {

		// This should get us 1ms, 2ms, 4ms, 8ms
		for jx := 0; jx < 4; jx++ {
			dur, err := bo.Next(false)
			if dur >= top {
				t.Errorf("unexpected: %v", dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}

		// Next in the sequence should be
		// 16ms, 32ms, 64ms, but these should all be
		// governed down to the 10ms ceiling
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(false)
			if dur != top {
				t.Errorf("expected %v: %v", top, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}

		// Call for a reset, even a couple of times. This
		// sets us up for the next cycle to start fresh
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(true)
			if dur != ZeroDuration {
				t.Errorf("expected %v: %v", ZeroDuration, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}
	}
}

func TestElapsed(t *testing.T) {
	expected := time.Minute
	under := NewConstant(expected)

	window := time.Millisecond * 200
	bo := Elapsed(under, window)

	// Several cycles to prove reset works
	cycles := 2 + rand.Intn(3)
	for ix := 0; ix < cycles; ix++ {
		// Since we're not over time, we should just get
		// what the underlying says
		for jx := 0; jx < 10; jx++ {
			dur, err := bo.Next(false)
			if dur != expected {
				t.Errorf("expected %v: %v", expected, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}

		// Wait until we've gone over time
		time.Sleep(window)

		// We expect to be told to stop, even for several attempts
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(false)
			if dur != ZeroDuration {
				t.Errorf("expected %v: %v", ZeroDuration, dur)
			}
			if err != ErrStop {
				t.Errorf("expected %v: %v", ErrStop, err)
			}
		}

		// Call for a reset, even a couple of times. This
		// sets us up for the next cycle to start fresh
		for jx := 0; jx < 3; jx++ {
			dur, err := bo.Next(true)
			if dur != ZeroDuration {
				t.Errorf("expected %v: %v", ZeroDuration, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}
	}
}

func TestMaxAttemptsShortCircuit(t *testing.T) {
	// Ensure that when the max attempts are triggered,
	// we don't actually hit the underlying BackOff (i.e. short-circuit).
	// Not sure if/when this might be useful, but it was asserted so why
	// not test it?!? ;)

	// It does feel a bit tautoligical to be testin MaxAttempts by
	// using another MaxAttempts... But I do believe the logic is sound.
	expected := time.Second
	under := MaxAttempts(NewConstant(expected), 2, false)
	bo := MaxAttempts(under, 1, false)

	// We expect one good result out of the top layer
	dur, err := bo.Next(false)
	if err != nil {
		t.Errorf("unexpected: %v", err)
	}
	if dur != expected {
		t.Errorf("expected %v: %v", expected, dur)
	}

	// But any further attempts on the top layer, and we expect
	// to receive ErrStop
	for ix := 0; ix < 5; ix++ {
		dur, err = bo.Next(false)
		if dur != ZeroDuration {
			t.Errorf("expected %v: %v", ZeroDuration, dur)
		}
		if err != ErrStop {
			t.Errorf("expected %v: %v", ErrStop, err)
		}
	}

	// HOWEVER, due to the short-circuit nature of the top layer,
	// we expect the underlying BackOff to still have ONE good
	// request left in it.
	dur, err = under.Next(false)
	if err != nil {
		t.Errorf("unexpected: %v", err)
	}
	if dur != expected {
		t.Errorf("expected %v: %v", expected, dur)
	}
}

func TestElapsedShortCircuit(t *testing.T) {
	// Ensure that when too much time has passed,
	// we don't actually hit the underlying BackOff (i.e. short-circuit).
	expected := time.Second
	under := MaxAttempts(NewConstant(expected), 2, false)
	bound := 100 * time.Millisecond
	bo := Elapsed(under, bound)

	// We expect one good result out of the top layer
	dur, err := bo.Next(false)
	if err != nil {
		t.Errorf("unexpected: %v", err)
	}
	if dur != expected {
		t.Errorf("expected %v: %v", expected, dur)
	}

	// After waiting too much time, any further attempts on the top layer,
	// and we expect to receive ErrStop
	time.Sleep(bound)
	for ix := 0; ix < 5; ix++ {
		dur, err = bo.Next(false)
		if dur != ZeroDuration {
			t.Errorf("expected %v: %v", ZeroDuration, dur)
		}
		if err != ErrStop {
			t.Errorf("expected %v: %v", ErrStop, err)
		}
	}

	// HOWEVER, due to the short-circuit nature of the top layer,
	// we expect the underlying BackOff to still have ONE good
	// request left in it.
	dur, err = under.Next(false)
	if err != nil {
		t.Errorf("unexpected: %v", err)
	}
	if dur != expected {
		t.Errorf("expected %v: %v", expected, dur)
	}
}

// TODO: more tests
// TestMaxAttemptsSafe
<|endoftext|>


<commit_before>package pkg

import (
	"fmt"
	"time"

	"github.com/gophr-pm/gophr/lib/db"
	"github.com/gophr-pm/gophr/lib/db/query"
)

// InsertArgs is the arguments struct for Insert.
type InsertArgs struct {
	Repo        string
	Stars       int
	Author      string
	Awesome     bool
	Queryable   db.Queryable
	Description string
}

// Insert puts a package into the database if it doesn't exist.
func Insert(args InsertArgs) error {
	// Now that we have all the requisite data, insert the new package.
	if err := query.InsertInto(packagesTableName).
		Value(packagesColumnNameRepo, args.Repo).
		Value(packagesColumnNameStars, args.Stars).
		Value(packagesColumnNameAuthor, args.Author).
		Value(packagesColumnNameAwesome, args.Awesome).
		Value(packagesColumnNameDescription, args.Description).
		Value(packagesColumnNameDateDiscovered, time.Now()).
		Value(
			packagesColumnNameSearchBlob,
			composeSearchBlob(args.Author, args.Repo, args.Description)).
		IfNotExists().
		Create(args.Queryable).
		Exec(); err != nil {
		return fmt.Errorf(
			"Failed to insert package %s/%s: %v",
			args.Author,
			args.Repo,
			err)
	}

	return nil
}
<commit_msg>added scores to the default assert<commit_after>package pkg

import (
	"fmt"
	"time"

	"github.com/gophr-pm/gophr/lib/db"
	"github.com/gophr-pm/gophr/lib/db/query"
)

// InsertArgs is the arguments struct for Insert.
type InsertArgs struct {
	Repo        string
	Stars       int
	Author      string
	Awesome     bool
	Queryable   db.Queryable
	Description string
}

// Insert puts a package into the database if it doesn't exist.
func Insert(args InsertArgs) error {
	// Now that we have all the requisite data, insert the new package.
	if err := query.InsertInto(packagesTableName).
		Value(packagesColumnNameRepo, args.Repo).
		Value(packagesColumnNameStars, args.Stars).
		Value(packagesColumnNameAuthor, args.Author).
		Value(packagesColumnNameAwesome, args.Awesome).
		Value(packagesColumnNameTrendScore, 0).
		Value(
			packagesColumnNameSearchScore,
			CalcSearchScore(args.Stars, 0, args.Awesome, 0)).
		Value(packagesColumnNameDescription, args.Description).
		Value(packagesColumnNameDateDiscovered, time.Now()).
		Value(
			packagesColumnNameSearchBlob,
			composeSearchBlob(args.Author, args.Repo, args.Description)).
		IfNotExists().
		Create(args.Queryable).
		Exec(); err != nil {
		return fmt.Errorf(
			"Failed to insert package %s/%s: %v",
			args.Author,
			args.Repo,
			err)
	}

	return nil
}
<|endoftext|>


<commit_before>// $G $D/$F.go && $L $F.$A && ./$A.out

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

// Verify that unbuffered channels act as pure fifos.

package main

const N = 10

func AsynchFifo() {
	ch := new(chan int, N);
	for i := 0; i < N; i++ {
		ch -< i
	}
	for i := 0; i < N; i++ {
		if <-ch != i {
			print "bad receive\n";
			sys.exit(1);
		}
	}
}

func Chain(ch *chan<- int, val int, in *chan<- int, out *chan-< int) {
	<-in;
	if <-ch != val {
		panic val
	}
	out -< 1
}

// thread together a daisy chain to read the elements in sequence
func SynchFifo() {
	ch := new(chan int);
	in := new(chan int);
	start := in;
	for i := 0; i < N; i++ {
		out := new(chan int);
		go Chain(ch, i, in, out);
		in = out;
	}
	start -< 0;
	for i := 0; i < N; i++ {
		ch -< i
	}
}

func main() {
	AsynchFifo();
	SynchFifo();
}

<commit_msg>fix bug - need to read final message in daisy chain to avoid hang<commit_after>// $G $D/$F.go && $L $F.$A && ./$A.out

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

// Verify that unbuffered channels act as pure fifos.

package main

const N = 10

func AsynchFifo() {
	ch := new(chan int, N);
	for i := 0; i < N; i++ {
		ch -< i
	}
	for i := 0; i < N; i++ {
		if <-ch != i {
			print "bad receive\n";
			sys.exit(1);
		}
	}
}

func Chain(ch *chan<- int, val int, in *chan<- int, out *chan-< int) {
	<-in;
	if <-ch != val {
		panic val
	}
	out -< 1
}

// thread together a daisy chain to read the elements in sequence
func SynchFifo() {
	ch := new(chan int);
	in := new(chan int);
	start := in;
	for i := 0; i < N; i++ {
		out := new(chan int);
		go Chain(ch, i, in, out);
		in = out;
	}
	start -< 0;
	for i := 0; i < N; i++ {
		ch -< i
	}
	<-in
}

func main() {
	AsynchFifo();
	SynchFifo();
}

<|endoftext|>


<commit_before>// Copyright 2012 Gary Burd
//
// 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.

// Note: This file has been modified. Package and method names were updated
// to reflect the fact that the scan file has been moved from zoom/redis/ to
// zoom/ (the root of this project). I also added some clean up that deletes
// used keys after each test is complete.

package test

import (
	"errors"
	"fmt"
	"github.com/stephenalexbrowne/zoom"
	"github.com/stephenalexbrowne/zoom/redis"
	"math"
	"reflect"
	"testing"
	"time"
)

var scanConversionTests = []struct {
	src  interface{}
	dest interface{}
}{
	{[]byte("-inf"), math.Inf(-1)},
	{[]byte("+inf"), math.Inf(1)},
	{[]byte("0"), float64(0)},
	{[]byte("3.14159"), float64(3.14159)},
	{[]byte("3.14"), float32(3.14)},
	{[]byte("-100"), int(-100)},
	{[]byte("101"), int(101)},
	{int64(102), int(102)},
	{[]byte("103"), uint(103)},
	{int64(104), uint(104)},
	{[]byte("105"), int8(105)},
	{int64(106), int8(106)},
	{[]byte("107"), uint8(107)},
	{int64(108), uint8(108)},
	{[]byte("0"), false},
	{int64(0), false},
	{[]byte("f"), false},
	{[]byte("1"), true},
	{int64(1), true},
	{[]byte("t"), true},
	{[]byte("hello"), "hello"},
	{[]byte("world"), []byte("world")},
	{[]interface{}{[]byte("foo")}, []string{"foo"}},
	{[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}},
	{[]interface{}{[]byte("1")}, []int{1}},
	{[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}},
	{[]interface{}{[]byte("1")}, []byte{1}},
	{[]interface{}{[]byte("1")}, []bool{true}},
}

var scanConversionErrorTests = []struct {
	src  interface{}
	dest interface{}
}{
	{[]byte("1234"), byte(0)},
	{int64(1234), byte(0)},
	{[]byte("-1"), byte(0)},
	{int64(-1), byte(0)},
	{[]byte("junk"), false},
	{redis.Error("blah"), false},
}

func TestScanConversion(t *testing.T) {
	for _, tt := range scanConversionTests {
		values := []interface{}{tt.src}
		dest := reflect.New(reflect.TypeOf(tt.dest))
		values, err := zoom.Scan(values, dest.Interface())
		if err != nil {
			t.Errorf("Scan(%v) returned error %v", tt, err)
			continue
		}
		if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
			t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
		}
	}
}

func TestScanConversionError(t *testing.T) {
	for _, tt := range scanConversionErrorTests {
		values := []interface{}{tt.src}
		dest := reflect.New(reflect.TypeOf(tt.dest))
		values, err := zoom.Scan(values, dest.Interface())
		if err == nil {
			t.Errorf("Scan(%v) did not return error", tt)
		}
	}
}

func ExampleScan() {
	c, err := dial()
	if err != nil {
		panic(err)
	}
	defer c.Close()

	c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
	c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
	c.Send("HMSET", "album:3", "title", "Beat")
	c.Send("LPUSH", "albums", "1")
	c.Send("LPUSH", "albums", "2")
	c.Send("LPUSH", "albums", "3")
	values, err := redis.Values(c.Do("SORT", "albums",
		"BY", "album:*->rating",
		"GET", "album:*->title",
		"GET", "album:*->rating"))
	if err != nil {
		panic(err)
	}

	for len(values) > 0 {
		var title string
		rating := -1 // initialize to illegal value to detect nil.
		values, err = zoom.Scan(values, &title, &rating)
		if err != nil {
			panic(err)
		}
		if rating == -1 {
			fmt.Println(title, "not-rated")
		} else {
			fmt.Println(title, rating)
		}
	}
	// Output:
	// Beat not-rated
	// Earthbound 1
	// Red 5
}

type s0 struct {
	X  int
	Y  int `redis:"y"`
	Bt bool
}

type s1 struct {
	X  int    `redis:"-"`
	I  int    `redis:"i"`
	U  uint   `redis:"u"`
	S  string `redis:"s"`
	P  []byte `redis:"p"`
	B  bool   `redis:"b"`
	Bt bool
	Bf bool
	s0
}

var scanStructTests = []struct {
	title string
	reply []string
	value interface{}
}{
	{"basic",
		[]string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"},
		&s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}},
	},
}

func TestScanStruct(t *testing.T) {
	for _, tt := range scanStructTests {

		var reply []interface{}
		for _, v := range tt.reply {
			reply = append(reply, []byte(v))
		}

		value := reflect.New(reflect.ValueOf(tt.value).Type().Elem())

		if err := zoom.ScanStruct(reply, value.Interface()); err != nil {
			t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err)
		}

		if !reflect.DeepEqual(value.Interface(), tt.value) {
			t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value)
		}
	}
}

var argsTests = []struct {
	title    string
	actual   zoom.Args
	expected zoom.Args
}{
	{"struct ptr",
		zoom.Args{}.AddFlat(&struct {
			I  int    `redis:"i"`
			U  uint   `redis:"u"`
			S  string `redis:"s"`
			P  []byte `redis:"p"`
			Bt bool
			Bf bool
		}{
			-1234, 5678, "hello", []byte("world"), true, false,
		}),
		zoom.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "Bt", true, "Bf", false},
	},
	{"struct",
		zoom.Args{}.AddFlat(struct{ I int }{123}),
		zoom.Args{"I", 123},
	},
	{"slice",
		zoom.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
		zoom.Args{1, "a", "b", "c", 2},
	},
}

func TestArgs(t *testing.T) {
	for _, tt := range argsTests {
		if !reflect.DeepEqual(tt.actual, tt.expected) {
			t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
		}
	}
}

func ExampleArgs() {
	c, err := dial()
	if err != nil {
		panic(err)
	}
	defer c.Close()

	var p1, p2 struct {
		Title  string `redis:"title"`
		Author string `redis:"author"`
		Body   string `redis:"body"`
	}

	p1.Title = "Example"
	p1.Author = "Gary"
	p1.Body = "Hello"

	if _, err := c.Do("HMSET", zoom.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
		panic(err)
	}

	m := map[string]string{
		"title":  "Example2",
		"author": "Steve",
		"body":   "Map",
	}

	if _, err := c.Do("HMSET", zoom.Args{}.Add("id2").AddFlat(m)...); err != nil {
		panic(err)
	}

	for _, id := range []string{"id1", "id2"} {

		v, err := redis.Values(c.Do("HGETALL", id))
		if err != nil {
			panic(err)
		}

		if err := zoom.ScanStruct(v, &p2); err != nil {
			panic(err)
		}

		fmt.Printf("%+v\n", p2)
	}

	// Output:
	// {Title:Example Author:Gary Body:Hello}
	// {Title:Example2 Author:Steve Body:Map}
}

type testConn struct {
	redis.Conn
}

func dial() (redis.Conn, error) {
	c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second)
	if err != nil {
		return nil, err
	}

	_, err = c.Do("SELECT", "9")
	if err != nil {
		return nil, err
	}

	n, err := redis.Int(c.Do("DBSIZE"))
	if err != nil {
		return nil, err
	}

	if n != 0 {
		return nil, errors.New("Database #9 is not empty, test can not continue")
	}

	return testConn{c}, nil
}
<commit_msg>Re-add the cleanup code. Apparently it was needed?<commit_after>// Copyright 2012 Gary Burd
//
// 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.

// Note: This file has been modified. Package and method names were updated
// to reflect the fact that the scan file has been moved from zoom/redis/ to
// zoom/ (the root of this project). I also added some clean up that deletes
// used keys after each test is complete.

package test

import (
	"errors"
	"fmt"
	"github.com/stephenalexbrowne/zoom"
	"github.com/stephenalexbrowne/zoom/redis"
	"math"
	"reflect"
	"testing"
	"time"
)

var scanConversionTests = []struct {
	src  interface{}
	dest interface{}
}{
	{[]byte("-inf"), math.Inf(-1)},
	{[]byte("+inf"), math.Inf(1)},
	{[]byte("0"), float64(0)},
	{[]byte("3.14159"), float64(3.14159)},
	{[]byte("3.14"), float32(3.14)},
	{[]byte("-100"), int(-100)},
	{[]byte("101"), int(101)},
	{int64(102), int(102)},
	{[]byte("103"), uint(103)},
	{int64(104), uint(104)},
	{[]byte("105"), int8(105)},
	{int64(106), int8(106)},
	{[]byte("107"), uint8(107)},
	{int64(108), uint8(108)},
	{[]byte("0"), false},
	{int64(0), false},
	{[]byte("f"), false},
	{[]byte("1"), true},
	{int64(1), true},
	{[]byte("t"), true},
	{[]byte("hello"), "hello"},
	{[]byte("world"), []byte("world")},
	{[]interface{}{[]byte("foo")}, []string{"foo"}},
	{[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}},
	{[]interface{}{[]byte("1")}, []int{1}},
	{[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}},
	{[]interface{}{[]byte("1")}, []byte{1}},
	{[]interface{}{[]byte("1")}, []bool{true}},
}

var scanConversionErrorTests = []struct {
	src  interface{}
	dest interface{}
}{
	{[]byte("1234"), byte(0)},
	{int64(1234), byte(0)},
	{[]byte("-1"), byte(0)},
	{int64(-1), byte(0)},
	{[]byte("junk"), false},
	{redis.Error("blah"), false},
}

func TestScanConversion(t *testing.T) {
	for _, tt := range scanConversionTests {
		values := []interface{}{tt.src}
		dest := reflect.New(reflect.TypeOf(tt.dest))
		values, err := zoom.Scan(values, dest.Interface())
		if err != nil {
			t.Errorf("Scan(%v) returned error %v", tt, err)
			continue
		}
		if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
			t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
		}
	}
}

func TestScanConversionError(t *testing.T) {
	for _, tt := range scanConversionErrorTests {
		values := []interface{}{tt.src}
		dest := reflect.New(reflect.TypeOf(tt.dest))
		values, err := zoom.Scan(values, dest.Interface())
		if err == nil {
			t.Errorf("Scan(%v) did not return error", tt)
		}
	}
}

func ExampleScan() {
	c, err := dial()
	if err != nil {
		panic(err)
	}
	defer c.Close()

	c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
	c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
	c.Send("HMSET", "album:3", "title", "Beat")
	c.Send("LPUSH", "albums", "1")
	c.Send("LPUSH", "albums", "2")
	c.Send("LPUSH", "albums", "3")
	values, err := redis.Values(c.Do("SORT", "albums",
		"BY", "album:*->rating",
		"GET", "album:*->title",
		"GET", "album:*->rating"))
	if err != nil {
		panic(err)
	}

	for len(values) > 0 {
		var title string
		rating := -1 // initialize to illegal value to detect nil.
		values, err = zoom.Scan(values, &title, &rating)
		if err != nil {
			panic(err)
		}
		if rating == -1 {
			fmt.Println(title, "not-rated")
		} else {
			fmt.Println(title, rating)
		}
	}
	// Output:
	// Beat not-rated
	// Earthbound 1
	// Red 5

	// delete the keys we created so the test passes next time
	c.Do("DEL", "albums", "album:1", "album:2", "album:3")
}

type s0 struct {
	X  int
	Y  int `redis:"y"`
	Bt bool
}

type s1 struct {
	X  int    `redis:"-"`
	I  int    `redis:"i"`
	U  uint   `redis:"u"`
	S  string `redis:"s"`
	P  []byte `redis:"p"`
	B  bool   `redis:"b"`
	Bt bool
	Bf bool
	s0
}

var scanStructTests = []struct {
	title string
	reply []string
	value interface{}
}{
	{"basic",
		[]string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"},
		&s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}},
	},
}

func TestScanStruct(t *testing.T) {
	for _, tt := range scanStructTests {

		var reply []interface{}
		for _, v := range tt.reply {
			reply = append(reply, []byte(v))
		}

		value := reflect.New(reflect.ValueOf(tt.value).Type().Elem())

		if err := zoom.ScanStruct(reply, value.Interface()); err != nil {
			t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err)
		}

		if !reflect.DeepEqual(value.Interface(), tt.value) {
			t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value)
		}
	}
}

var argsTests = []struct {
	title    string
	actual   zoom.Args
	expected zoom.Args
}{
	{"struct ptr",
		zoom.Args{}.AddFlat(&struct {
			I  int    `redis:"i"`
			U  uint   `redis:"u"`
			S  string `redis:"s"`
			P  []byte `redis:"p"`
			Bt bool
			Bf bool
		}{
			-1234, 5678, "hello", []byte("world"), true, false,
		}),
		zoom.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "Bt", true, "Bf", false},
	},
	{"struct",
		zoom.Args{}.AddFlat(struct{ I int }{123}),
		zoom.Args{"I", 123},
	},
	{"slice",
		zoom.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
		zoom.Args{1, "a", "b", "c", 2},
	},
}

func TestArgs(t *testing.T) {
	for _, tt := range argsTests {
		if !reflect.DeepEqual(tt.actual, tt.expected) {
			t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
		}
	}
}

func ExampleArgs() {
	c, err := dial()
	if err != nil {
		panic(err)
	}
	defer c.Close()

	var p1, p2 struct {
		Title  string `redis:"title"`
		Author string `redis:"author"`
		Body   string `redis:"body"`
	}

	p1.Title = "Example"
	p1.Author = "Gary"
	p1.Body = "Hello"

	if _, err := c.Do("HMSET", zoom.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
		panic(err)
	}

	m := map[string]string{
		"title":  "Example2",
		"author": "Steve",
		"body":   "Map",
	}

	if _, err := c.Do("HMSET", zoom.Args{}.Add("id2").AddFlat(m)...); err != nil {
		panic(err)
	}

	for _, id := range []string{"id1", "id2"} {

		v, err := redis.Values(c.Do("HGETALL", id))
		if err != nil {
			panic(err)
		}

		if err := zoom.ScanStruct(v, &p2); err != nil {
			panic(err)
		}

		fmt.Printf("%+v\n", p2)
	}

	// Output:
	// {Title:Example Author:Gary Body:Hello}
	// {Title:Example2 Author:Steve Body:Map}

	// delete the keys we just used
	c.Do("DEL", "id1", "id2")
}

type testConn struct {
	redis.Conn
}

func dial() (redis.Conn, error) {
	c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second)
	if err != nil {
		return nil, err
	}

	_, err = c.Do("SELECT", "9")
	if err != nil {
		return nil, err
	}

	n, err := redis.Int(c.Do("DBSIZE"))
	if err != nil {
		return nil, err
	}

	if n != 0 {
		return nil, errors.New("Database #9 is not empty, test can not continue")
	}

	return testConn{c}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
)

func proto_0_0(inFlag, outFlag bool, errFlag, workdir string, args []string) error {
	proc := exec.Command(args[0], args[1:]...)
	proc.Dir = workdir

	done := make(chan bool)
	done_count := 0
	done_count += wrapStdin(proc, os.Stdin, inFlag, done)
	if outFlag {
		done_count += wrapStdout(proc, os.Stdout, 'o', done)
	}
	if errFlag == "out" && outFlag {
		done_count += wrapStderr(proc, os.Stdout, 'o', done)
	} else if errFlag == "err" {
		done_count += wrapStderr(proc, os.Stdout, 'e', done)
	} else if errFlag != "nil" {
		fmt.Fprintf(os.Stderr, "undefined redirect: '%v'\n", errFlag)
		fatal("undefined redirect")
	}

	err := proc.Run()
	for i := 0; i < done_count; i++ {
		<-done
	}
	return err
}
<commit_msg>Fix logic error in error flag handling<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
)

func proto_0_0(inFlag, outFlag bool, errFlag, workdir string, args []string) error {
	proc := exec.Command(args[0], args[1:]...)
	proc.Dir = workdir

	done := make(chan bool)
	done_count := 0
	done_count += wrapStdin(proc, os.Stdin, inFlag, done)
	if outFlag {
		done_count += wrapStdout(proc, os.Stdout, 'o', done)
	}
	switch errFlag {
	case "out":
		if outFlag {
			done_count += wrapStderr(proc, os.Stdout, 'o', done)
		}
	case "err":
		done_count += wrapStderr(proc, os.Stdout, 'e', done)
	case "nil":
		// no-op
	default:
		fmt.Fprintf(os.Stderr, "undefined redirect: '%v'\n", errFlag)
		fatal("undefined redirect")
	}

	err := proc.Run()
	for i := 0; i < done_count; i++ {
		<-done
	}
	return err
}
<|endoftext|>