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

// +build !appengine

package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/go.talks/pkg/socket"

	// Imports so that go build/install automatically installs them.
	_ "code.google.com/p/go-tour/pic"
	_ "code.google.com/p/go-tour/tree"
	_ "code.google.com/p/go-tour/wc"
)

const (
	basePkg    = "code.google.com/p/go-tour/"
	socketPath = "/socket"
)

var (
	httpListen  = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput  = flag.Bool("html", false, "render program output as HTML")
	openBrowser = flag.Bool("openbrowser", true, "open browser automatically")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)

	// GOPATH containing the tour packages
	gopath = os.Getenv("GOPATH")

	httpAddr string
)

func isRoot(path string) bool {
	_, err := os.Stat(filepath.Join(path, "tour.article"))
	return err == nil
}

func findRoot() (string, error) {
	ctx := build.Default
	p, err := ctx.Import(basePkg, "", build.FindOnly)
	if err == nil && isRoot(p.Dir) {
		return p.Dir, nil
	}
	tourRoot := filepath.Join(runtime.GOROOT(), "misc", "tour")
	ctx.GOPATH = tourRoot
	p, err = ctx.Import(basePkg, "", build.FindOnly)
	if err == nil && isRoot(tourRoot) {
		gopath = tourRoot
		return tourRoot, nil
	}
	return "", fmt.Errorf("could not find go-tour content; check $GOROOT and $GOPATH")
}

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// find and serve the go tour files
	root, err := findRoot()
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}

	log.Println("Serving content from", root)

	host, port, err := net.SplitHostPort(*httpListen)
	if err != nil {
		log.Fatal(err)
	}
	if host == "" {
		host = "localhost"
	}
	if host != "127.0.0.1" && host != "localhost" {
		log.Print(localhostWarning)
	}
	httpAddr = host + ":" + port

	if err := initTour(root); err != nil {
		log.Fatal(err)
	}

	fs := http.FileServer(http.Dir(root))
	http.Handle("/favicon.ico", fs)
	http.Handle("/static/", fs)
	http.Handle("/talks/", fs)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/" {
			if err := renderTour(w); err != nil {
				log.Println(err)
			}
			return
		}
		http.Error(w, "not found", 404)
	})

	http.Handle(socketPath, socket.Handler)

	err = serveScripts(filepath.Join(root, "js"), "socket.js")
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		url := "http://" + httpAddr
		if waitServer(url) && *openBrowser && startBrowser(url) {
			log.Printf("A browser window should open. If not, please visit %s", url)
		} else {
			log.Printf("Please open your web browser and visit %s", url)
		}
	}()
	log.Fatal(http.ListenAndServe(httpAddr, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

type response struct {
	Output string `json:"output"`
	Errors string `json:"compile_errors"`
}

func init() {
	socket.Environ = environ
}

// environ returns an execution environment containing only GO* variables
// and replacing GOPATH with the value of the global var gopath.
func environ() (env []string) {
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GO") {
			continue
		}
		if strings.HasPrefix(v, "GOPATH=") {
			v = "GOPATH=" + gopath
		}
		env = append(env, v)
	}
	return
}

// waitServer waits some time for the http Server to start
// serving url and returns whether it starts
func waitServer(url string) bool {
	tries := 20
	for tries > 0 {
		resp, err := http.Get(url)
		if err == nil {
			resp.Body.Close()
			return true
		}
		time.Sleep(100 * time.Millisecond)
		tries--
	}
	return false
}

// startBrowser tries to open the URL in a browser, and returns
// whether it succeed.
func startBrowser(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}

// prepContent for the local tour simply returns the content as-is.
func prepContent(r io.Reader) io.Reader { return r }

// socketAddr returns the WebSocket handler address.
func socketAddr() string { return "ws://" + httpAddr + socketPath }
<commit_msg>go-tour: pass TEMP and TMPDIR environment variables to go tool<commit_after>// Copyright 2011 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.

// +build !appengine

package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/go.talks/pkg/socket"

	// Imports so that go build/install automatically installs them.
	_ "code.google.com/p/go-tour/pic"
	_ "code.google.com/p/go-tour/tree"
	_ "code.google.com/p/go-tour/wc"
)

const (
	basePkg    = "code.google.com/p/go-tour/"
	socketPath = "/socket"
)

var (
	httpListen  = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput  = flag.Bool("html", false, "render program output as HTML")
	openBrowser = flag.Bool("openbrowser", true, "open browser automatically")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)

	// GOPATH containing the tour packages
	gopath = os.Getenv("GOPATH")

	httpAddr string
)

func isRoot(path string) bool {
	_, err := os.Stat(filepath.Join(path, "tour.article"))
	return err == nil
}

func findRoot() (string, error) {
	ctx := build.Default
	p, err := ctx.Import(basePkg, "", build.FindOnly)
	if err == nil && isRoot(p.Dir) {
		return p.Dir, nil
	}
	tourRoot := filepath.Join(runtime.GOROOT(), "misc", "tour")
	ctx.GOPATH = tourRoot
	p, err = ctx.Import(basePkg, "", build.FindOnly)
	if err == nil && isRoot(tourRoot) {
		gopath = tourRoot
		return tourRoot, nil
	}
	return "", fmt.Errorf("could not find go-tour content; check $GOROOT and $GOPATH")
}

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// find and serve the go tour files
	root, err := findRoot()
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}

	log.Println("Serving content from", root)

	host, port, err := net.SplitHostPort(*httpListen)
	if err != nil {
		log.Fatal(err)
	}
	if host == "" {
		host = "localhost"
	}
	if host != "127.0.0.1" && host != "localhost" {
		log.Print(localhostWarning)
	}
	httpAddr = host + ":" + port

	if err := initTour(root); err != nil {
		log.Fatal(err)
	}

	fs := http.FileServer(http.Dir(root))
	http.Handle("/favicon.ico", fs)
	http.Handle("/static/", fs)
	http.Handle("/talks/", fs)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/" {
			if err := renderTour(w); err != nil {
				log.Println(err)
			}
			return
		}
		http.Error(w, "not found", 404)
	})

	http.Handle(socketPath, socket.Handler)

	err = serveScripts(filepath.Join(root, "js"), "socket.js")
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		url := "http://" + httpAddr
		if waitServer(url) && *openBrowser && startBrowser(url) {
			log.Printf("A browser window should open. If not, please visit %s", url)
		} else {
			log.Printf("Please open your web browser and visit %s", url)
		}
	}()
	log.Fatal(http.ListenAndServe(httpAddr, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

type response struct {
	Output string `json:"output"`
	Errors string `json:"compile_errors"`
}

func init() {
	socket.Environ = environ
}

// environ returns an execution environment containing only GO* variables
// and replacing GOPATH with the value of the global var gopath.
func environ() (env []string) {
	for _, v := range os.Environ() {
		if strings.HasPrefix(v, "GO") ||
			strings.HasPrefix(v, "TMPDIR=") ||
			strings.HasPrefix(v, "TEMP=") {
			if strings.HasPrefix(v, "GOPATH=") {
				v = "GOPATH=" + gopath
			}
			env = append(env, v)
		}
	}
	return
}

// waitServer waits some time for the http Server to start
// serving url and returns whether it starts
func waitServer(url string) bool {
	tries := 20
	for tries > 0 {
		resp, err := http.Get(url)
		if err == nil {
			resp.Body.Close()
			return true
		}
		time.Sleep(100 * time.Millisecond)
		tries--
	}
	return false
}

// startBrowser tries to open the URL in a browser, and returns
// whether it succeed.
func startBrowser(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}

// prepContent for the local tour simply returns the content as-is.
func prepContent(r io.Reader) io.Reader { return r }

// socketAddr returns the WebSocket handler address.
func socketAddr() string { return "ws://" + httpAddr + socketPath }
<|endoftext|>


<commit_before>package main

import "golang.org/x/tour/tree"
import "fmt"

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
	// Quasi recursive way to walk the tree
	if t.Left != nil {
		Walk(t.Left, ch)
	}
	// Quasi recursive way to walk the tree
	if t.Right != nil {
		Walk(t.Right, ch)
	}
	// This is gonna run regarldess
	ch <- t.Value
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
	// This actually needs to use Walk, so no cigar
	return t1.Value == t2.Value
}

func main() {
	ch := make(chan int)

	go Walk(tree.New(1), ch)

	for index := 0; index < 10; index++ {
		fmt.Println(<-ch)
	}
}
<commit_msg>I'm fairly ashamed about a lot of this code, but god damnit I did it! I'll google a 100 times better solution...<commit_after>package main

import "golang.org/x/tour/tree"
import "fmt"

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
	// Quasi recursive way to walk the tree
	if t.Left != nil {
		Walk(t.Left, ch)
	}
	// Quasi recursive way to walk the tree
	if t.Right != nil {
		Walk(t.Right, ch)
	}
	// This sends values to the tree!
	ch <- t.Value
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {

	// We make two channels to walk trees
	ch1 := make(chan int)
	ch2 := make(chan int)

	// We send both trees to walk
	go Walk(t1, ch1)
	go Walk(t2, ch2)

	firstCompare, secondCompare := WalkedTreeToArray(ch1), WalkedTreeToArray(ch2)

	// Can't do this with a single loop because items can be in different orders,
	for _, value := range firstCompare {
		for i := range secondCompare {
			if secondCompare[i] == value {
				// We "Delete" this value since it has matched once
				secondCompare = append(secondCompare[:i], secondCompare[i+1:]...)
				break
			}
		}
	}

	fmt.Println(firstCompare, secondCompare)

	// If we have 0 elements left they are the same
	return len(secondCompare) == 0
}

func WalkedTreeToArray(ch chan int) []int {
	result := make([]int, 10)
	for index := 0; index < 10; index++ {
		result[index] = <-ch
	}
	return result
}

func main() {
	ch := make(chan int)

	go Walk(tree.New(1), ch)

	fmt.Println(WalkedTreeToArray(ch))

	fmt.Println("\n\n")

	fmt.Println("Expect true: ", Same(tree.New(1), tree.New(1)))
	fmt.Println("Expect false: ", Same(tree.New(1), tree.New(2)))
}
<|endoftext|>


<commit_before>// Copyright 2018 The Ebiten Authors
//
// 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.

// +build darwin,!ios
// +build !js

package ebiten

// #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Foundation
//
// #import <Foundation/Foundation.h>
//
// static int getMacOSMinorVersion() {
//   NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
//   return (int)version.minorVersion;
// }
import "C"

import (
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal"
	"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl"
	"github.com/hajimehoshi/ebiten/internal/graphicsdriver/opengl"
)

var (
	// isMetalSupported represents whether Metal is supported.
	isMetalSupported = true
)

func init() {
	// On old mac devices like iMac 2011, Metal is not supported (#779).
	if _, err := mtl.CreateSystemDefaultDevice(); err != nil {
		isMetalSupported = false
	}
	// On macOS 10.11 El Capitan, there is a rendering issue on Metal (#781).
	// Use the OpenGL in macOS 10.11 or older.
	if C.getMacOSMinorVersion() <= 11 {
		isMetalSupported = false
	}
}

func graphicsDriver() driver.Graphics {
	if isMetalSupported {
		return metal.Get()
	}
	return opengl.Get()
}
<commit_msg>graphics: Early return at the driver selector<commit_after>// Copyright 2018 The Ebiten Authors
//
// 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.

// +build darwin,!ios
// +build !js

package ebiten

// #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Foundation
//
// #import <Foundation/Foundation.h>
//
// static int getMacOSMinorVersion() {
//   NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
//   return (int)version.minorVersion;
// }
import "C"

import (
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal"
	"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl"
	"github.com/hajimehoshi/ebiten/internal/graphicsdriver/opengl"
)

var (
	// isMetalSupported represents whether Metal is supported.
	isMetalSupported = true
)

func init() {
	// On old mac devices like iMac 2011, Metal is not supported (#779).
	if _, err := mtl.CreateSystemDefaultDevice(); err != nil {
		isMetalSupported = false
		return
	}
	// On macOS 10.11 El Capitan, there is a rendering issue on Metal (#781).
	// Use the OpenGL in macOS 10.11 or older.
	if C.getMacOSMinorVersion() <= 11 {
		isMetalSupported = false
		return
	}
}

func graphicsDriver() driver.Graphics {
	if isMetalSupported {
		return metal.Get()
	}
	return opengl.Get()
}
<|endoftext|>


<commit_before>package backends

import (
	"errors"
	"github.com/RichardKnop/machinery/v1/tasks"
	"reflect"
	"time"
)

// AsyncResult represents a task result
type AsyncResult struct {
	Signature *tasks.Signature
	taskState *tasks.TaskState
	backend   Interface
}

// ChordAsyncResult represents a result of a chord
type ChordAsyncResult struct {
	groupAsyncResults []*AsyncResult
	chordAsyncResult  *AsyncResult
	backend           Interface
}

// ChainAsyncResult represents a result of a chain of tasks
type ChainAsyncResult struct {
	asyncResults []*AsyncResult
	backend      Interface
}

// NewAsyncResult creates AsyncResult instance
func NewAsyncResult(signature *tasks.Signature, backend Interface) *AsyncResult {
	return &AsyncResult{
		Signature: signature,
		taskState: new(tasks.TaskState),
		backend:   backend,
	}
}

// NewChordAsyncResult creates ChordAsyncResult instance
func NewChordAsyncResult(groupTasks []*tasks.Signature, chordCallback *tasks.Signature, backend Interface) *ChordAsyncResult {
	asyncResults := make([]*AsyncResult, len(groupTasks))
	for i, task := range groupTasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChordAsyncResult{
		groupAsyncResults: asyncResults,
		chordAsyncResult:  NewAsyncResult(chordCallback, backend),
		backend:           backend,
	}
}

// NewChainAsyncResult creates ChainAsyncResult instance
func NewChainAsyncResult(tasks []*tasks.Signature, backend Interface) *ChainAsyncResult {
	asyncResults := make([]*AsyncResult, len(tasks))
	for i, task := range tasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChainAsyncResult{
		asyncResults: asyncResults,
		backend:      backend,
	}
}

//touch the state and no wait
func (asyncResult *AsyncResult) Touch() ([]reflect.Value, error) {
	if asyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}
	asyncResult.GetState()
	// Purge state if we are using AMQP backend
	_, isAMQPBackend := asyncResult.backend.(*AMQPBackend)
	if isAMQPBackend && asyncResult.taskState.IsCompleted() {
		asyncResult.backend.PurgeState(asyncResult.taskState.TaskUUID)
	}

	if asyncResult.taskState.IsSuccess() {
		resultValues := make([]reflect.Value, len(asyncResult.taskState.Results))
		for i, result := range asyncResult.taskState.Results {
			resultValue, err := tasks.ReflectValue(result.Type, result.Value)
			if err != nil {
				return nil, err
			}
			resultValues[i] = resultValue
		}
		return resultValues, nil
	}

	if asyncResult.taskState.IsFailure() {
		return nil, errors.New(asyncResult.taskState.Error)
	}
	return nil, nil
}

// Get returns task results (synchronous blocking call)
func (asyncResult *AsyncResult) Get(sleepDuration time.Duration) ([]reflect.Value, error) {
	if asyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	for {
		asyncResult.GetState()

		// Purge state if we are using AMQP backend
		_, isAMQPBackend := asyncResult.backend.(*AMQPBackend)
		if isAMQPBackend && asyncResult.taskState.IsCompleted() {
			asyncResult.backend.PurgeState(asyncResult.taskState.TaskUUID)
		}

		if asyncResult.taskState.IsSuccess() {
			resultValues := make([]reflect.Value, len(asyncResult.taskState.Results))
			for i, result := range asyncResult.taskState.Results {
				resultValue, err := tasks.ReflectValue(result.Type, result.Value)
				if err != nil {
					return nil, err
				}
				resultValues[i] = resultValue
			}
			return resultValues, nil
		}

		if asyncResult.taskState.IsFailure() {
			return nil, errors.New(asyncResult.taskState.Error)
		}

		<-time.After(sleepDuration)
	}
}

// GetWithTimeout returns task results limited in time (synchronous blocking call)
func (asyncResult *AsyncResult) GetWithTimeout(timeoutDuration, sleepDuration time.Duration) ([]reflect.Value, error) {
	if asyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	timeout := time.NewTimer(timeoutDuration)

	for {
		select {
		case <-timeout.C:
			return nil, errors.New("Timeout reached")
		default:
			asyncResult.GetState()

			// Purge state if we are using AMQP backend
			_, isAMQPBackend := asyncResult.backend.(*AMQPBackend)
			if isAMQPBackend && asyncResult.taskState.IsCompleted() {
				asyncResult.backend.PurgeState(asyncResult.taskState.TaskUUID)
			}

			if asyncResult.taskState.IsSuccess() {
				resultValues := make([]reflect.Value, len(asyncResult.taskState.Results))
				for i, result := range asyncResult.taskState.Results {
					resultValue, err := tasks.ReflectValue(result.Type, result.Value)
					if err != nil {
						return nil, err
					}
					resultValues[i] = resultValue
				}
				return resultValues, nil
			}

			if asyncResult.taskState.IsFailure() {
				return nil, errors.New(asyncResult.taskState.Error)
			}

			<-time.After(sleepDuration)
		}
	}
}

// GetState returns latest task state
func (asyncResult *AsyncResult) GetState() *tasks.TaskState {
	if asyncResult.taskState.IsCompleted() {
		return asyncResult.taskState
	}

	taskState, err := asyncResult.backend.GetState(asyncResult.Signature.UUID)
	if err == nil {
		asyncResult.taskState = taskState
	}

	return asyncResult.taskState
}

// Get returns results of a chain of tasks (synchronous blocking call)
func (chainAsyncResult *ChainAsyncResult) Get(sleepDuration time.Duration) ([]reflect.Value, error) {
	if chainAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var (
		results []reflect.Value
		err     error
	)

	for _, asyncResult := range chainAsyncResult.asyncResults {
		results, err = asyncResult.Get(sleepDuration)
		if err != nil {
			return nil, err
		}
	}

	return results, err
}

// Get returns result of a chord (synchronous blocking call)
func (chordAsyncResult *ChordAsyncResult) Get(sleepDuration time.Duration) ([]reflect.Value, error) {
	if chordAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var err error
	for _, asyncResult := range chordAsyncResult.groupAsyncResults {
		_, err = asyncResult.Get(sleepDuration)
		if err != nil {
			return nil, err
		}
	}

	return chordAsyncResult.chordAsyncResult.Get(sleepDuration)
}

// Get returns results of a chain of tasks with timeout
func (chainAsyncResult *ChainAsyncResult) GetWithTimeout(timeoutDuration, sleepDuration time.Duration) ([]reflect.Value, error) {
	if chainAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var (
		results []reflect.Value
		err     error
	)

	timeout := time.NewTimer(timeoutDuration)
	ln := len(chainAsyncResult.asyncResults)
	lastResult := chainAsyncResult.asyncResults[ln-1]

	for {
		select {
		case <-timeout.C:
			return nil, errors.New("Timeout reach")
		default:

			for _, asyncResult := range chainAsyncResult.asyncResults {
				_, errcur := asyncResult.Touch()
				if errcur != nil {
					return nil, err
				}
			}

			results, err = lastResult.Touch()
			if err != nil {
				return nil, err
			}
			if results != nil {
				return results, err
			}
			<-time.After(sleepDuration)
		}
	}
	return results, err
}

// Get returns result of a chord with timeout
func (chordAsyncResult *ChordAsyncResult) GetWithTimeout(timeoutDuration, sleepDuration time.Duration) ([]reflect.Value, error) {
	if chordAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var (
		results []reflect.Value
		err     error
	)

	timeout := time.NewTimer(timeoutDuration)
	for {
		select {
		case <-timeout.C:
			return nil, errors.New("Timeout reach")
		default:
			for _, asyncResult := range chordAsyncResult.groupAsyncResults {
				_, errcur := asyncResult.Touch()
				if errcur != nil {
					return nil, err
				}
			}

			results, err = chordAsyncResult.chordAsyncResult.Touch()
			if err != nil {
				return nil, nil
			}
			if results != nil {
				return results, err
			}
			<-time.After(sleepDuration)
		}
	}

	return results, err
}
<commit_msg>Formatting fixes for v1/backends/async_result.go<commit_after>package backends

import (
	"errors"
	"reflect"
	"time"

	"github.com/RichardKnop/machinery/v1/tasks"
)

// AsyncResult represents a task result
type AsyncResult struct {
	Signature *tasks.Signature
	taskState *tasks.TaskState
	backend   Interface
}

// ChordAsyncResult represents a result of a chord
type ChordAsyncResult struct {
	groupAsyncResults []*AsyncResult
	chordAsyncResult  *AsyncResult
	backend           Interface
}

// ChainAsyncResult represents a result of a chain of tasks
type ChainAsyncResult struct {
	asyncResults []*AsyncResult
	backend      Interface
}

// NewAsyncResult creates AsyncResult instance
func NewAsyncResult(signature *tasks.Signature, backend Interface) *AsyncResult {
	return &AsyncResult{
		Signature: signature,
		taskState: new(tasks.TaskState),
		backend:   backend,
	}
}

// NewChordAsyncResult creates ChordAsyncResult instance
func NewChordAsyncResult(groupTasks []*tasks.Signature, chordCallback *tasks.Signature, backend Interface) *ChordAsyncResult {
	asyncResults := make([]*AsyncResult, len(groupTasks))
	for i, task := range groupTasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChordAsyncResult{
		groupAsyncResults: asyncResults,
		chordAsyncResult:  NewAsyncResult(chordCallback, backend),
		backend:           backend,
	}
}

// NewChainAsyncResult creates ChainAsyncResult instance
func NewChainAsyncResult(tasks []*tasks.Signature, backend Interface) *ChainAsyncResult {
	asyncResults := make([]*AsyncResult, len(tasks))
	for i, task := range tasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChainAsyncResult{
		asyncResults: asyncResults,
		backend:      backend,
	}
}

// Touch the state and don't wait
func (asyncResult *AsyncResult) Touch() ([]reflect.Value, error) {
	if asyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	asyncResult.GetState()

	// Purge state if we are using AMQP backend
	_, isAMQPBackend := asyncResult.backend.(*AMQPBackend)
	if isAMQPBackend && asyncResult.taskState.IsCompleted() {
		asyncResult.backend.PurgeState(asyncResult.taskState.TaskUUID)
	}

	if asyncResult.taskState.IsSuccess() {
		resultValues := make([]reflect.Value, len(asyncResult.taskState.Results))
		for i, result := range asyncResult.taskState.Results {
			resultValue, err := tasks.ReflectValue(result.Type, result.Value)
			if err != nil {
				return nil, err
			}
			resultValues[i] = resultValue
		}
		return resultValues, nil
	}

	if asyncResult.taskState.IsFailure() {
		return nil, errors.New(asyncResult.taskState.Error)
	}

	return nil, nil
}

// Get returns task results (synchronous blocking call)
func (asyncResult *AsyncResult) Get(sleepDuration time.Duration) ([]reflect.Value, error) {
	for {
		result, err := asyncResult.Touch()

		if result == nil && err == nil {
			<-time.After(sleepDuration)
		} else {
			return result, err
		}
	}
}

// GetWithTimeout returns task results with a timeout (synchronous blocking call)
func (asyncResult *AsyncResult) GetWithTimeout(timeoutDuration, sleepDuration time.Duration) ([]reflect.Value, error) {
	timeout := time.NewTimer(timeoutDuration)

	for {
		select {
		case <-timeout.C:
			return nil, errors.New("Timeout reached")
		default:
			result, err := asyncResult.Touch()

			if result == nil && err == nil {
				<-time.After(sleepDuration)
			} else {
				return result, err
			}
		}
	}
}

// GetState returns latest task state
func (asyncResult *AsyncResult) GetState() *tasks.TaskState {
	if asyncResult.taskState.IsCompleted() {
		return asyncResult.taskState
	}

	taskState, err := asyncResult.backend.GetState(asyncResult.Signature.UUID)
	if err == nil {
		asyncResult.taskState = taskState
	}

	return asyncResult.taskState
}

// Get returns results of a chain of tasks (synchronous blocking call)
func (chainAsyncResult *ChainAsyncResult) Get(sleepDuration time.Duration) ([]reflect.Value, error) {
	if chainAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var (
		results []reflect.Value
		err     error
	)

	for _, asyncResult := range chainAsyncResult.asyncResults {
		results, err = asyncResult.Get(sleepDuration)
		if err != nil {
			return nil, err
		}
	}

	return results, err
}

// Get returns result of a chord (synchronous blocking call)
func (chordAsyncResult *ChordAsyncResult) Get(sleepDuration time.Duration) ([]reflect.Value, error) {
	if chordAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var err error
	for _, asyncResult := range chordAsyncResult.groupAsyncResults {
		_, err = asyncResult.Get(sleepDuration)
		if err != nil {
			return nil, err
		}
	}

	return chordAsyncResult.chordAsyncResult.Get(sleepDuration)
}

// GetWithTimeout returns results of a chain of tasks with timeout (synchronous blocking call)
func (chainAsyncResult *ChainAsyncResult) GetWithTimeout(timeoutDuration, sleepDuration time.Duration) ([]reflect.Value, error) {
	if chainAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var (
		results []reflect.Value
		err     error
	)

	timeout := time.NewTimer(timeoutDuration)
	ln := len(chainAsyncResult.asyncResults)
	lastResult := chainAsyncResult.asyncResults[ln-1]

	for {
		select {
		case <-timeout.C:
			return nil, errors.New("Timeout reached")
		default:

			for _, asyncResult := range chainAsyncResult.asyncResults {
				_, errcur := asyncResult.Touch()
				if errcur != nil {
					return nil, err
				}
			}

			results, err = lastResult.Touch()
			if err != nil {
				return nil, err
			}
			if results != nil {
				return results, err
			}
			<-time.After(sleepDuration)
		}
	}
}

// GetWithTimeout returns result of a chord with a timeout (synchronous blocking call)
func (chordAsyncResult *ChordAsyncResult) GetWithTimeout(timeoutDuration, sleepDuration time.Duration) ([]reflect.Value, error) {
	if chordAsyncResult.backend == nil {
		return nil, errors.New("Result backend not configured")
	}

	var (
		results []reflect.Value
		err     error
	)

	timeout := time.NewTimer(timeoutDuration)
	for {
		select {
		case <-timeout.C:
			return nil, errors.New("Timeout reached")
		default:
			for _, asyncResult := range chordAsyncResult.groupAsyncResults {
				_, errcur := asyncResult.Touch()
				if errcur != nil {
					return nil, err
				}
			}

			results, err = chordAsyncResult.chordAsyncResult.Touch()
			if err != nil {
				return nil, nil
			}
			if results != nil {
				return results, err
			}
			<-time.After(sleepDuration)
		}
	}
}
<|endoftext|>


<commit_before>package slogger

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"
)

// Do not set this to zero or deadlocks might occur
const ROLLING_FILE_APPENDER_CHANNEL_SIZE = 4096

type RollingFileAppender struct {
	MaxFileSize uint64
	file *os.File
	absPath string
	curFileSize uint64
	appendCh chan *Log
	syncCh chan bool
	errHandler func(error)
	headerGenerator func() string
}

func NewRollingFileAppender(filename string, maxFileSize uint64, errHandler func(error), headerGenerator func() string) (*RollingFileAppender, error) {
	if errHandler == nil {
		errHandler = func(err error) { }
	}

	absPath, err := filepath.Abs(filename)
	if err != nil {
		return nil, err
	}

	file, err := os.OpenFile(
		absPath,
		os.O_WRONLY | os.O_APPEND | os.O_CREATE,
		0666,
	)
	if err != nil {
		return nil, err
	}

	fileInfo, err := file.Stat()
	if err != nil {
		return nil, err
	}

	curFileSize := uint64(fileInfo.Size())

	appender := &RollingFileAppender {
		MaxFileSize: maxFileSize,
		file: file,
		absPath: absPath,
		curFileSize: curFileSize,
		appendCh: make(chan *Log, ROLLING_FILE_APPENDER_CHANNEL_SIZE),
		syncCh: make(chan bool),
		errHandler: errHandler,
		headerGenerator: headerGenerator,
	}

	go appender.listenForAppends()
	appender.logHeader()
	return appender, nil
}

func (self RollingFileAppender) Append(log *Log) error {
	select {
	case self.appendCh <- log:
		// nothing else to do
	default:
		// channel is full. log a warning
		self.appendCh <- fullWarningLog()
		self.appendCh <- log
	}
	return nil
}

func (self RollingFileAppender) Close() error {
	self.waitUntilEmpty()
	return self.file.Close()
}

// These are commented out until I determine as to whether they are thread-safe -Tim

// func (self RollingFileAppender) SetErrHandler(errHandler func(error)) {
// 	self.errHandler = errHandler
// }

// func (self RollingFileAppender) SetHeaderGenerator(headerGenerator func() string) {
// 	self.headerGenerator = headerGenerator
// 	self.logHeader()
// }

func fullWarningLog() *Log {
	return internalWarningLog(
		"appendCh is full. You may want to increase ROLLING_FILE_APPENDER_CHANNEL_SIZE (currently %d).",
		[]interface{}{ROLLING_FILE_APPENDER_CHANNEL_SIZE},
	)
}

func internalWarningLog(messageFmt string, args []interface{}) *Log {
	return simpleLog("RollingFileAppender", WARN, 3, messageFmt, args)
}

func newRotatedFilename(baseFilename string) string {
	now := time.Now()

	return fmt.Sprintf("%s.%d-%02d-%02dT%02d-%02d-%02d",
		baseFilename,
		now.Year(),
		now.Month(),
		now.Day(),
		now.Hour(),
		now.Minute(),
		now.Second())
}

func simpleLog(prefix string, level Level, callerSkip int, messageFmt string, args []interface{}) *Log {
	_, file, line, ok := runtime.Caller(callerSkip)
	if !ok {
		file = "UNKNOWN_FILE"
		line = -1
	}

	return &Log {
		Prefix: prefix,
		Level: level,
		Filename: file,
		Line: line,
		Timestamp: time.Now(),
		messageFmt: messageFmt,
		args: args,
	}
}

func (self RollingFileAppender) listenForAppends() {
	needsSync := false
	for {
		if needsSync {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
			default:
				self.file.Sync()
				needsSync = false
			}
		} else {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
				needsSync = true
			case <- self.syncCh:
				self.syncCh <- (len(self.appendCh) <= 0)
			}
		}
	}
}

func (self RollingFileAppender) logHeader() {
	if self.headerGenerator != nil {
		header := self.headerGenerator()
		log := simpleLog("header", INFO, 3, header, []interface{}{})

		// do not count header as part of size towards rotation in
		// order to prevent infinite rotation when max size is smaller
		// than header
		self.reallyAppend(log, false)
	}
}

func (self RollingFileAppender) reallyAppend(log *Log, trackSize bool) {
	if self.file == nil {
		self.errHandler(errors.New("I have no logfile to write to!"))
		return
	}

	msg := FormatLog(log)

	n, err := self.file.WriteString(msg)

	if err != nil {
		self.errHandler(fmt.Errorf("Could not log to %s : %s", self.file.Name(), err.Error()))
		return
	}

	if trackSize {
		self.curFileSize += uint64(n)

		if self.curFileSize > self.MaxFileSize {
			self.rotate()
		}
	}
	return
}

// returns true on success, false otherwise
func (self RollingFileAppender) renameLogFile(oldFilename, newFilename string) bool {
	err := os.Rename(oldFilename, newFilename)
	if err != nil {
		self.errHandler(fmt.Errorf(
			"Error while renaming %s to %s . Will reopen. : %s",
			oldFilename, newFilename, err.Error()))

		file, err := os.OpenFile(oldFilename, os.O_RDWR, 0666)

		if err == nil {
			self.file = file
		} else {
			self.curFileSize = 0
			self.file = nil
			self.errHandler(fmt.Errorf(
				"Error while reopening %s after failing to rename. Further logging will fail: %s",
				oldFilename, err.Error()))
		}
		return false
	}
	self.curFileSize = 0
	return true
}

func (self RollingFileAppender) rotate() {
	// close current log
	if err := self.file.Close(); err != nil {
		self.errHandler(fmt.Errorf(
			"Error while closing %s : %s" , self.absPath, err.Error()))
	}

	// rename old log
	if !self.renameLogFile(self.absPath, newRotatedFilename(self.absPath)) {
		return
	}

	// create new log
	file, err := os.Create(self.absPath)

	if err != nil {
		self.file = nil
		self.errHandler(fmt.Errorf(
			"Failed to create %s . Further logging will fail. : %s",
			self.absPath, err.Error()))
		return
	}

	self.file = file
	self.logHeader()
	return
}

func (self RollingFileAppender) waitUntilEmpty() {
	self.syncCh <- true
	for !(<- self.syncCh) {
		self.syncCh <- true
	}
}

<commit_msg>Change syncCh to be a chan (chan bool) so that a separate channel is used for replies so that multiple simultaneous calls to Close() do not cause problems.<commit_after>package slogger

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"
)

// Do not set this to zero or deadlocks might occur
const ROLLING_FILE_APPENDER_CHANNEL_SIZE = 4096

type RollingFileAppender struct {
	MaxFileSize uint64
	file *os.File
	absPath string
	curFileSize uint64
	appendCh chan *Log
	syncCh chan (chan bool)
	errHandler func(error)
	headerGenerator func() string
}

func NewRollingFileAppender(filename string, maxFileSize uint64, errHandler func(error), headerGenerator func() string) (*RollingFileAppender, error) {
	if errHandler == nil {
		errHandler = func(err error) { }
	}

	absPath, err := filepath.Abs(filename)
	if err != nil {
		return nil, err
	}

	file, err := os.OpenFile(
		absPath,
		os.O_WRONLY | os.O_APPEND | os.O_CREATE,
		0666,
	)
	if err != nil {
		return nil, err
	}

	fileInfo, err := file.Stat()
	if err != nil {
		return nil, err
	}

	curFileSize := uint64(fileInfo.Size())

	appender := &RollingFileAppender {
		MaxFileSize: maxFileSize,
		file: file,
		absPath: absPath,
		curFileSize: curFileSize,
		appendCh: make(chan *Log, ROLLING_FILE_APPENDER_CHANNEL_SIZE),
		syncCh: make(chan (chan bool)),
		errHandler: errHandler,
		headerGenerator: headerGenerator,
	}

	go appender.listenForAppends()
	appender.logHeader()
	return appender, nil
}

func (self RollingFileAppender) Append(log *Log) error {
	select {
	case self.appendCh <- log:
		// nothing else to do
	default:
		// channel is full. log a warning
		self.appendCh <- fullWarningLog()
		self.appendCh <- log
	}
	return nil
}

func (self RollingFileAppender) Close() error {
	self.waitUntilEmpty()
	return self.file.Close()
}

// These are commented out until I determine as to whether they are thread-safe -Tim

// func (self RollingFileAppender) SetErrHandler(errHandler func(error)) {
// 	self.errHandler = errHandler
// }

// func (self RollingFileAppender) SetHeaderGenerator(headerGenerator func() string) {
// 	self.headerGenerator = headerGenerator
// 	self.logHeader()
// }

func fullWarningLog() *Log {
	return internalWarningLog(
		"appendCh is full. You may want to increase ROLLING_FILE_APPENDER_CHANNEL_SIZE (currently %d).",
		[]interface{}{ROLLING_FILE_APPENDER_CHANNEL_SIZE},
	)
}

func internalWarningLog(messageFmt string, args []interface{}) *Log {
	return simpleLog("RollingFileAppender", WARN, 3, messageFmt, args)
}

func newRotatedFilename(baseFilename string) string {
	now := time.Now()

	return fmt.Sprintf("%s.%d-%02d-%02dT%02d-%02d-%02d",
		baseFilename,
		now.Year(),
		now.Month(),
		now.Day(),
		now.Hour(),
		now.Minute(),
		now.Second())
}

func simpleLog(prefix string, level Level, callerSkip int, messageFmt string, args []interface{}) *Log {
	_, file, line, ok := runtime.Caller(callerSkip)
	if !ok {
		file = "UNKNOWN_FILE"
		line = -1
	}

	return &Log {
		Prefix: prefix,
		Level: level,
		Filename: file,
		Line: line,
		Timestamp: time.Now(),
		messageFmt: messageFmt,
		args: args,
	}
}

func (self RollingFileAppender) listenForAppends() {
	needsSync := false
	for {
		if needsSync {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
			default:
				self.file.Sync()
				needsSync = false
			}
		} else {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
				needsSync = true
			case syncReplyCh := <- self.syncCh:
				syncReplyCh <- (len(self.appendCh) <= 0)
			}
		}
	}
}

func (self RollingFileAppender) logHeader() {
	if self.headerGenerator != nil {
		header := self.headerGenerator()
		log := simpleLog("header", INFO, 3, header, []interface{}{})

		// do not count header as part of size towards rotation in
		// order to prevent infinite rotation when max size is smaller
		// than header
		self.reallyAppend(log, false)
	}
}

func (self RollingFileAppender) reallyAppend(log *Log, trackSize bool) {
	if self.file == nil {
		self.errHandler(errors.New("I have no logfile to write to!"))
		return
	}

	msg := FormatLog(log)

	n, err := self.file.WriteString(msg)

	if err != nil {
		self.errHandler(fmt.Errorf("Could not log to %s : %s", self.file.Name(), err.Error()))
		return
	}

	if trackSize {
		self.curFileSize += uint64(n)

		if self.curFileSize > self.MaxFileSize {
			self.rotate()
		}
	}
	return
}

// returns true on success, false otherwise
func (self RollingFileAppender) renameLogFile(oldFilename, newFilename string) bool {
	err := os.Rename(oldFilename, newFilename)
	if err != nil {
		self.errHandler(fmt.Errorf(
			"Error while renaming %s to %s . Will reopen. : %s",
			oldFilename, newFilename, err.Error()))

		file, err := os.OpenFile(oldFilename, os.O_RDWR, 0666)

		if err == nil {
			self.file = file
		} else {
			self.curFileSize = 0
			self.file = nil
			self.errHandler(fmt.Errorf(
				"Error while reopening %s after failing to rename. Further logging will fail: %s",
				oldFilename, err.Error()))
		}
		return false
	}
	self.curFileSize = 0
	return true
}

func (self RollingFileAppender) rotate() {
	// close current log
	if err := self.file.Close(); err != nil {
		self.errHandler(fmt.Errorf(
			"Error while closing %s : %s" , self.absPath, err.Error()))
	}

	// rename old log
	if !self.renameLogFile(self.absPath, newRotatedFilename(self.absPath)) {
		return
	}

	// create new log
	file, err := os.Create(self.absPath)

	if err != nil {
		self.file = nil
		self.errHandler(fmt.Errorf(
			"Failed to create %s . Further logging will fail. : %s",
			self.absPath, err.Error()))
		return
	}

	self.file = file
	self.logHeader()
	return
}

func (self RollingFileAppender) waitUntilEmpty() {
	replyCh := make(chan bool)
	self.syncCh <- replyCh
	for !(<- replyCh) {
		self.syncCh <- replyCh
	}
}

<|endoftext|>


<commit_before>package inigo_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"testing"
	"time"

	"github.com/onsi/ginkgo/config"

	"github.com/cloudfoundry/gunk/natsrunner"

	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/vito/cmdtest"
	"github.com/cloudfoundry-incubator/gordon"

	"github.com/cloudfoundry-incubator/inigo/executor_runner"
	"github.com/cloudfoundry-incubator/inigo/fake_cc"
	"github.com/cloudfoundry-incubator/inigo/fileserver_runner"
	"github.com/cloudfoundry-incubator/inigo/inigo_server"
	"github.com/cloudfoundry-incubator/inigo/loggregator_runner"
	"github.com/cloudfoundry-incubator/inigo/servistry_runner"
	"github.com/cloudfoundry-incubator/inigo/stager_runner"
	WardenRunner "github.com/cloudfoundry-incubator/warden-linux/integration/runner"
)

var SHORT_TIMEOUT = 5.0
var LONG_TIMEOUT = 10.0

var etcdRunner *etcdstorerunner.ETCDClusterRunner

var wardenRunner *WardenRunner.Runner
var wardenAddr = filepath.Join(os.TempDir(), "warden-temp-socker", "warden.sock")
var wardenClient gordon.Client

var natsRunner *natsrunner.NATSRunner
var natsPort int

var loggregatorRunner *loggregator_runner.LoggregatorRunner
var loggregatorPort int
var loggregatorSharedSecret string

var executorRunner *executor_runner.ExecutorRunner
var executorPath string

var stagerRunner *stager_runner.StagerRunner
var stagerPath string

var servistryRunner *servistry_runner.ServistryRunner

var fakeCC *fake_cc.FakeCC
var fakeCCAddress string

var fileServerRunner *fileserver_runner.FileServerRunner
var fileServerPath string
var fileServerPort int

var smelterZipPath string

func TestInigo(t *testing.T) {
	var err error
	if os.Getenv("SHORT_TIMEOUT") != "" {
		SHORT_TIMEOUT, err = strconv.ParseFloat(os.Getenv("SHORT_TIMEOUT"), 64)
		if err != nil {
			panic(err)
		}
	}

	if os.Getenv("LONG_TIMEOUT") != "" {
		LONG_TIMEOUT, err = strconv.ParseFloat(os.Getenv("LONG_TIMEOUT"), 64)
		if err != nil {
			panic(err)
		}
	}

	registerSignalHandler()
	RegisterFailHandler(Fail)

	startUpFakeCC()
	setUpEtcd()
	setupWarden()
	setUpNats()
	setUpLoggregator()
	setUpExecutor()
	setUpStager()
	setUpServistry()
	compileAndZipUpSmelter()
	setUpFileServer()
	connectToWarden()

	BeforeEach(func() {
		fakeCC.Reset()
		etcdRunner.Start()
		natsRunner.Start()
		loggregatorRunner.Start()

		inigo_server.Start(wardenClient)

		currentTestDescription := CurrentGinkgoTestDescription()
		fmt.Fprintf(GinkgoWriter, "\n%s\n%s\n\n", strings.Repeat("~", 50), currentTestDescription.FullTestText)
	})

	AfterEach(func() {
		executorRunner.Stop()
		stagerRunner.Stop()
		fileServerRunner.Stop()

		loggregatorRunner.Stop()
		natsRunner.Stop()
		etcdRunner.Stop()

		inigo_server.Stop(wardenClient)
	})

	RunSpecs(t, "Inigo Integration Suite")

	if config.GinkgoConfig.ParallelNode == 1 && config.GinkgoConfig.ParallelTotal > 1 {
		waitForOtherGinkgoNodes()
	}

	notifyFinished()

	cleanup()
}

func startUpFakeCC() {
	BeforeEach(func() {
		fakeCC = fake_cc.New()
		fakeCCAddress = fakeCC.Start()
	})
}

func setUpEtcd() {
	BeforeEach(func() {
		etcdRunner = etcdstorerunner.NewETCDClusterRunner(
			5001+config.GinkgoConfig.ParallelNode,
			1,
		)
	})
}

func setupWarden() {
	wardenBinPath := os.Getenv("WARDEN_BINPATH")
	wardenRootfs := os.Getenv("WARDEN_ROOTFS")

	if wardenBinPath == "" || wardenRootfs == "" {
		println("Please define either WARDEN_NETWORK and WARDEN_ADDR (for a running Warden), or")
		println("WARDEN_BINPATH and WARDEN_ROOTFS (for the tests to start it)")
		println("")
		failFast("warden is not set up")
		return
	}
	var err error

	err = os.MkdirAll(filepath.Dir(wardenAddr), 0700)
	if err != nil {
		failFast(err.Error())
	}

	wardenPath, err := cmdtest.BuildIn(os.Getenv("WARDEN_LINUX_GOPATH"), "github.com/cloudfoundry-incubator/warden-linux", "-race")
	if err != nil {
		failFast("failed to compile warden:", err)
	}

	wardenRunner, err = WardenRunner.New(wardenPath, wardenBinPath, wardenRootfs, "unix", wardenAddr)
	if err != nil {
		failFast("warden failed to initialize: " + err.Error())
	}

	wardenRunner.SnapshotsPath = ""
}

func connectToWarden() {
	var err error
	if config.GinkgoConfig.ParallelNode == 1 {
		err = wardenRunner.Start()
	} else {
		err = wardenRunner.WaitForStart()
	}

	if err != nil {
		failFast("warden failed to start: " + err.Error())
	}

	wardenClient = wardenRunner.NewClient()

	err = wardenClient.Connect()
	if err != nil {
		failFast("warden is not up: " + err.Error())
		return
	}
}

func setUpNats() {
	natsPort = 4222 + config.GinkgoConfig.ParallelNode

	BeforeEach(func() {
		natsRunner = natsrunner.NewNATSRunner(natsPort)
	})
}

func setUpLoggregator() {
	loggregatorPort = 3456 + config.GinkgoConfig.ParallelNode
	loggregatorSharedSecret = "conspiracy"

	loggregatorPath, err := cmdtest.BuildIn(os.Getenv("LOGGREGATOR_GOPATH"), "loggregator/loggregator")
	if err != nil {
		failFast("failed to compile loggregator:", err)
	}

	BeforeEach(func() {
		loggregatorRunner = loggregator_runner.New(
			loggregatorPath,
			loggregator_runner.Config{
				IncomingPort:           loggregatorPort,
				OutgoingPort:           8083 + config.GinkgoConfig.ParallelNode,
				MaxRetainedLogMessages: 1000,
				SharedSecret:           loggregatorSharedSecret,
				NatsHost:               "127.0.0.1",
				NatsPort:               natsPort,
			},
		)
	})
}

func setUpExecutor() {
	var err error
	executorPath, err = cmdtest.BuildIn(os.Getenv("EXECUTOR_GOPATH"), "github.com/cloudfoundry-incubator/executor", "-race")
	if err != nil {
		failFast("failed to compile executor:", err)
	}

	BeforeEach(func() {
		executorRunner = executor_runner.New(
			executorPath,
			wardenRunner.Network,
			wardenRunner.Addr,
			etcdRunner.NodeURLS(),
			fmt.Sprintf("127.0.0.1:%d", loggregatorPort),
			loggregatorSharedSecret,
		)
	})
}

func setUpStager() {
	var err error
	stagerPath, err = cmdtest.BuildIn(os.Getenv("STAGER_GOPATH"), "github.com/cloudfoundry-incubator/stager", "-race")
	if err != nil {
		failFast("failed to compile stager:", err)
	}

	BeforeEach(func() {
		stagerRunner = stager_runner.New(
			stagerPath,
			etcdRunner.NodeURLS(),
			[]string{fmt.Sprintf("127.0.0.1:%d", natsPort)},
		)
	})
}

func setUpServistry() {
	servistryPath, err := cmdtest.BuildIn(os.Getenv("SERVISTRY_GOPATH"), "github.com/cloudfoundry-incubator/servistry", "-race")
	if err != nil {
		failFast("failed to compile servistry:", err)
	}

	servistryRunner = servistry_runner.New(
		servistryPath,
		etcdRunner.NodeURLS(),
		[]string{fmt.Sprintf("127.0.0.1:%d", natsPort)},
	)
}

func compileAndZipUpSmelter() {
	smelterPath, err := cmdtest.BuildIn(os.Getenv("LINUX_SMELTER_GOPATH"), "github.com/cloudfoundry-incubator/linux-smelter", "-race")
	if err != nil {
		failFast("failed to compile smelter", err)
	}

	smelterDir := filepath.Dir(smelterPath)
	err = os.Rename(smelterPath, filepath.Join(smelterDir, "run"))
	if err != nil {
		failFast("failed to move smelter", err)
	}

	cmd := exec.Command("zip", "smelter.zip", "run")
	cmd.Dir = smelterDir
	err = cmd.Run()
	if err != nil {
		failFast("failed to zip up smelter", err)
	}

	smelterZipPath = filepath.Join(smelterDir, "smelter.zip")
}

func setUpFileServer() {
	var err error
	fileServerPath, err = cmdtest.BuildIn(os.Getenv("FILE_SERVER_GOPATH"), "github.com/cloudfoundry-incubator/file-server", "-race")
	if err != nil {
		failFast("failed to compile file server:", err)
	}

	fileServerPort = 12760 + config.GinkgoConfig.ParallelNode

	BeforeEach(func() {
		fileServerRunner = fileserver_runner.New(
			fileServerPath,
			fileServerPort,
			etcdRunner.NodeURLS(),
			fakeCCAddress,
			fake_cc.CC_USERNAME,
			fake_cc.CC_PASSWORD,
		)
	})
}

func cleanup() {
	defer GinkgoRecover()

	if etcdRunner != nil {
		etcdRunner.Stop()
	}

	if config.GinkgoConfig.ParallelNode == 1 {
		if wardenRunner != nil {
			wardenRunner.TearDown()
			wardenRunner.Stop()
		}
	}

	if natsRunner != nil {
		natsRunner.Stop()
	}

	if loggregatorRunner != nil {
		loggregatorRunner.Stop()
	}

	if executorRunner != nil {
		executorRunner.Stop()
	}

	if stagerRunner != nil {
		stagerRunner.Stop()
	}

	if fileServerRunner != nil {
		fileServerRunner.Stop()
	}
}

func failFast(msg string, errs ...error) {
	println("!!!!! " + msg + " !!!!!")
	if len(errs) > 0 {
		println("error: " + errs[0].Error())
	}
	cleanup()
	os.Exit(1)
}

func waitForOtherGinkgoNodes() {
	for {
		found := true
		for i := 2; i <= config.GinkgoConfig.ParallelTotal; i++ {
			_, err := os.Stat(fmt.Sprintf("/tmp/ginkgo-%d", i))
			if err != nil {
				found = false
			}
		}
		if found {
			return
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func notifyFinished() {
	ioutil.WriteFile(fmt.Sprintf("/tmp/ginkgo-%d", config.GinkgoConfig.ParallelNode), []byte("done"), 0777)
}

func registerSignalHandler() {
	c := make(chan os.Signal, 1)

	go func() {
		select {
		case <-c:
			println("cleaning up!")

			cleanup()

			println("goodbye!")
			os.Exit(1)
		}
	}()

	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
}
<commit_msg>Changed servistry to set up before each test, like the other component setups.<commit_after>package inigo_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"testing"
	"time"

	"github.com/onsi/ginkgo/config"

	"github.com/cloudfoundry/gunk/natsrunner"

	"github.com/cloudfoundry-incubator/gordon"
	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/vito/cmdtest"

	"github.com/cloudfoundry-incubator/inigo/executor_runner"
	"github.com/cloudfoundry-incubator/inigo/fake_cc"
	"github.com/cloudfoundry-incubator/inigo/fileserver_runner"
	"github.com/cloudfoundry-incubator/inigo/inigo_server"
	"github.com/cloudfoundry-incubator/inigo/loggregator_runner"
	"github.com/cloudfoundry-incubator/inigo/servistry_runner"
	"github.com/cloudfoundry-incubator/inigo/stager_runner"
	WardenRunner "github.com/cloudfoundry-incubator/warden-linux/integration/runner"
)

var SHORT_TIMEOUT = 5.0
var LONG_TIMEOUT = 10.0

var etcdRunner *etcdstorerunner.ETCDClusterRunner

var wardenRunner *WardenRunner.Runner
var wardenAddr = filepath.Join(os.TempDir(), "warden-temp-socker", "warden.sock")
var wardenClient gordon.Client

var natsRunner *natsrunner.NATSRunner
var natsPort int

var loggregatorRunner *loggregator_runner.LoggregatorRunner
var loggregatorPort int
var loggregatorSharedSecret string

var executorRunner *executor_runner.ExecutorRunner
var executorPath string

var stagerRunner *stager_runner.StagerRunner
var stagerPath string

var servistryRunner *servistry_runner.ServistryRunner

var fakeCC *fake_cc.FakeCC
var fakeCCAddress string

var fileServerRunner *fileserver_runner.FileServerRunner
var fileServerPath string
var fileServerPort int

var smelterZipPath string

func TestInigo(t *testing.T) {
	var err error
	if os.Getenv("SHORT_TIMEOUT") != "" {
		SHORT_TIMEOUT, err = strconv.ParseFloat(os.Getenv("SHORT_TIMEOUT"), 64)
		if err != nil {
			panic(err)
		}
	}

	if os.Getenv("LONG_TIMEOUT") != "" {
		LONG_TIMEOUT, err = strconv.ParseFloat(os.Getenv("LONG_TIMEOUT"), 64)
		if err != nil {
			panic(err)
		}
	}

	registerSignalHandler()
	RegisterFailHandler(Fail)

	startUpFakeCC()
	setUpEtcd()
	setupWarden()
	setUpNats()
	setUpLoggregator()
	setUpExecutor()
	setUpStager()
	setUpServistry()
	compileAndZipUpSmelter()
	setUpFileServer()
	connectToWarden()

	BeforeEach(func() {
		fakeCC.Reset()
		etcdRunner.Start()
		natsRunner.Start()
		loggregatorRunner.Start()

		inigo_server.Start(wardenClient)

		currentTestDescription := CurrentGinkgoTestDescription()
		fmt.Fprintf(GinkgoWriter, "\n%s\n%s\n\n", strings.Repeat("~", 50), currentTestDescription.FullTestText)
	})

	AfterEach(func() {
		executorRunner.Stop()
		stagerRunner.Stop()
		fileServerRunner.Stop()

		loggregatorRunner.Stop()
		natsRunner.Stop()
		etcdRunner.Stop()

		inigo_server.Stop(wardenClient)
	})

	RunSpecs(t, "Inigo Integration Suite")

	if config.GinkgoConfig.ParallelNode == 1 && config.GinkgoConfig.ParallelTotal > 1 {
		waitForOtherGinkgoNodes()
	}

	notifyFinished()

	cleanup()
}

func startUpFakeCC() {
	BeforeEach(func() {
		fakeCC = fake_cc.New()
		fakeCCAddress = fakeCC.Start()
	})
}

func setUpEtcd() {
	BeforeEach(func() {
		etcdRunner = etcdstorerunner.NewETCDClusterRunner(
			5001+config.GinkgoConfig.ParallelNode,
			1,
		)
	})
}

func setupWarden() {
	wardenBinPath := os.Getenv("WARDEN_BINPATH")
	wardenRootfs := os.Getenv("WARDEN_ROOTFS")

	if wardenBinPath == "" || wardenRootfs == "" {
		println("Please define either WARDEN_NETWORK and WARDEN_ADDR (for a running Warden), or")
		println("WARDEN_BINPATH and WARDEN_ROOTFS (for the tests to start it)")
		println("")
		failFast("warden is not set up")
		return
	}
	var err error

	err = os.MkdirAll(filepath.Dir(wardenAddr), 0700)
	if err != nil {
		failFast(err.Error())
	}

	wardenPath, err := cmdtest.BuildIn(os.Getenv("WARDEN_LINUX_GOPATH"), "github.com/cloudfoundry-incubator/warden-linux", "-race")
	if err != nil {
		failFast("failed to compile warden:", err)
	}

	wardenRunner, err = WardenRunner.New(wardenPath, wardenBinPath, wardenRootfs, "unix", wardenAddr)
	if err != nil {
		failFast("warden failed to initialize: " + err.Error())
	}

	wardenRunner.SnapshotsPath = ""
}

func connectToWarden() {
	var err error
	if config.GinkgoConfig.ParallelNode == 1 {
		err = wardenRunner.Start()
	} else {
		err = wardenRunner.WaitForStart()
	}

	if err != nil {
		failFast("warden failed to start: " + err.Error())
	}

	wardenClient = wardenRunner.NewClient()

	err = wardenClient.Connect()
	if err != nil {
		failFast("warden is not up: " + err.Error())
		return
	}
}

func setUpNats() {
	natsPort = 4222 + config.GinkgoConfig.ParallelNode

	BeforeEach(func() {
		natsRunner = natsrunner.NewNATSRunner(natsPort)
	})
}

func setUpLoggregator() {
	loggregatorPort = 3456 + config.GinkgoConfig.ParallelNode
	loggregatorSharedSecret = "conspiracy"

	loggregatorPath, err := cmdtest.BuildIn(os.Getenv("LOGGREGATOR_GOPATH"), "loggregator/loggregator")
	if err != nil {
		failFast("failed to compile loggregator:", err)
	}

	BeforeEach(func() {
		loggregatorRunner = loggregator_runner.New(
			loggregatorPath,
			loggregator_runner.Config{
				IncomingPort:           loggregatorPort,
				OutgoingPort:           8083 + config.GinkgoConfig.ParallelNode,
				MaxRetainedLogMessages: 1000,
				SharedSecret:           loggregatorSharedSecret,
				NatsHost:               "127.0.0.1",
				NatsPort:               natsPort,
			},
		)
	})
}

func setUpExecutor() {
	var err error
	executorPath, err = cmdtest.BuildIn(os.Getenv("EXECUTOR_GOPATH"), "github.com/cloudfoundry-incubator/executor", "-race")
	if err != nil {
		failFast("failed to compile executor:", err)
	}

	BeforeEach(func() {
		executorRunner = executor_runner.New(
			executorPath,
			wardenRunner.Network,
			wardenRunner.Addr,
			etcdRunner.NodeURLS(),
			fmt.Sprintf("127.0.0.1:%d", loggregatorPort),
			loggregatorSharedSecret,
		)
	})
}

func setUpStager() {
	var err error
	stagerPath, err = cmdtest.BuildIn(os.Getenv("STAGER_GOPATH"), "github.com/cloudfoundry-incubator/stager", "-race")
	if err != nil {
		failFast("failed to compile stager:", err)
	}

	BeforeEach(func() {
		stagerRunner = stager_runner.New(
			stagerPath,
			etcdRunner.NodeURLS(),
			[]string{fmt.Sprintf("127.0.0.1:%d", natsPort)},
		)
	})
}

func setUpServistry() {
	servistryPath, err := cmdtest.BuildIn(os.Getenv("SERVISTRY_GOPATH"), "github.com/cloudfoundry-incubator/servistry", "-race")
	if err != nil {
		failFast("failed to compile servistry:", err)
	}

	BeforeEach(func() {
		servistryRunner = servistry_runner.New(
			servistryPath,
			etcdRunner.NodeURLS(),
			[]string{fmt.Sprintf("127.0.0.1:%d", natsPort)},
		)
	})
}

func compileAndZipUpSmelter() {
	smelterPath, err := cmdtest.BuildIn(os.Getenv("LINUX_SMELTER_GOPATH"), "github.com/cloudfoundry-incubator/linux-smelter", "-race")
	if err != nil {
		failFast("failed to compile smelter", err)
	}

	smelterDir := filepath.Dir(smelterPath)
	err = os.Rename(smelterPath, filepath.Join(smelterDir, "run"))
	if err != nil {
		failFast("failed to move smelter", err)
	}

	cmd := exec.Command("zip", "smelter.zip", "run")
	cmd.Dir = smelterDir
	err = cmd.Run()
	if err != nil {
		failFast("failed to zip up smelter", err)
	}

	smelterZipPath = filepath.Join(smelterDir, "smelter.zip")
}

func setUpFileServer() {
	var err error
	fileServerPath, err = cmdtest.BuildIn(os.Getenv("FILE_SERVER_GOPATH"), "github.com/cloudfoundry-incubator/file-server", "-race")
	if err != nil {
		failFast("failed to compile file server:", err)
	}

	fileServerPort = 12760 + config.GinkgoConfig.ParallelNode

	BeforeEach(func() {
		fileServerRunner = fileserver_runner.New(
			fileServerPath,
			fileServerPort,
			etcdRunner.NodeURLS(),
			fakeCCAddress,
			fake_cc.CC_USERNAME,
			fake_cc.CC_PASSWORD,
		)
	})
}

func cleanup() {
	defer GinkgoRecover()

	if etcdRunner != nil {
		etcdRunner.Stop()
	}

	if config.GinkgoConfig.ParallelNode == 1 {
		if wardenRunner != nil {
			wardenRunner.TearDown()
			wardenRunner.Stop()
		}
	}

	if natsRunner != nil {
		natsRunner.Stop()
	}

	if loggregatorRunner != nil {
		loggregatorRunner.Stop()
	}

	if executorRunner != nil {
		executorRunner.Stop()
	}

	if stagerRunner != nil {
		stagerRunner.Stop()
	}

	if fileServerRunner != nil {
		fileServerRunner.Stop()
	}
}

func failFast(msg string, errs ...error) {
	println("!!!!! " + msg + " !!!!!")
	if len(errs) > 0 {
		println("error: " + errs[0].Error())
	}
	cleanup()
	os.Exit(1)
}

func waitForOtherGinkgoNodes() {
	for {
		found := true
		for i := 2; i <= config.GinkgoConfig.ParallelTotal; i++ {
			_, err := os.Stat(fmt.Sprintf("/tmp/ginkgo-%d", i))
			if err != nil {
				found = false
			}
		}
		if found {
			return
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func notifyFinished() {
	ioutil.WriteFile(fmt.Sprintf("/tmp/ginkgo-%d", config.GinkgoConfig.ParallelNode), []byte("done"), 0777)
}

func registerSignalHandler() {
	c := make(chan os.Signal, 1)

	go func() {
		select {
		case <-c:
			println("cleaning up!")

			cleanup()

			println("goodbye!")
			os.Exit(1)
		}
	}()

	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
}
<|endoftext|>


<commit_before>// +build required

package glfw

// this file exists purely to prevent the golang toolchain from stripping
// away the c source directories and files.
//
// How it works:
//  - every directory which only includes c source files receives a dummy.go file.
//  - every directory we want to preserve is included here as a _ import.
//  - this file is given a build to exclude it from the regular build.
import (
	// Prevent go tooling from stripping out the c source files.
	_ "github.com/go-gl/glfw/v3.3/glfw/glfw/deps"
	_ "github.com/go-gl/glfw/v3.3/glfw/glfw/include/GLFW"
	_ "github.com/go-gl/glfw/v3.3/glfw/glfw/src"
)
<commit_msg>Update v3.3/glfw/build_cgo_hack.go<commit_after>// +build required

package glfw

// This file exists purely to prevent the golang toolchain from stripping
// away the c source directories and files when `go mod vendor` is used
// to populate a `vendor/` directory of a project depending on `go-gl/glfw`.
//
// How it works:
//  - every directory which only includes c source files receives a dummy.go file.
//  - every directory we want to preserve is included here as a _ import.
//  - this file is given a build to exclude it from the regular build.
import (
	// Prevent go tooling from stripping out the c source files.
	_ "github.com/go-gl/glfw/v3.3/glfw/glfw/deps"
	_ "github.com/go-gl/glfw/v3.3/glfw/glfw/include/GLFW"
	_ "github.com/go-gl/glfw/v3.3/glfw/glfw/src"
)
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"reflect"
	"time"

	"os"

	"github.com/google/gapid/test/robot/web/client/widgets/grid"
)

type trackInfo struct {
	track                 Item
	packageDisplayToOrder map[string]int
	packageList           []string
	headPackage           string
}

var (
	traceKind  = item{id: "trace"}
	reportKind = item{id: "report"}
	replayKind = item{id: "replay"}

	packageDisplayTemplate = "{{if .information.tag}}{{.information.tag}}" +
		"{{else if and (isUserType .information.type) (.information.cl)}}{{.information.cl}}" +
		"{{else if  .information.uploader}}{{.information.uploader}} - {{.id}}" +
		"{{else}}unknown - {{.id}}" +
		"{{end}}"

	machineDisplayTemplate = "{{if .information.Name}}{{.information.Name}}" +
		"{{else if .information.Configuration.Hardware.Name}}{{.information.Configuration.Hardware.Name}}" +
		"{{else}}{{.information.Configuration.OS}} - {{.information.id.data}}" +
		"{{end}}"

	kindDimension = &dimension{
		name:     "kind",
		enumData: enum{traceKind, replayKind, reportKind},
		valueOf: func(t *task) Item {
			return t.kind
		},
	}
	subjectDimension = &dimension{
		name: "subject",
		valueOf: func(t *task) Item {
			return t.trace.subject
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", "{{.Information.APK.package}}", template.FuncMap{})(queryArray("/subjects/"))
		},
	}
	targetDimension = &dimension{
		name: "traceTarget",
		valueOf: func(t *task) Item {
			return t.trace.target
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}
	hostDimension = &dimension{
		name: "host",
		valueOf: func(t *task) Item {
			return t.host
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}

	tracks = map[string]*trackInfo{"auto": &trackInfo{
		track: item{
			id:         "",
			display:    "auto",
			underlying: map[string]string{"id": "", "name": "auto", "head": ""},
		},
		packageList: []string{},
		headPackage: "",
	}}
	packageDisplayToOrder = map[string]int{}
	packageToTrack        = map[string]Item{}

	trackDimension = &dimension{
		name: "track",
		valueOf: func(t *task) Item {
			it, ok := packageToTrack[t.pkg.Id()]
			if !ok {
				return tracks["auto"].track
			}
			return it
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", "{{.name}}", template.FuncMap{})(queryArray("/tracks/"))
			for _, it := range result {
				track := it.Underlying().(map[string]interface{})
				trackName, ok := track["name"].(string)
				if !ok {
					continue
				}
				trackHead, ok := track["head"].(string)
				if !ok {
					continue
				}
				tracks[trackName] = &trackInfo{
					track: it,
					packageDisplayToOrder: map[string]int{},
					headPackage:           trackHead,
				}
			}
			result = append(result, tracks["auto"].track)
			return result
		},
		selectAuto: func(c *constraints, d *dimension) {
			if m, found := tracks["master"]; found {
				(*c)[d] = m.track
			} else if len(tracks["auto"].packageList) != 0 {
				(*c)[d] = tracks["auto"].track
			} else {
				delete(*c, d)
			}
		},
	}
	packageDimension = &dimension{
		name: "package",
		valueOf: func(t *task) Item {
			return t.pkg
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", packageDisplayTemplate, template.FuncMap{"isUserType": isUserType})(queryArray("/packages/"))
			itemMap := map[string]Item{}
			childMap := map[string]string{}
			rootPkgs := []string{}
			for _, it := range result {
				pkgRoot := it.Underlying().(map[string]interface{})
				pkgId, ok := pkgRoot["id"].(string)
				itemMap[pkgId] = it
				if !ok {
					continue
				}
				if parentMem, ok := pkgRoot["parent"]; ok {
					parentId, ok := parentMem.(string)
					if !ok {
						continue
					}
					childMap[parentId] = pkgId
				} else {
					rootPkgs = append(rootPkgs, pkgId)
				}
			}
			for _, root := range rootPkgs {
				packageList := []string{}
				packageDisplayToOrder[itemMap[root].Display()] = len(packageDisplayToOrder)
				packageList = append(packageList, root)
				for childId, ok := childMap[root]; ok; childId, ok = childMap[root] {
					packageDisplayToOrder[itemMap[childId].Display()] = len(packageDisplayToOrder)
					// want tracks stored from Root -> Head
					packageList = append(packageList, childId)
					root = childId
				}
				head := root
				foundTrack := false
				// force update the trackDimension to populate tracks
				trackDimension.getEnum()
				for _, destTrack := range tracks {
					if destTrack.headPackage == head {
						destTrack.packageList = packageList
						for _, p := range destTrack.packageList {
							packageToTrack[p] = destTrack.track
						}
						foundTrack = true
						break
					}
				}
				if !foundTrack {
					// We just append all packages to the "auto" track that didn't match an existing track
					tracks["auto"].packageList = append(tracks["auto"].packageList, packageList...)
					tracks["auto"].headPackage = packageList[len(packageList)-1]
				}
			}
			return result
		},
		enumSort: func(a, b string) bool {
			if ao, ok := packageDisplayToOrder[a]; ok {
				if bo, ok := packageDisplayToOrder[b]; ok {
					return ao < bo
				}
			}
			return a < b
		},
		selectAuto: func(c *constraints, d *dimension) {
			if t, found := (*c)[trackDimension]; found {
				(*c)[d] = d.GetItem(tracks[t.Display()].headPackage)
			} else {
				delete(*c, d)
			}
		},
	}

	dimensions = []*dimension{kindDimension, subjectDimension, packageDimension, trackDimension, targetDimension, hostDimension}
)

func isUserType(t reflect.Value) bool {
	// cannot currently use build.Type_UserType to check the type need to fix that.
	return t.Kind() == reflect.Float64 && t.Float() == float64(2)
}

func itemGetter(idPattern string, displayPattern string, functions template.FuncMap) func([]interface{}) enum {
	mustTemplate := func(s string) *template.Template {
		return template.Must(template.New(fmt.Sprintf("t%d", time.Now().Unix())).Funcs(functions).Parse(s))
	}
	exec := func(t *template.Template, item interface{}) string {
		var b bytes.Buffer
		t.Execute(&b, item)
		return b.String()
	}
	idt := mustTemplate(idPattern)
	dispt := mustTemplate(displayPattern)
	return func(entries []interface{}) enum {
		result := enum{}
		for _, it := range entries {
			result = append(result, item{id: exec(idt, it), display: exec(dispt, it), underlying: it})
		}
		return result
	}
}

func queryRestEndpoint(path string) ([]byte, error) {
	resp, err := http.Get(path)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

func queryArray(path string) []interface{} {
	// TODO: Cache this, as we're using the same path for multiple dimensions.
	body, err := queryRestEndpoint(path)
	if err != nil {
		panic(err)
	}

	arr := []interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func queryObject(path string) map[string]interface{} {
	resp, err := http.Get(path)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	arr := map[string]interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func clearDimensionData() {
	for _, d := range dimensions {
		if d.enumSrc != nil {
			d.enumData = nil
		}
		d.itemMap = nil
	}
}

func newTask(entry map[string]interface{}, kind Item) *task {
	t := &task{
		underlying: entry,
		trace:      traceInfo{target: nilItem, subject: nilItem},
		kind:       kind,
		host:       nilItem,
		pkg:        nilItem,
		parent:     nil,
	}

	if st, ok := entry["status"].(float64); ok {
		switch int(st) {
		case 1:
			t.status = grid.InProgress
			t.result = grid.Unknown
		case 2:
			t.status = grid.Current
			t.result = grid.Succeeded
		case 3:
			t.status = grid.Current
			t.result = grid.Failed
		}
	} else {
		t.status = grid.Stale
		t.result = grid.Failed
	}
	return t
}

func compareTasksSimilar(t1 *task, t2 *task) bool {
	// similar tasks can have different packages, but have the same target, subject, and host.
	if t1.trace.target.Id() == t2.trace.target.Id() && t1.trace.subject.Id() == t2.trace.subject.Id() && t1.host.Id() == t2.host.Id() {
		return true
	}
	return false
}

func connectTaskParentChild(childListMap map[string][]*task, parentListMap map[string][]*task, t *task) {
	findParentPkgIdInList := func(idList []string, childId string) string {
		// it is the index of id's parent.
		for it, id := range idList[1:] {
			if childId == id {
				return idList[it]
			}
		}
		return ""
	}
	pkgId := t.pkg.Id()
	parentListMap[pkgId] = append(parentListMap[pkgId], t)

	if parPkgId := findParentPkgIdInList(tracks["auto"].packageList, pkgId); parPkgId != "" {
		childListMap[parPkgId] = append(childListMap[parPkgId], t)

		if parentList, ok := parentListMap[parPkgId]; ok {
			for _, parent := range parentList {
				if compareTasksSimilar(t, parent) {
					t.parent = parent
				}
			}
		}
	}

	if childList, ok := childListMap[pkgId]; ok {
		for _, child := range childList {
			if compareTasksSimilar(t, child) {
				if child.parent != nil {
					fmt.Fprintf(os.Stderr, "A task's parent was found twice? parent package id: %v; child package id: %v", pkgId, child.pkg.Id())
				} else {
					child.parent = t
				}
			}
		}
	}
}

func robotTasksPerKind(kind Item, path string, fun func(map[string]interface{}, *task)) []*task {
	tasks := []*task{}
	notCurrentTasks := []*task{}
	currentTasks := []*task{}
	childMap := map[string][]*task{}
	parentMap := map[string][]*task{}

	for _, e := range queryArray(path) {
		e := e.(map[string]interface{})
		t := newTask(e, kind)
		fun(e, t)
		connectTaskParentChild(childMap, parentMap, t)
		tasks = append(tasks, t)
		if t.status != grid.Current {
			notCurrentTasks = append(notCurrentTasks, t)
		} else {
			currentTasks = append(currentTasks, t)
		}
	}
	for _, t := range notCurrentTasks {
		if t.parent != nil {
			t.result = t.parent.result
		}
	}
	for _, t := range currentTasks {
		if t.parent != nil && t.parent.result != t.result {
			t.status = grid.Changed
		}
	}
	return tasks
}

func getRobotTasks() []*task {
	traceMap := map[string]*task{}
	tasks := []*task{}

	traceProc := func(e map[string]interface{}, t *task) {
		ei := e["input"].(map[string]interface{})
		t.trace = traceInfo{
			target:  targetDimension.GetItem(e["target"]),
			subject: subjectDimension.GetItem(ei["subject"]),
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
		if eo := e["output"]; eo != nil {
			if traceOutput := eo.(map[string]interface{})["trace"]; traceOutput != nil {
				traceMap[traceOutput.(string)] = t
			}
		}
	}
	tasks = append(tasks, robotTasksPerKind(traceKind, "/traces/", traceProc)...)

	subTaskProc := func(e map[string]interface{}, t *task) {
		ei := (e["input"].(map[string]interface{}))
		id := ei["trace"].(string)
		if traceTask, found := traceMap[id]; found {
			t.trace = traceTask.trace
		} else {
			fmt.Fprintf(os.Stderr, "Trace %s not found when processing action\n", id)
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
	}
	tasks = append(tasks, robotTasksPerKind(replayKind, "/replays/", subTaskProc)...)
	tasks = append(tasks, robotTasksPerKind(reportKind, "/reports/", subTaskProc)...)

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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"reflect"
	"time"

	"os"

	"github.com/google/gapid/test/robot/web/client/widgets/grid"
)

type trackInfo struct {
	track                 Item
	packageDisplayToOrder map[string]int
	packageList           []string
	headPackage           string
}

var (
	traceKind  = item{id: "trace"}
	reportKind = item{id: "report"}
	replayKind = item{id: "replay"}

	packageDisplayTemplate = "{{if .information.tag}}{{.information.tag}}" +
		"{{else if and (isUserType .information.type) (.information.cl)}}{{.information.cl}}" +
		"{{else if  .information.uploader}}{{.information.uploader}} - {{.id}}" +
		"{{else}}unknown - {{.id}}" +
		"{{end}}"

	machineDisplayTemplate = "{{if .information.Name}}{{.information.Name}}" +
		"{{else if .information.Configuration.Hardware.Name}}{{.information.Configuration.Hardware.Name}}" +
		"{{else}}{{.information.Configuration.OS}} - {{.information.id.data}}" +
		"{{end}}"

	kindDimension = &dimension{
		name:     "kind",
		enumData: enum{traceKind, replayKind, reportKind},
		valueOf: func(t *task) Item {
			return t.kind
		},
	}
	subjectDimension = &dimension{
		name: "subject",
		valueOf: func(t *task) Item {
			return t.trace.subject
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", "{{.Information.APK.package}}", template.FuncMap{})(queryArray("/subjects/"))
		},
	}
	targetDimension = &dimension{
		name: "traceTarget",
		valueOf: func(t *task) Item {
			return t.trace.target
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}
	hostDimension = &dimension{
		name: "host",
		valueOf: func(t *task) Item {
			return t.host
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}

	tracks = map[string]*trackInfo{"auto": &trackInfo{
		track: item{
			id:         "",
			display:    "auto",
			underlying: map[string]string{"id": "", "name": "auto", "head": ""},
		},
		packageList: []string{},
		headPackage: "",
	}}
	packageDisplayToOrder = map[string]int{}
	packageToTrack        = map[string]*trackInfo{}

	trackDimension = &dimension{
		name: "track",
		valueOf: func(t *task) Item {
			tInfo, ok := packageToTrack[t.pkg.Id()]
			if !ok {
				return tracks["auto"].track
			}
			return tInfo.track
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", "{{.name}}", template.FuncMap{})(queryArray("/tracks/"))
			for _, it := range result {
				track := it.Underlying().(map[string]interface{})
				trackName, ok := track["name"].(string)
				if !ok {
					continue
				}
				trackHead, ok := track["head"].(string)
				if !ok {
					continue
				}
				tracks[trackName] = &trackInfo{
					track: it,
					packageDisplayToOrder: map[string]int{},
					headPackage:           trackHead,
				}
			}
			result = append(result, tracks["auto"].track)
			return result
		},
		selectAuto: func(c *constraints, d *dimension) {
			if m, found := tracks["master"]; found {
				(*c)[d] = m.track
			} else if len(tracks["auto"].packageList) != 0 {
				(*c)[d] = tracks["auto"].track
			} else {
				delete(*c, d)
			}
		},
	}
	packageDimension = &dimension{
		name: "package",
		valueOf: func(t *task) Item {
			return t.pkg
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", packageDisplayTemplate, template.FuncMap{"isUserType": isUserType})(queryArray("/packages/"))
			itemMap := map[string]Item{}
			childMap := map[string]string{}
			rootPkgs := []string{}
			for _, it := range result {
				pkgRoot := it.Underlying().(map[string]interface{})
				pkgId, ok := pkgRoot["id"].(string)
				itemMap[pkgId] = it
				if !ok {
					continue
				}
				if parentMem, ok := pkgRoot["parent"]; ok {
					parentId, ok := parentMem.(string)
					if !ok {
						continue
					}
					childMap[parentId] = pkgId
				} else {
					rootPkgs = append(rootPkgs, pkgId)
				}
			}
			for _, root := range rootPkgs {
				packageList := []string{}
				packageDisplayToOrder[itemMap[root].Display()] = len(packageDisplayToOrder)
				packageList = append(packageList, root)
				for childId, ok := childMap[root]; ok; childId, ok = childMap[root] {
					packageDisplayToOrder[itemMap[childId].Display()] = len(packageDisplayToOrder)
					// want tracks stored from Root -> Head
					packageList = append(packageList, childId)
					root = childId
				}
				head := root
				foundTrack := false
				// force update the trackDimension to populate tracks
				trackDimension.getEnum()
				for _, destTrack := range tracks {
					if destTrack.headPackage == head {
						destTrack.packageList = packageList
						for _, p := range destTrack.packageList {
							packageToTrack[p] = destTrack
						}
						foundTrack = true
						break
					}
				}
				if !foundTrack {
					// We just append all packages to the "auto" track that didn't match an existing track
					tracks["auto"].packageList = append(tracks["auto"].packageList, packageList...)
					tracks["auto"].headPackage = packageList[len(packageList)-1]
				}
			}
			return result
		},
		enumSort: func(a, b string) bool {
			if ao, ok := packageDisplayToOrder[a]; ok {
				if bo, ok := packageDisplayToOrder[b]; ok {
					return ao < bo
				}
			}
			return a < b
		},
		selectAuto: func(c *constraints, d *dimension) {
			if t, found := (*c)[trackDimension]; found {
				(*c)[d] = d.GetItem(tracks[t.Display()].headPackage)
			} else {
				delete(*c, d)
			}
		},
	}

	dimensions = []*dimension{kindDimension, subjectDimension, packageDimension, trackDimension, targetDimension, hostDimension}
)

func isUserType(t reflect.Value) bool {
	// cannot currently use build.Type_UserType to check the type need to fix that.
	return t.Kind() == reflect.Float64 && t.Float() == float64(2)
}

func itemGetter(idPattern string, displayPattern string, functions template.FuncMap) func([]interface{}) enum {
	mustTemplate := func(s string) *template.Template {
		return template.Must(template.New(fmt.Sprintf("t%d", time.Now().Unix())).Funcs(functions).Parse(s))
	}
	exec := func(t *template.Template, item interface{}) string {
		var b bytes.Buffer
		t.Execute(&b, item)
		return b.String()
	}
	idt := mustTemplate(idPattern)
	dispt := mustTemplate(displayPattern)
	return func(entries []interface{}) enum {
		result := enum{}
		for _, it := range entries {
			result = append(result, item{id: exec(idt, it), display: exec(dispt, it), underlying: it})
		}
		return result
	}
}

func queryRestEndpoint(path string) ([]byte, error) {
	resp, err := http.Get(path)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

func queryArray(path string) []interface{} {
	// TODO: Cache this, as we're using the same path for multiple dimensions.
	body, err := queryRestEndpoint(path)
	if err != nil {
		panic(err)
	}

	arr := []interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func queryObject(path string) map[string]interface{} {
	resp, err := http.Get(path)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	arr := map[string]interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func clearDimensionData() {
	for _, d := range dimensions {
		if d.enumSrc != nil {
			d.enumData = nil
		}
		d.itemMap = nil
	}
}

func newTask(entry map[string]interface{}, kind Item) *task {
	t := &task{
		underlying: entry,
		trace:      traceInfo{target: nilItem, subject: nilItem},
		kind:       kind,
		host:       nilItem,
		pkg:        nilItem,
		parent:     nil,
	}

	if st, ok := entry["status"].(float64); ok {
		switch int(st) {
		case 1:
			t.status = grid.InProgress
			t.result = grid.Unknown
		case 2:
			t.status = grid.Current
			t.result = grid.Succeeded
		case 3:
			t.status = grid.Current
			t.result = grid.Failed
		}
	} else {
		t.status = grid.Stale
		t.result = grid.Failed
	}
	return t
}

func compareTasksSimilar(t1 *task, t2 *task) bool {
	// similar tasks can have different packages, but have the same target, subject, and host.
	if t1.trace.target.Id() == t2.trace.target.Id() && t1.trace.subject.Id() == t2.trace.subject.Id() && t1.host.Id() == t2.host.Id() {
		return true
	}
	return false
}

func connectTaskParentChild(childListMap map[string][]*task, parentListMap map[string][]*task, t *task) {
	findParentPkgIdInList := func(idList []string, childId string) string {
		// it is the index of id's parent.
		for it, id := range idList[1:] {
			if childId == id {
				return idList[it]
			}
		}
		return ""
	}
	pkgId := t.pkg.Id()
	parentListMap[pkgId] = append(parentListMap[pkgId], t)

	if parPkgId := findParentPkgIdInList(packageToTrack[pkgId].packageList, pkgId); parPkgId != "" {
		childListMap[parPkgId] = append(childListMap[parPkgId], t)

		if parentList, ok := parentListMap[parPkgId]; ok {
			for _, parent := range parentList {
				if compareTasksSimilar(t, parent) {
					t.parent = parent
				}
			}
		}
	}

	if childList, ok := childListMap[pkgId]; ok {
		for _, child := range childList {
			if compareTasksSimilar(t, child) {
				if child.parent != nil {
					fmt.Fprintf(os.Stderr, "A task's parent was found twice? parent package id: %v; child package id: %v", pkgId, child.pkg.Id())
				} else {
					child.parent = t
				}
			}
		}
	}
}

func robotTasksPerKind(kind Item, path string, fun func(map[string]interface{}, *task)) []*task {
	tasks := []*task{}
	notCurrentTasks := []*task{}
	currentTasks := []*task{}
	childMap := map[string][]*task{}
	parentMap := map[string][]*task{}

	for _, e := range queryArray(path) {
		e := e.(map[string]interface{})
		t := newTask(e, kind)
		fun(e, t)
		connectTaskParentChild(childMap, parentMap, t)
		tasks = append(tasks, t)
		if t.status != grid.Current {
			notCurrentTasks = append(notCurrentTasks, t)
		} else {
			currentTasks = append(currentTasks, t)
		}
	}
	for _, t := range notCurrentTasks {
		if t.parent != nil {
			t.result = t.parent.result
		}
	}
	for _, t := range currentTasks {
		if t.parent != nil && t.parent.result != t.result {
			t.status = grid.Changed
		}
	}
	return tasks
}

func getRobotTasks() []*task {
	traceMap := map[string]*task{}
	tasks := []*task{}

	traceProc := func(e map[string]interface{}, t *task) {
		ei := e["input"].(map[string]interface{})
		t.trace = traceInfo{
			target:  targetDimension.GetItem(e["target"]),
			subject: subjectDimension.GetItem(ei["subject"]),
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
		if eo := e["output"]; eo != nil {
			if traceOutput := eo.(map[string]interface{})["trace"]; traceOutput != nil {
				traceMap[traceOutput.(string)] = t
			}
		}
	}
	tasks = append(tasks, robotTasksPerKind(traceKind, "/traces/", traceProc)...)

	subTaskProc := func(e map[string]interface{}, t *task) {
		ei := (e["input"].(map[string]interface{}))
		id := ei["trace"].(string)
		if traceTask, found := traceMap[id]; found {
			t.trace = traceTask.trace
		} else {
			fmt.Fprintf(os.Stderr, "Trace %s not found when processing action\n", id)
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
	}
	tasks = append(tasks, robotTasksPerKind(replayKind, "/replays/", subTaskProc)...)
	tasks = append(tasks, robotTasksPerKind(reportKind, "/reports/", subTaskProc)...)

	return tasks
}
<|endoftext|>


<commit_before>package schedule

import (
	"log"
	"sync"
	"time"
)

// Maintain a wait group of all unfinished jobs on the queue
type Queue struct {
	unfinished sync.WaitGroup
	logger     Logger
}

// TODO Replace job with an interface?
// TODO Create a common Task struct that can handle these cases?
func (q *Queue) Now(job func() error) {
	// Do the job immediately
	q.unfinished.Add(1)
	go func() {
		// Create a status and record the start time
		// TODO How to record the original job?
		s := Status{
			Start: time.Now(),
		}

		// TODO Should tasks all implement a Job interface
		// In that case, this operation must return an error
		s.Error = job()
		s.End = time.Now()
		if s.Error == nil {
			s.OK = true
		}

		// Send the status to the logger
		q.logger.Log(s)

		// Decrement the job counter
		q.unfinished.Done()
	}()
}

func (q *Queue) Repeat(job func() error, freq time.Duration) {
	q.unfinished.Add(1)
	// TODO Create a new go func for each iteration of the job
	// if the job needs to be STARTED every duration
	go func() {
		for {
			s := Status{
				Start: time.Now(),
			}
			// TODO Should tasks all implement a Job interface
			// In that case, this operation must return an error
			s.Error = job()
			s.End = time.Now()
			if s.Error == nil {
				s.OK = true
			}
			// Send the status to the logger
			q.logger.Log(s)
			time.Sleep(freq)
		}
		q.unfinished.Done()
	}()
}

func (q *Queue) RepeatN(job func() error, freq time.Duration, n int) {
	q.unfinished.Add(1)
	// TODO Create a new go func for each iteration of the job
	// if the job needs to be STARTED every duration
	go func() {
		for i := 0; i < n; i++ {
			s := Status{
				Start: time.Now(),
			}
			// TODO Should tasks all implement a Job interface
			// In that case, this operation must return an error
			s.Error = job()
			s.End = time.Now()
			if s.Error == nil {
				s.OK = true
			}
			// Send the status to the logger
			q.logger.Log(s)
			time.Sleep(freq)
		}
		q.unfinished.Done()
	}()
}

// Perform the task daily at the given time of day
// TODO The clock needs a location
// TODO Assumes UTC for now
func (q *Queue) Daily(job func() error, clock Clock) {
	q.unfinished.Add(1)
	// TODO New goroutine for each iteration?
	go func() {
		for {
			now := time.Now().UTC()
			next := time.Date(now.Year(), now.Month(), now.Day(), clock.hour, clock.min, clock.sec, 0, time.UTC)
			log.Println(now)
			log.Println(next)

			if next.Before(now) {
				// Next has already occured, add a day
				next = next.Add(24 * time.Hour)
			}
			log.Println(next)

			// Wait for this next time to occur
			<-TickAt(next)
			s := Status{
				Start: time.Now(),
			}
			// TODO Should tasks all implement a Job interface
			// In that case, this operation must return an error
			s.Error = job()
			s.End = time.Now()
			if s.Error == nil {
				s.OK = true
			}
			// Send the status to the logger
			q.logger.Log(s)
		}
		q.unfinished.Done()
	}()
}

// TODO When would this ever return an error?
func (q *Queue) WaitForJobsToFinish() error {
	q.unfinished.Wait()
	return nil
}

// Create a queue with a default logger
func New() *Queue {
	return &Queue{
		logger: &DefaultLogger{},
	}
}

var std = New()

// Aliases for operations on the std queue
func Now(job func() error) {
	std.Now(job)
}

func Repeat(job func() error, freq time.Duration) {
	std.Repeat(job, freq)
}

func RepeatN(job func() error, freq time.Duration, n int) {
	std.RepeatN(job, freq, n)
}

func Daily(job func() error, clock Clock) {
	std.Daily(job, clock)
}

func WaitForJobsToFinish() error {
	return std.WaitForJobsToFinish()
}
<commit_msg>Removed extraneous log calls in schedule<commit_after>package schedule

import (
	"sync"
	"time"
)

// Maintain a wait group of all unfinished jobs on the queue
type Queue struct {
	unfinished sync.WaitGroup
	logger     Logger
}

// TODO Replace job with an interface?
// TODO Create a common Task struct that can handle these cases?
func (q *Queue) Now(job func() error) {
	// Do the job immediately
	q.unfinished.Add(1)
	go func() {
		// Create a status and record the start time
		// TODO How to record the original job?
		s := Status{
			Start: time.Now(),
		}

		// TODO Should tasks all implement a Job interface
		// In that case, this operation must return an error
		s.Error = job()
		s.End = time.Now()
		if s.Error == nil {
			s.OK = true
		}

		// Send the status to the logger
		q.logger.Log(s)

		// Decrement the job counter
		q.unfinished.Done()
	}()
}

func (q *Queue) Repeat(job func() error, freq time.Duration) {
	q.unfinished.Add(1)
	// TODO Create a new go func for each iteration of the job
	// if the job needs to be STARTED every duration
	go func() {
		for {
			s := Status{
				Start: time.Now(),
			}
			// TODO Should tasks all implement a Job interface
			// In that case, this operation must return an error
			s.Error = job()
			s.End = time.Now()
			if s.Error == nil {
				s.OK = true
			}
			// Send the status to the logger
			q.logger.Log(s)
			time.Sleep(freq)
		}
		q.unfinished.Done()
	}()
}

func (q *Queue) RepeatN(job func() error, freq time.Duration, n int) {
	q.unfinished.Add(1)
	// TODO Create a new go func for each iteration of the job
	// if the job needs to be STARTED every duration
	go func() {
		for i := 0; i < n; i++ {
			s := Status{
				Start: time.Now(),
			}
			// TODO Should tasks all implement a Job interface
			// In that case, this operation must return an error
			s.Error = job()
			s.End = time.Now()
			if s.Error == nil {
				s.OK = true
			}
			// Send the status to the logger
			q.logger.Log(s)
			time.Sleep(freq)
		}
		q.unfinished.Done()
	}()
}

// Perform the task daily at the given time of day
// TODO The clock needs a location
// TODO Assumes UTC for now
func (q *Queue) Daily(job func() error, clock Clock) {
	q.unfinished.Add(1)
	// TODO New goroutine for each iteration?
	go func() {
		for {
			now := time.Now().UTC()
			next := time.Date(now.Year(), now.Month(), now.Day(), clock.hour, clock.min, clock.sec, 0, time.UTC)
			if next.Before(now) {
				// Next has already occured, add a day
				next = next.Add(24 * time.Hour)
			}

			// Wait for this next time to occur
			<-TickAt(next)
			s := Status{
				Start: time.Now(),
			}
			// TODO Should tasks all implement a Job interface
			// In that case, this operation must return an error
			s.Error = job()
			s.End = time.Now()
			if s.Error == nil {
				s.OK = true
			}
			// Send the status to the logger
			q.logger.Log(s)
		}
		q.unfinished.Done()
	}()
}

// TODO When would this ever return an error?
func (q *Queue) WaitForJobsToFinish() error {
	q.unfinished.Wait()
	return nil
}

// Create a queue with a default logger
func New() *Queue {
	return &Queue{
		logger: &DefaultLogger{},
	}
}

var std = New()

// Aliases for operations on the std queue
func Now(job func() error) {
	std.Now(job)
}

func Repeat(job func() error, freq time.Duration) {
	std.Repeat(job, freq)
}

func RepeatN(job func() error, freq time.Duration, n int) {
	std.RepeatN(job, freq, n)
}

func Daily(job func() error, clock Clock) {
	std.Daily(job, clock)
}

func WaitForJobsToFinish() error {
	return std.WaitForJobsToFinish()
}
<|endoftext|>