<commit_before>// HTTP server that uses OAuth to create security.PrivateID objects.
package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"

	"veyron/services/identity/googleoauth"
	"veyron/services/identity/handlers"
	"veyron/services/identity/util"
	"veyron2/rt"
	"veyron2/security"
	"veyron2/vlog"
)

var (
	port          = flag.Int("port", 8125, "Port number on which the HTTP server listens on.")
	host          = flag.String("host", defaultHost(), "Hostname the HTTP server listens on. This can be the name of the host running the webserver, but if running behind a NAT or load balancer, this should be the host name that clients will connect to. For example, if set to 'x.com', Veyron identities will have the IssuerName set to 'x.com' and clients can expect to find the public key of the signer at 'x.com/pubkey/'.")
	tlsconfig     = flag.String("tlsconfig", "", "Comma-separated list of TLS certificate and private key files. If empty, will not use HTTPS.")
	minExpiryDays = flag.Int("min_expiry_days", 365, "Minimum expiry time (in days) of identities issued by this server")
	googleConfig  = flag.String("google_config", "", "Path to the JSON-encoded file containing the ClientID for web applications registered with the Google Developer Console. (Use the 'Download JSON' link on the Google APIs console).")
	googleDomain  = flag.String("google_domain", "", "An optional domain name. When set, only email addresses from this domain are allowed to authenticate via Google OAuth")

	generate = flag.String("generate", "", "If non-empty, instead of running an HTTP server, a new identity will be created with the provided name and saved to --identity (if specified) and dumped to STDOUT in base64-encoded-vom")
	identity = flag.String("identity", "", "Path to the file where the VOM-encoded security.PrivateID created with --generate will be written.")
)

func main() {
	// Setup flags and logging
	flag.Usage = usage
	r := rt.Init()
	defer r.Shutdown()

	if len(*generate) > 0 {
		generateAndSaveIdentity()
		return
	}

	// Setup handlers
	http.HandleFunc("/", handleMain)
	http.Handle("/pubkey/", handlers.Object{r.Identity().PublicID().PublicKey()}) // public key of this identity server
	if enableRandomHandler() {
		http.Handle("/random/", handlers.Random{r}) // mint identities with a random name
	}
	http.HandleFunc("/bless/", handlers.Bless) // use a provided PrivateID to bless a provided PublicID
	// Google OAuth
	if enableGoogleOAuth() {
		f, err := os.Open(*googleConfig)
		if err != nil {
			vlog.Fatalf("Failed to open %q: %v", *googleConfig, err)
		}
		clientid, secret, err := googleoauth.ClientIDAndSecretFromJSON(f)
		if err != nil {
			vlog.Fatalf("Failed to decode %q: %v", *googleConfig, err)
		}
		f.Close()
		n := "/google/"
		http.Handle(n, googleoauth.NewHandler(googleoauth.HandlerArgs{
			UseTLS:              enableTLS(),
			Addr:                fmt.Sprintf("%s:%d", *host, *port),
			Prefix:              n,
			ClientID:            clientid,
			ClientSecret:        secret,
			MinExpiryDays:       *minExpiryDays,
			Runtime:             r,
			RestrictEmailDomain: *googleDomain,
		}))
	}
	startHTTPServer(*port)
}

func enableTLS() bool           { return len(*tlsconfig) > 0 }
func enableGoogleOAuth() bool   { return len(*googleConfig) > 0 }
func enableRandomHandler() bool { return !enableGoogleOAuth() }

func startHTTPServer(port int) {
	addr := fmt.Sprintf(":%d", port)
	if !enableTLS() {
		vlog.Infof("Starting HTTP server (without TLS) at http://%v", addr)
		if err := http.ListenAndServe(addr, nil); err != nil {
			vlog.Fatalf("http.ListenAndServe failed: %v", err)
		}
		return
	}
	paths := strings.Split(*tlsconfig, ",")
	if len(paths) != 2 {
		vlog.Fatalf("Could not parse --tlsconfig. Must have exactly two components, separated by a comma")
	}
	vlog.Infof("Starting HTTP server with TLS using certificate [%s] and private key [%s] at https://%s", paths[0], paths[1], addr)
	if err := http.ListenAndServeTLS(addr, paths[0], paths[1], nil); err != nil {
		vlog.Fatalf("http.ListenAndServeTLS failed: %v", err)
	}
}

func usage() {
	fmt.Fprintf(os.Stderr, `%s starts an HTTP server that mints veyron identities in response to GET requests.

To generate TLS certificates so the HTTP server can use SSL:
go run $GOROOT/src/pkg/crypto/tls/generate_cert.go --host <IP address>

To enable use of Google APIs to use Google OAuth for authorization, set --google_config,
which must point to the contents of a JSON file obtained after registering your application
with the Google Developer Console at:
https://code.google.com/apis/console
More details on Google OAuth at:
https://developers.google.com/accounts/docs/OAuth2Login

Flags:
`, os.Args[0])
	flag.PrintDefaults()
}

func defaultHost() string {
	host, err := os.Hostname()
	if err != nil {
		vlog.Fatalf("Failed to get hostname: %v", err)
	}
	return host
}

func handleMain(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`
<!doctype html>

<meta charset="UTF-8">
<title>Veyron Identity Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">

Veyron Identity Generation

This HTTP server mints veyron identities. The public key of the identity of this server is available in
base64-encoded-vom-encoded format.

`))
	if enableGoogleOAuth() {
		w.Write([]byte(`<a class="btn btn-lg btn-primary" href="/google/auth">Google</a> `))
	}
	if enableRandomHandler() {
		w.Write([]byte(`<a class="btn btn-lg btn-primary" href="/random/">Random</a> `))
	}
	w.Write([]byte(`Bless As

`))
}

func generateAndSaveIdentity() {
	id, err := rt.R().NewIdentity(*generate)
	if err != nil {
		vlog.Fatalf("Runtime.NewIdentity(%q) failed: %v", *generate, err)
	}
	if len(*identity) > 0 {
		if err = saveIdentity(*identity, id); err != nil {
			vlog.Fatalf("SaveIdentity %v: %v", *identity, err)
		}
	}
	b64, err := util.Base64VomEncode(id)
	if err != nil {
		vlog.Fatalf("Base64VomEncode(%q) failed: %v", id, err)
	}
	fmt.Println(b64)
}

func saveIdentity(filePath string, id security.PrivateID) error {
	f, err := os.OpenFile(filePath, os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := security.SaveIdentity(f, id); err != nil {
		return err
	}
	return nil
}
<commit_msg>veyron/services/identity/identityd: Bind to localhost by default.<commit_after>// HTTP server that uses OAuth to create security.PrivateID objects.
package main

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"strings"

	"veyron/services/identity/googleoauth"
	"veyron/services/identity/handlers"
	"veyron/services/identity/util"
	"veyron2/rt"
	"veyron2/security"
	"veyron2/vlog"
)

var (
	address       = flag.String("address", "localhost:8125", "Address on which the HTTP server listens on.")
	host          = flag.String("host", defaultHost(), "Hostname the HTTP server listens on. This can be the name of the host running the webserver, but if running behind a NAT or load balancer, this should be the host name that clients will connect to. For example, if set to 'x.com', Veyron identities will have the IssuerName set to 'x.com' and clients can expect to find the public key of the signer at 'x.com/pubkey/'.")
	tlsconfig     = flag.String("tlsconfig", "", "Comma-separated list of TLS certificate and private key files. If empty, will not use HTTPS.")
	minExpiryDays = flag.Int("min_expiry_days", 365, "Minimum expiry time (in days) of identities issued by this server")
	googleConfig  = flag.String("google_config", "", "Path to the JSON-encoded file containing the ClientID for web applications registered with the Google Developer Console. (Use the 'Download JSON' link on the Google APIs console).")
	googleDomain  = flag.String("google_domain", "", "An optional domain name. When set, only email addresses from this domain are allowed to authenticate via Google OAuth")

	generate = flag.String("generate", "", "If non-empty, instead of running an HTTP server, a new identity will be created with the provided name and saved to --identity (if specified) and dumped to STDOUT in base64-encoded-vom")
	identity = flag.String("identity", "", "Path to the file where the VOM-encoded security.PrivateID created with --generate will be written.")
)

func main() {
	// Setup flags and logging
	flag.Usage = usage
	r := rt.Init()
	defer r.Shutdown()

	if len(*generate) > 0 {
		generateAndSaveIdentity()
		return
	}

	// Setup handlers
	http.HandleFunc("/", handleMain)
	http.Handle("/pubkey/", handlers.Object{r.Identity().PublicID().PublicKey()}) // public key of this identity server
	if enableRandomHandler() {
		http.Handle("/random/", handlers.Random{r}) // mint identities with a random name
	}
	http.HandleFunc("/bless/", handlers.Bless) // use a provided PrivateID to bless a provided PublicID
	// Google OAuth
	if enableGoogleOAuth() {
		_, port, err := net.SplitHostPort(*address)
		if err != nil {
			vlog.Fatalf("Failed to parse %q: %v", *address, err)
		}
		f, err := os.Open(*googleConfig)
		if err != nil {
			vlog.Fatalf("Failed to open %q: %v", *googleConfig, err)
		}
		clientid, secret, err := googleoauth.ClientIDAndSecretFromJSON(f)
		if err != nil {
			vlog.Fatalf("Failed to decode %q: %v", *googleConfig, err)
		}
		f.Close()
		n := "/google/"
		http.Handle(n, googleoauth.NewHandler(googleoauth.HandlerArgs{
			UseTLS:              enableTLS(),
			Addr:                fmt.Sprintf("%s:%s", *host, port),
			Prefix:              n,
			ClientID:            clientid,
			ClientSecret:        secret,
			MinExpiryDays:       *minExpiryDays,
			Runtime:             r,
			RestrictEmailDomain: *googleDomain,
		}))
	}
	startHTTPServer(*address)
}

func enableTLS() bool           { return len(*tlsconfig) > 0 }
func enableGoogleOAuth() bool   { return len(*googleConfig) > 0 }
func enableRandomHandler() bool { return !enableGoogleOAuth() }

func startHTTPServer(addr string) {
	if !enableTLS() {
		vlog.Infof("Starting HTTP server (without TLS) at http://%v", addr)
		if err := http.ListenAndServe(addr, nil); err != nil {
			vlog.Fatalf("http.ListenAndServe failed: %v", err)
		}
		return
	}
	paths := strings.Split(*tlsconfig, ",")
	if len(paths) != 2 {
		vlog.Fatalf("Could not parse --tlsconfig. Must have exactly two components, separated by a comma")
	}
	vlog.Infof("Starting HTTP server with TLS using certificate [%s] and private key [%s] at https://%s", paths[0], paths[1], addr)
	if err := http.ListenAndServeTLS(addr, paths[0], paths[1], nil); err != nil {
		vlog.Fatalf("http.ListenAndServeTLS failed: %v", err)
	}
}

func usage() {
	fmt.Fprintf(os.Stderr, `%s starts an HTTP server that mints veyron identities in response to GET requests.

To generate TLS certificates so the HTTP server can use SSL:
go run $GOROOT/src/pkg/crypto/tls/generate_cert.go --host <IP address>

To enable use of Google APIs to use Google OAuth for authorization, set --google_config,
which must point to the contents of a JSON file obtained after registering your application
with the Google Developer Console at:
https://code.google.com/apis/console
More details on Google OAuth at:
https://developers.google.com/accounts/docs/OAuth2Login

Flags:
`, os.Args[0])
	flag.PrintDefaults()
}

func defaultHost() string {
	host, err := os.Hostname()
	if err != nil {
		vlog.Fatalf("Failed to get hostname: %v", err)
	}
	return host
}

func handleMain(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`
<!doctype html>

<meta charset="UTF-8">
<title>Veyron Identity Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">

Veyron Identity Generation

This HTTP server mints veyron identities. The public key of the identity of this server is available in
base64-encoded-vom-encoded format.

`))
	if enableGoogleOAuth() {
		w.Write([]byte(`<a class="btn btn-lg btn-primary" href="/google/auth">Google</a> `))
	}
	if enableRandomHandler() {
		w.Write([]byte(`<a class="btn btn-lg btn-primary" href="/random/">Random</a> `))
	}
	w.Write([]byte(`Bless As

`))
}

func generateAndSaveIdentity() {
	id, err := rt.R().NewIdentity(*generate)
	if err != nil {
		vlog.Fatalf("Runtime.NewIdentity(%q) failed: %v", *generate, err)
	}
	if len(*identity) > 0 {
		if err = saveIdentity(*identity, id); err != nil {
			vlog.Fatalf("SaveIdentity %v: %v", *identity, err)
		}
	}
	b64, err := util.Base64VomEncode(id)
	if err != nil {
		vlog.Fatalf("Base64VomEncode(%q) failed: %v", id, err)
	}
	fmt.Println(b64)
}

func saveIdentity(filePath string, id security.PrivateID) error {
	f, err := os.OpenFile(filePath, os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := security.SaveIdentity(f, id); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 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 bar

import (
	"bufio"
	"encoding/json"
	"io"
	"os"
	"os/signal"
	"strconv"
	"strings"

	"golang.org/x/sys/unix"
)

// i3Output is sent to i3bar.
type i3Output []map[string]interface{}

// i3Event instances are received from i3bar on stdin.
type i3Event struct {
	Event
	Name string `json:"name"`
}

// i3Header is sent at the beginning of output.
type i3Header struct {
	Version     int  `json:"version"`
	StopSignal  int  `json:"stop_signal,omitempty"`
	ContSignal  int  `json:"cont_signal,omitempty"`
	ClickEvents bool `json:"click_events"`
}

// i3Module wraps Module with extra information to help run i3bar.
type i3Module struct {
	Module
	Name       string
	LastOutput i3Output
	// Keep track of the paused/resumed state of the module.
	// Using a channel here allows concurrent pause/resume across
	// modules while guaranteeing ordering.
	paused   chan bool
	pausable Pausable
}

// output converts the module's output to i3Output by adding the name (position),
// sets the module's last output to the converted i3Output, and signals the bar
// to update its output.
func (m *i3Module) output(ch chan<- interface{}) {
	for o := range m.Stream() {
		var i3out i3Output
		for _, segment := range o.Segments() {
			segmentOut := segment.i3map()
			segmentOut["name"] = m.Name
			i3out = append(i3out, segmentOut)
		}
		m.LastOutput = i3out
		ch <- nil
	}
}

// loopPauseResume loops over values on the resumed channel and calls
// pause or resume on the wrapped module as appropriate.
func (m *i3Module) loopPauseResume() {
	for paused := range m.paused {
		if paused {
			m.pausable.Pause()
		} else {
			m.pausable.Resume()
		}
	}
}

// pause enqueues a pause call on the wrapped module
// if the wrapped module supports being paused.
func (m *i3Module) pause() {
	if m.paused != nil {
		m.paused <- true
	}
}

// resume enqueues a resume call on the wrapped module
// if the wrapped module supports being paused.
func (m *i3Module) resume() {
	if m.paused != nil {
		m.paused <- false
	}
}

// I3Bar is a "bar" instance that handles events and streams output.
type I3Bar struct {
	// The list of modules that make up this bar.
	i3Modules []*i3Module
	// The channel that receives a signal on module updates.
	update chan interface{}
	// The channel that aggregates all events from i3.
	events chan i3Event
	// The Reader to read events from (e.g. stdin)
	reader io.Reader
	// The Writer to write bar output to (e.g. stdout)
	writer io.Writer
	// A json encoder set to write to the output stream.
	encoder *json.Encoder
	// Flipped when Run() is called, to prevent issues with modules
	// being added after the bar has been started.
	started bool
	// Suppress pause/resume signal handling to workaround potential
	// weirdness with signals.
	suppressSignals bool
}

// Add adds a module to a bar, and returns the bar for chaining.
func (b *I3Bar) Add(modules ...Module) *I3Bar {
	for _, m := range modules {
		b.addModule(m)
	}
	// Return the bar for chaining (e.g. bar.Add(x, y).Run())
	return b
}

// addModule adds a single module to the bar.
func (b *I3Bar) addModule(module Module) {
	// Panic if adding modules to an already running bar.
	// TODO: Support this in the future.
	if b.started {
		panic("Cannot add modules after .Run()")
	}
	// Use the position of the module in the list as the "name", so when i3bar
	// sends us events, we can use atoi(name) to get the correct module.
	name := strconv.Itoa(len(b.i3Modules))
	i3Module := i3Module{
		Module: module,
		Name:   name,
	}
	if pauseable, ok := module.(Pausable); ok {
		i3Module.paused = make(chan bool, 10)
		i3Module.pausable = pauseable
		go i3Module.loopPauseResume()
	}
	b.i3Modules = append(b.i3Modules, &i3Module)
}

// SuppressSignals instructs the bar to skip the pause/resume signal handling.
// Must be called before Run.
func (b *I3Bar) SuppressSignals(suppressSignals bool) *I3Bar {
	if b.started {
		panic("Cannot change signal handling after .Run()")
	}
	b.suppressSignals = suppressSignals
	return b
}

// Run sets up all the streams and enters the main loop.
func (b *I3Bar) Run() error {
	var signalChan chan os.Signal
	if !b.suppressSignals {
		// Set up signal handlers for USR1/2 to pause/resume supported modules.
		signalChan = make(chan os.Signal, 2)
		signal.Notify(signalChan, unix.SIGUSR1, unix.SIGUSR2)
	}

	// Mark the bar as started.
	b.started = true

	// Read events from the input stream, pipe them to the events channel.
	go b.readEvents()
	for _, m := range b.i3Modules {
		go m.output(b.update)
	}

	// Write header.
	header := i3Header{
		Version:     1,
		ClickEvents: true,
	}

	if !b.suppressSignals {
		// Go doesn't allow us to handle the default SIGSTOP,
		// so we'll use SIGUSR1 and SIGUSR2 for pause/resume.
		header.StopSignal = int(unix.SIGUSR1)
		header.ContSignal = int(unix.SIGUSR2)
	}
	// Set up the encoder for the output stream,
	// so that module outputs can be written directly.
	b.encoder = json.NewEncoder(b.writer)
	if err := b.encoder.Encode(&header); err != nil {
		return err
	}
	// Start the infinite array.
	if _, err := io.WriteString(b.writer, "["); err != nil {
		return err
	}

	// Infinite arrays on both sides.
	for {
		select {
		case _ = <-b.update:
			// The complete bar needs to printed on each update.
			if err := b.print(); err != nil {
				return err
			}
		case event := <-b.events:
			// Events are stripped of the name before being dispatched to the
			// correct module.
			if module, ok := b.get(event.Name); ok {
				// Check that the module actually supports click events.
				if clickable, ok := module.Module.(Clickable); ok {
					// Goroutine to prevent click handlers from blocking the bar.
					go clickable.Click(event.Event)
				}
			}
		case sig := <-signalChan:
			switch sig {
			case unix.SIGUSR1:
				b.pause()
			case unix.SIGUSR2:
				b.resume()
			}
		}
	}
}

// Run with a list of modules just adds each module and runs the bar on stdout/stdin.
func Run(modules ...Module) error {
	return New().Add(modules...).Run()
}

// RunOnIo takes a list of modules and the input/output streams, and runs the bar.
func RunOnIo(reader io.Reader, writer io.Writer, modules ...Module) error {
	return NewOnIo(reader, writer).Add(modules...).Run()
}

// New constructs a new bar running on standard I/O.
func New() *I3Bar {
	return NewOnIo(os.Stdin, os.Stdout)
}

// NewOnIo constructs a new bar with an input and output stream, for maximum flexibility.
func NewOnIo(reader io.Reader, writer io.Writer) *I3Bar {
	return &I3Bar{
		update: make(chan interface{}),
		events: make(chan i3Event),
		reader: reader,
		writer: writer,
	}
}

// print outputs the entire bar, using the last output for each module.
func (b *I3Bar) print() error {
	// i3bar requires the entire bar to be printed at once, so we just take the
	// last cached value for each module and construct the current bar.
	// The bar will update any modules before calling this method, so the
	// LastOutput property of each module will represent the current state.
	var outputs []map[string]interface{}
	for _, m := range b.i3Modules {
		for _, segment := range m.LastOutput {
			outputs = append(outputs, segment)
		}
	}
	if err := b.encoder.Encode(outputs); err != nil {
		return err
	}
	_, err := io.WriteString(b.writer, ",\n")
	return err
}

// get finds the module that corresponds to the given "name" from i3.
func (b *I3Bar) get(name string) (*i3Module, bool) {
	index, err := strconv.Atoi(name)
	if err != nil {
		return nil, false
	}
	if index < 0 || len(b.i3Modules) <= index {
		return nil, false
	}
	return b.i3Modules[index], true
}

// readEvents parses the infinite stream of events received from i3.
func (b *I3Bar) readEvents() {
	// Buffered I/O to allow complete events to be read in at once.
	reader := bufio.NewReader(b.reader)
	// Consume opening '['
	if rune, _, err := reader.ReadRune(); err != nil || rune != '[' {
		return
	}
	for {
		// While the 'proper' way to implement this infinite parser would be to keep
		// a state machine and hook into json parsing and stuff, we'll take a
		// shortcut since we know there are no nested objects. So all we have to do
		// is read until the first '}', decode it, consume the ',', and repeat.
		eventJSON, err := reader.ReadString('}')
		if err != nil {
			return
		}
		// The '}' is consumed by ReadString, but required by json Decoder.
		event := i3Event{}
		decoder := json.NewDecoder(strings.NewReader(eventJSON + "}"))
		err = decoder.Decode(&event)
		if err != nil {
			return
		}
		b.events <- event
		// Consume ','
		if _, err := reader.ReadString(','); err != nil {
			return
		}
	}
}

// pause instructs all pausable modules to suspend processing.
func (b *I3Bar) pause() {
	for _, m := range b.i3Modules {
		m.pause()
	}
}

// resume instructs all pausable modules to continue processing.
func (b *I3Bar) resume() {
	for _, m := range b.i3Modules {
		m.resume()
	}
}
<commit_msg>Don't panic on nil bar.Output<commit_after>// Copyright 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 bar

import (
	"bufio"
	"encoding/json"
	"io"
	"os"
	"os/signal"
	"strconv"
	"strings"

	"golang.org/x/sys/unix"
)

// i3Output is sent to i3bar.
type i3Output []map[string]interface{}

// i3Event instances are received from i3bar on stdin.
type i3Event struct {
	Event
	Name string `json:"name"`
}

// i3Header is sent at the beginning of output.
type i3Header struct {
	Version     int  `json:"version"`
	StopSignal  int  `json:"stop_signal,omitempty"`
	ContSignal  int  `json:"cont_signal,omitempty"`
	ClickEvents bool `json:"click_events"`
}

// i3Module wraps Module with extra information to help run i3bar.
type i3Module struct {
	Module
	Name       string
	LastOutput i3Output
	// Keep track of the paused/resumed state of the module.
	// Using a channel here allows concurrent pause/resume across
	// modules while guaranteeing ordering.
	paused   chan bool
	pausable Pausable
}

// output converts the module's output to i3Output by adding the name (position),
// sets the module's last output to the converted i3Output, and signals the bar
// to update its output.
func (m *i3Module) output(ch chan<- interface{}) {
	for o := range m.Stream() {
		var i3out i3Output
		if o != nil {
			for _, segment := range o.Segments() {
				segmentOut := segment.i3map()
				segmentOut["name"] = m.Name
				i3out = append(i3out, segmentOut)
			}
		}
		m.LastOutput = i3out
		ch <- nil
	}
}

// loopPauseResume loops over values on the resumed channel and calls
// pause or resume on the wrapped module as appropriate.
func (m *i3Module) loopPauseResume() {
	for paused := range m.paused {
		if paused {
			m.pausable.Pause()
		} else {
			m.pausable.Resume()
		}
	}
}

// pause enqueues a pause call on the wrapped module
// if the wrapped module supports being paused.
func (m *i3Module) pause() {
	if m.paused != nil {
		m.paused <- true
	}
}

// resume enqueues a resume call on the wrapped module
// if the wrapped module supports being paused.
func (m *i3Module) resume() {
	if m.paused != nil {
		m.paused <- false
	}
}

// I3Bar is a "bar" instance that handles events and streams output.
type I3Bar struct {
	// The list of modules that make up this bar.
	i3Modules []*i3Module
	// The channel that receives a signal on module updates.
	update chan interface{}
	// The channel that aggregates all events from i3.
	events chan i3Event
	// The Reader to read events from (e.g. stdin)
	reader io.Reader
	// The Writer to write bar output to (e.g. stdout)
	writer io.Writer
	// A json encoder set to write to the output stream.
	encoder *json.Encoder
	// Flipped when Run() is called, to prevent issues with modules
	// being added after the bar has been started.
	started bool
	// Suppress pause/resume signal handling to workaround potential
	// weirdness with signals.
	suppressSignals bool
}

// Add adds a module to a bar, and returns the bar for chaining.
func (b *I3Bar) Add(modules ...Module) *I3Bar {
	for _, m := range modules {
		b.addModule(m)
	}
	// Return the bar for chaining (e.g. bar.Add(x, y).Run())
	return b
}

// addModule adds a single module to the bar.
func (b *I3Bar) addModule(module Module) {
	// Panic if adding modules to an already running bar.
	// TODO: Support this in the future.
	if b.started {
		panic("Cannot add modules after .Run()")
	}
	// Use the position of the module in the list as the "name", so when i3bar
	// sends us events, we can use atoi(name) to get the correct module.
	name := strconv.Itoa(len(b.i3Modules))
	i3Module := i3Module{
		Module: module,
		Name:   name,
	}
	if pauseable, ok := module.(Pausable); ok {
		i3Module.paused = make(chan bool, 10)
		i3Module.pausable = pauseable
		go i3Module.loopPauseResume()
	}
	b.i3Modules = append(b.i3Modules, &i3Module)
}

// SuppressSignals instructs the bar to skip the pause/resume signal handling.
// Must be called before Run.
func (b *I3Bar) SuppressSignals(suppressSignals bool) *I3Bar {
	if b.started {
		panic("Cannot change signal handling after .Run()")
	}
	b.suppressSignals = suppressSignals
	return b
}

// Run sets up all the streams and enters the main loop.
func (b *I3Bar) Run() error {
	var signalChan chan os.Signal
	if !b.suppressSignals {
		// Set up signal handlers for USR1/2 to pause/resume supported modules.
		signalChan = make(chan os.Signal, 2)
		signal.Notify(signalChan, unix.SIGUSR1, unix.SIGUSR2)
	}

	// Mark the bar as started.
	b.started = true

	// Read events from the input stream, pipe them to the events channel.
	go b.readEvents()
	for _, m := range b.i3Modules {
		go m.output(b.update)
	}

	// Write header.
	header := i3Header{
		Version:     1,
		ClickEvents: true,
	}

	if !b.suppressSignals {
		// Go doesn't allow us to handle the default SIGSTOP,
		// so we'll use SIGUSR1 and SIGUSR2 for pause/resume.
		header.StopSignal = int(unix.SIGUSR1)
		header.ContSignal = int(unix.SIGUSR2)
	}
	// Set up the encoder for the output stream,
	// so that module outputs can be written directly.
	b.encoder = json.NewEncoder(b.writer)
	if err := b.encoder.Encode(&header); err != nil {
		return err
	}
	// Start the infinite array.
	if _, err := io.WriteString(b.writer, "["); err != nil {
		return err
	}

	// Infinite arrays on both sides.
	for {
		select {
		case _ = <-b.update:
			// The complete bar needs to printed on each update.
			if err := b.print(); err != nil {
				return err
			}
		case event := <-b.events:
			// Events are stripped of the name before being dispatched to the
			// correct module.
			if module, ok := b.get(event.Name); ok {
				// Check that the module actually supports click events.
				if clickable, ok := module.Module.(Clickable); ok {
					// Goroutine to prevent click handlers from blocking the bar.
					go clickable.Click(event.Event)
				}
			}
		case sig := <-signalChan:
			switch sig {
			case unix.SIGUSR1:
				b.pause()
			case unix.SIGUSR2:
				b.resume()
			}
		}
	}
}

// Run with a list of modules just adds each module and runs the bar on stdout/stdin.
func Run(modules ...Module) error {
	return New().Add(modules...).Run()
}

// RunOnIo takes a list of modules and the input/output streams, and runs the bar.
func RunOnIo(reader io.Reader, writer io.Writer, modules ...Module) error {
	return NewOnIo(reader, writer).Add(modules...).Run()
}

// New constructs a new bar running on standard I/O.
func New() *I3Bar {
	return NewOnIo(os.Stdin, os.Stdout)
}

// NewOnIo constructs a new bar with an input and output stream, for maximum flexibility.
func NewOnIo(reader io.Reader, writer io.Writer) *I3Bar {
	return &I3Bar{
		update: make(chan interface{}),
		events: make(chan i3Event),
		reader: reader,
		writer: writer,
	}
}

// print outputs the entire bar, using the last output for each module.
func (b *I3Bar) print() error {
	// i3bar requires the entire bar to be printed at once, so we just take the
	// last cached value for each module and construct the current bar.
	// The bar will update any modules before calling this method, so the
	// LastOutput property of each module will represent the current state.
	var outputs []map[string]interface{}
	for _, m := range b.i3Modules {
		for _, segment := range m.LastOutput {
			outputs = append(outputs, segment)
		}
	}
	if err := b.encoder.Encode(outputs); err != nil {
		return err
	}
	_, err := io.WriteString(b.writer, ",\n")
	return err
}

// get finds the module that corresponds to the given "name" from i3.
func (b *I3Bar) get(name string) (*i3Module, bool) {
	index, err := strconv.Atoi(name)
	if err != nil {
		return nil, false
	}
	if index < 0 || len(b.i3Modules) <= index {
		return nil, false
	}
	return b.i3Modules[index], true
}

// readEvents parses the infinite stream of events received from i3.
func (b *I3Bar) readEvents() {
	// Buffered I/O to allow complete events to be read in at once.
	reader := bufio.NewReader(b.reader)
	// Consume opening '['
	if rune, _, err := reader.ReadRune(); err != nil || rune != '[' {
		return
	}
	for {
		// While the 'proper' way to implement this infinite parser would be to keep
		// a state machine and hook into json parsing and stuff, we'll take a
		// shortcut since we know there are no nested objects. So all we have to do
		// is read until the first '}', decode it, consume the ',', and repeat.
		eventJSON, err := reader.ReadString('}')
		if err != nil {
			return
		}
		// The '}' is consumed by ReadString, but required by json Decoder.
		event := i3Event{}
		decoder := json.NewDecoder(strings.NewReader(eventJSON + "}"))
		err = decoder.Decode(&event)
		if err != nil {
			return
		}
		b.events <- event
		// Consume ','
		if _, err := reader.ReadString(','); err != nil {
			return
		}
	}
}

// pause instructs all pausable modules to suspend processing.
func (b *I3Bar) pause() {
	for _, m := range b.i3Modules {
		m.pause()
	}
}

// resume instructs all pausable modules to continue processing.
func (b *I3Bar) resume() {
	for _, m := range b.i3Modules {
		m.resume()
	}
}
<|endoftext|>


<commit_before>package kapacitor

import (
	"errors"
	"time"

	"sync"
	"sync/atomic"

	"github.com/influxdata/kapacitor/edge"
	"github.com/influxdata/kapacitor/models"
	"github.com/influxdata/kapacitor/pipeline"
)

type BarrierNode struct {
	node
	b              *pipeline.BarrierNode
	barrierStopper map[models.GroupID]func()
}

// Create a new  BarrierNode, which emits a barrier if data traffic has been idle for the configured amount of time.
func newBarrierNode(et *ExecutingTask, n *pipeline.BarrierNode, d NodeDiagnostic) (*BarrierNode, error) {
	if n.Idle == 0 && n.Period == 0 {
		return nil, errors.New("barrier node must have either a non zero idle or a non zero period")
	}
	bn := &BarrierNode{
		node:           node{Node: n, et: et, diag: d},
		b:              n,
		barrierStopper: map[models.GroupID]func(){},
	}
	bn.node.runF = bn.runBarrierEmitter
	return bn, nil
}

func (n *BarrierNode) runBarrierEmitter([]byte) error {
	defer n.stopBarrierEmitter()
	consumer := edge.NewGroupedConsumer(n.ins[0], n)
	n.statMap.Set(statCardinalityGauge, consumer.CardinalityVar())
	return consumer.Consume()
}

func (n *BarrierNode) stopBarrierEmitter() {
	for _, stopF := range n.barrierStopper {
		stopF()
	}
}

func (n *BarrierNode) NewGroup(group edge.GroupInfo, first edge.PointMeta) (edge.Receiver, error) {
	r, stopF, err := n.newBarrier(group, first)
	if err != nil {
		return nil, err
	}
	n.barrierStopper[group.ID] = stopF
	return edge.NewReceiverFromForwardReceiverWithStats(
		n.outs,
		edge.NewTimedForwardReceiver(n.timer, r),
	), nil
}

func (n *BarrierNode) newBarrier(group edge.GroupInfo, first edge.PointMeta) (edge.ForwardReceiver, func(), error) {
	switch {
	case n.b.Idle != 0:
		idleBarrier := newIdleBarrier(
			first.Name(),
			group,
			n.b.Idle,
			n.outs,
		)
		return idleBarrier, idleBarrier.Stop, nil
	case n.b.Period != 0:
		periodicBarrier := newPeriodicBarrier(
			first.Name(),
			group,
			n.b.Period,
			n.outs,
		)
		return periodicBarrier, periodicBarrier.Stop, nil
	default:
		return nil, nil, errors.New("unreachable code, barrier node should have non-zero idle or non-zero period")
	}
}

type idleBarrier struct {
	name  string
	group edge.GroupInfo

	idle  time.Duration
	lastT atomic.Value
	timer *time.Timer
	wg    sync.WaitGroup
	outs  []edge.StatsEdge
	stopC chan struct{}
}

func newIdleBarrier(name string, group edge.GroupInfo, idle time.Duration, outs []edge.StatsEdge) *idleBarrier {
	r := &idleBarrier{
		name:  name,
		group: group,
		idle:  idle,
		lastT: atomic.Value{},
		timer: time.NewTimer(idle),
		wg:    sync.WaitGroup{},
		outs:  outs,
		stopC: make(chan struct{}, 1),
	}

	r.Init()

	return r
}

func (n *idleBarrier) Init() {
	n.lastT.Store(time.Time{})
	n.wg.Add(1)

	go n.idleHandler()
}

func (n *idleBarrier) Stop() {
	close(n.stopC)
	n.timer.Stop()
	n.wg.Wait()
}

func (n *idleBarrier) BeginBatch(m edge.BeginBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *idleBarrier) BatchPoint(m edge.BatchPointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		n.resetTimer()
		return m, nil
	}
	return nil, nil
}
func (n *idleBarrier) EndBatch(m edge.EndBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *idleBarrier) Barrier(m edge.BarrierMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		n.resetTimer()
		return m, nil
	}
	return nil, nil
}
func (n *idleBarrier) DeleteGroup(m edge.DeleteGroupMessage) (edge.Message, error) {
	if m.GroupID() == n.group.ID {
		n.Stop()
	}
	return m, nil
}

func (n *idleBarrier) Point(m edge.PointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		n.resetTimer()
		return m, nil
	}
	return nil, nil
}

func (n *idleBarrier) resetTimer() {
	n.timer.Stop()
	n.timer.Reset(n.idle)
}

func (n *idleBarrier) emitBarrier() error {
	nowT := time.Now()
	n.lastT.Store(nowT)
	return edge.Forward(n.outs, edge.NewBarrierMessage(n.group, nowT))
}

func (n *idleBarrier) idleHandler() {
	defer n.wg.Done()
	for {
		select {
		case <-n.timer.C:
			n.emitBarrier()
			n.resetTimer()
		case <-n.stopC:
			return
		}
	}
}

type periodicBarrier struct {
	name  string
	group edge.GroupInfo

	lastT  atomic.Value
	ticker *time.Ticker
	wg     sync.WaitGroup
	outs   []edge.StatsEdge
	stopC  chan bool
}

func newPeriodicBarrier(name string, group edge.GroupInfo, period time.Duration, outs []edge.StatsEdge) *periodicBarrier {
	r := &periodicBarrier{
		name:   name,
		group:  group,
		lastT:  atomic.Value{},
		ticker: time.NewTicker(period),
		wg:     sync.WaitGroup{},
		outs:   outs,
		stopC:  make(chan bool, 1),
	}

	r.Init()

	return r
}

func (n *periodicBarrier) Init() {
	n.lastT.Store(time.Time{})
	n.wg.Add(1)

	go n.periodicEmitter()
}

func (n *periodicBarrier) Stop() {
	n.stopC <- true
	n.ticker.Stop()
	n.wg.Wait()
}

func (n *periodicBarrier) BeginBatch(m edge.BeginBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *periodicBarrier) BatchPoint(m edge.BatchPointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		return m, nil
	}
	return nil, nil
}
func (n *periodicBarrier) EndBatch(m edge.EndBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *periodicBarrier) Barrier(m edge.BarrierMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		return m, nil
	}
	return nil, nil
}
func (n *periodicBarrier) DeleteGroup(m edge.DeleteGroupMessage) (edge.Message, error) {
	if m.GroupID() == n.group.ID {
		n.Stop()
	}
	return m, nil
}

func (n *periodicBarrier) Point(m edge.PointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		return m, nil
	}
	return nil, nil
}

func (n *periodicBarrier) emitBarrier() error {
	nowT := time.Now()
	n.lastT.Store(nowT)
	return edge.Forward(n.outs, edge.NewBarrierMessage(n.group, nowT))
}

func (n *periodicBarrier) periodicEmitter() {
	defer n.wg.Done()
	for {
		select {
		case <-n.ticker.C:
			n.emitBarrier()
		case <-n.stopC:
			return
		}
	}
}
<commit_msg>using channel to signal timer reset<commit_after>package kapacitor

import (
	"errors"
	"time"

	"sync"
	"sync/atomic"

	"github.com/influxdata/kapacitor/edge"
	"github.com/influxdata/kapacitor/models"
	"github.com/influxdata/kapacitor/pipeline"
)

type BarrierNode struct {
	node
	b              *pipeline.BarrierNode
	barrierStopper map[models.GroupID]func()
}

// Create a new  BarrierNode, which emits a barrier if data traffic has been idle for the configured amount of time.
func newBarrierNode(et *ExecutingTask, n *pipeline.BarrierNode, d NodeDiagnostic) (*BarrierNode, error) {
	if n.Idle == 0 && n.Period == 0 {
		return nil, errors.New("barrier node must have either a non zero idle or a non zero period")
	}
	bn := &BarrierNode{
		node:           node{Node: n, et: et, diag: d},
		b:              n,
		barrierStopper: map[models.GroupID]func(){},
	}
	bn.node.runF = bn.runBarrierEmitter
	return bn, nil
}

func (n *BarrierNode) runBarrierEmitter([]byte) error {
	defer n.stopBarrierEmitter()
	consumer := edge.NewGroupedConsumer(n.ins[0], n)
	n.statMap.Set(statCardinalityGauge, consumer.CardinalityVar())
	return consumer.Consume()
}

func (n *BarrierNode) stopBarrierEmitter() {
	for _, stopF := range n.barrierStopper {
		stopF()
	}
}

func (n *BarrierNode) NewGroup(group edge.GroupInfo, first edge.PointMeta) (edge.Receiver, error) {
	r, stopF, err := n.newBarrier(group, first)
	if err != nil {
		return nil, err
	}
	n.barrierStopper[group.ID] = stopF
	return edge.NewReceiverFromForwardReceiverWithStats(
		n.outs,
		edge.NewTimedForwardReceiver(n.timer, r),
	), nil
}

func (n *BarrierNode) newBarrier(group edge.GroupInfo, first edge.PointMeta) (edge.ForwardReceiver, func(), error) {
	switch {
	case n.b.Idle != 0:
		idleBarrier := newIdleBarrier(
			first.Name(),
			group,
			n.b.Idle,
			n.outs,
		)
		return idleBarrier, idleBarrier.Stop, nil
	case n.b.Period != 0:
		periodicBarrier := newPeriodicBarrier(
			first.Name(),
			group,
			n.b.Period,
			n.outs,
		)
		return periodicBarrier, periodicBarrier.Stop, nil
	default:
		return nil, nil, errors.New("unreachable code, barrier node should have non-zero idle or non-zero period")
	}
}

type idleBarrier struct {
	name  string
	group edge.GroupInfo

	idle        time.Duration
	lastT       atomic.Value
	timer       *time.Timer
	wg          sync.WaitGroup
	outs        []edge.StatsEdge
	stopC       chan struct{}
	resetTimerC chan struct{}
}

func newIdleBarrier(name string, group edge.GroupInfo, idle time.Duration, outs []edge.StatsEdge) *idleBarrier {
	r := &idleBarrier{
		name:        name,
		group:       group,
		idle:        idle,
		lastT:       atomic.Value{},
		timer:       time.NewTimer(idle),
		wg:          sync.WaitGroup{},
		outs:        outs,
		stopC:       make(chan struct{}),
		resetTimerC: make(chan struct{}),
	}

	r.Init()

	return r
}

func (n *idleBarrier) Init() {
	n.lastT.Store(time.Time{})
	n.wg.Add(1)

	go n.idleHandler()
}

func (n *idleBarrier) Stop() {
	close(n.stopC)
	n.timer.Stop()
	n.wg.Wait()
}

func (n *idleBarrier) BeginBatch(m edge.BeginBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *idleBarrier) BatchPoint(m edge.BatchPointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		n.resetTimer()
		return m, nil
	}
	return nil, nil
}
func (n *idleBarrier) EndBatch(m edge.EndBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *idleBarrier) Barrier(m edge.BarrierMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		n.resetTimer()
		return m, nil
	}
	return nil, nil
}
func (n *idleBarrier) DeleteGroup(m edge.DeleteGroupMessage) (edge.Message, error) {
	if m.GroupID() == n.group.ID {
		n.Stop()
	}
	return m, nil
}

func (n *idleBarrier) Point(m edge.PointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		n.resetTimer()
		return m, nil
	}
	return nil, nil
}

func (n *idleBarrier) resetTimer() {
	n.resetTimerC <- struct{}{}
}

func (n *idleBarrier) emitBarrier() error {
	nowT := time.Now()
	n.lastT.Store(nowT)
	return edge.Forward(n.outs, edge.NewBarrierMessage(n.group, nowT))
}

func (n *idleBarrier) idleHandler() {
	defer n.wg.Done()
	for {
		select {
		case <-n.resetTimerC:
			if !n.timer.Stop() {
				<-n.timer.C
			}
			n.timer.Reset(n.idle)
		case <-n.timer.C:
			n.emitBarrier()
			n.timer.Reset(n.idle)
		case <-n.stopC:
			return
		}
	}
}

type periodicBarrier struct {
	name  string
	group edge.GroupInfo

	lastT  atomic.Value
	ticker *time.Ticker
	wg     sync.WaitGroup
	outs   []edge.StatsEdge
	stopC  chan struct{}
}

func newPeriodicBarrier(name string, group edge.GroupInfo, period time.Duration, outs []edge.StatsEdge) *periodicBarrier {
	r := &periodicBarrier{
		name:   name,
		group:  group,
		lastT:  atomic.Value{},
		ticker: time.NewTicker(period),
		wg:     sync.WaitGroup{},
		outs:   outs,
		stopC:  make(chan struct{}),
	}

	r.Init()

	return r
}

func (n *periodicBarrier) Init() {
	n.lastT.Store(time.Time{})
	n.wg.Add(1)

	go n.periodicEmitter()
}

func (n *periodicBarrier) Stop() {
	close(n.stopC)
	n.ticker.Stop()
	n.wg.Wait()
}

func (n *periodicBarrier) BeginBatch(m edge.BeginBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *periodicBarrier) BatchPoint(m edge.BatchPointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		return m, nil
	}
	return nil, nil
}
func (n *periodicBarrier) EndBatch(m edge.EndBatchMessage) (edge.Message, error) {
	return m, nil
}
func (n *periodicBarrier) Barrier(m edge.BarrierMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		return m, nil
	}
	return nil, nil
}
func (n *periodicBarrier) DeleteGroup(m edge.DeleteGroupMessage) (edge.Message, error) {
	if m.GroupID() == n.group.ID {
		n.Stop()
	}
	return m, nil
}

func (n *periodicBarrier) Point(m edge.PointMessage) (edge.Message, error) {
	if !m.Time().Before(n.lastT.Load().(time.Time)) {
		return m, nil
	}
	return nil, nil
}

func (n *periodicBarrier) emitBarrier() error {
	nowT := time.Now()
	n.lastT.Store(nowT)
	return edge.Forward(n.outs, edge.NewBarrierMessage(n.group, nowT))
}

func (n *periodicBarrier) periodicEmitter() {
	defer n.wg.Done()
	for {
		select {
		case <-n.ticker.C:
			n.emitBarrier()
		case <-n.stopC:
			return
		}
	}
}
<|endoftext|>


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

package http

import (
	"net/http"
	"time"

	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// httpAckNacker implements the AckNacker interface
type httpAckNacker struct {
	Chresp chan<- MsgRes // A channel dedicated to send back a response
}

// Ack implements the core.AckNacker interface
func (an httpAckNacker) Ack(p core.Packet) error {
	if an.Chresp == nil {
		return nil
	}
	defer close(an.Chresp)

	if p == nil {
		an.Chresp <- MsgRes{StatusCode: http.StatusOK}
		return nil
	}

	data, err := p.MarshalBinary()
	if err != nil {
		return errors.New(errors.Structural, err)
	}

	select {
	case an.Chresp <- MsgRes{
		StatusCode: http.StatusOK,
		Content:    data,
	}:
		return nil
	case <-time.After(time.Millisecond * 50):
		return errors.New(errors.Operational, "No response was given to the acknacker")
	}
}

// Nack implements the core.AckNacker interface
func (an httpAckNacker) Nack() error {
	if an.Chresp == nil {
		return nil
	}
	defer close(an.Chresp)

	select {
	case an.Chresp <- MsgRes{
		StatusCode: http.StatusNotFound,
		Content:    []byte(errors.Structural),
	}:
	case <-time.After(time.Millisecond * 50):
		return errors.New(errors.Operational, "No response was given to the acknacker")
	}
	return nil
}
<commit_msg>[test/http-adapter] Fix issue regarding to previous tests<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package http

import (
	"net/http"
	"time"

	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// httpAckNacker implements the AckNacker interface
type httpAckNacker struct {
	Chresp chan<- MsgRes // A channel dedicated to send back a response
}

// Ack implements the core.AckNacker interface
func (an httpAckNacker) Ack(p core.Packet) error {
	if an.Chresp == nil {
		return nil
	}
	defer close(an.Chresp)

	var data []byte
	if p != nil {
		var err error
		data, err = p.MarshalBinary()
		if err != nil {
			return errors.New(errors.Structural, err)
		}
	}

	select {
	case an.Chresp <- MsgRes{
		StatusCode: http.StatusOK,
		Content:    data,
	}:
		return nil
	case <-time.After(time.Millisecond * 50):
		return errors.New(errors.Operational, "No response was given to the acknacker")
	}
}

// Nack implements the core.AckNacker interface
func (an httpAckNacker) Nack() error {
	if an.Chresp == nil {
		return nil
	}
	defer close(an.Chresp)

	select {
	case an.Chresp <- MsgRes{
		StatusCode: http.StatusNotFound,
		Content:    []byte(errors.Structural),
	}:
		return nil
	case <-time.After(time.Millisecond * 50):
		return errors.New(errors.Operational, "No response was given to the acknacker")
	}
}
<|endoftext|>


<commit_before>package user

import (
	"encoding/json"
	"fmt"

	"github.com/silenceper/wechat/context"
	"github.com/silenceper/wechat/util"
)

const (
	userInfoURL     = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN"
	updateRemarkURL = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=%s"
)

//User 用户管理
type User struct {
	*context.Context
}

//NewUser 实例化
func NewUser(context *context.Context) *User {
	user := new(User)
	user.Context = context
	return user
}

//Info 用户基本信息
type Info struct {
	util.CommonError

	Subscribe     int32    `json:"subscribe"`
	OpenID        string   `json:"openid"`
	Nickname      string   `json:"nickname"`
	Sex           int32    `json:"sex"`
	City          string   `json:"city"`
	Country       string   `json:"country"`
	Province      string   `json:"province"`
	Language      string   `json:"language"`
	Headimgurl    string   `json:"headimgurl"`
	SubscribeTime int32    `json:"subscribe_time"`
	UnionID       string   `json:"unionid"`
	Remark        string   `json:"remark"`
	GroupID       int32    `json:"groupid"`
	TagidList     []string `json:"tagid_list"`
}

//GetUserInfo 获取用户基本信息
func (user *User) GetUserInfo(openID string) (userInfo *Info, err error) {
	var accessToken string
	accessToken, err = user.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf(userInfoURL, accessToken, openID)
	var response []byte
	response, err = util.HTTPGet(uri)
	if err != nil {
		return
	}
	userInfo = new(Info)
	err = json.Unmarshal(response, userInfo)
	if err != nil {
		return
	}
	if userInfo.ErrCode != 0 {
		err = fmt.Errorf("GetUserInfo Error , errcode=%d , errmsg=%s", userInfo.ErrCode, userInfo.ErrMsg)
		return
	}
	return
}

// UpdateRemark 设置用户备注名
func (user *User) UpdateRemark(openID, remark string) (err error) {
	var accessToken string
	accessToken, err = user.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf(updateRemarkURL, accessToken)
	var response []byte
	response, err = util.PostJSON(uri, map[string]string{"openid": openID, "remark": remark})
	if err != nil {
		return
	}

	return util.DecodeWithCommonError(response, "UpdateRemark")
}
<commit_msg>fix TagidList type error<commit_after>package user

import (
	"encoding/json"
	"fmt"

	"github.com/silenceper/wechat/context"
	"github.com/silenceper/wechat/util"
)

const (
	userInfoURL     = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN"
	updateRemarkURL = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=%s"
)

//User 用户管理
type User struct {
	*context.Context
}

//NewUser 实例化
func NewUser(context *context.Context) *User {
	user := new(User)
	user.Context = context
	return user
}

//Info 用户基本信息
type Info struct {
	util.CommonError

	Subscribe     int32   `json:"subscribe"`
	OpenID        string  `json:"openid"`
	Nickname      string  `json:"nickname"`
	Sex           int32   `json:"sex"`
	City          string  `json:"city"`
	Country       string  `json:"country"`
	Province      string  `json:"province"`
	Language      string  `json:"language"`
	Headimgurl    string  `json:"headimgurl"`
	SubscribeTime int32   `json:"subscribe_time"`
	UnionID       string  `json:"unionid"`
	Remark        string  `json:"remark"`
	GroupID       int32   `json:"groupid"`
	TagidList     []int32 `json:"tagid_list"`
}

//GetUserInfo 获取用户基本信息
func (user *User) GetUserInfo(openID string) (userInfo *Info, err error) {
	var accessToken string
	accessToken, err = user.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf(userInfoURL, accessToken, openID)
	var response []byte
	response, err = util.HTTPGet(uri)
	if err != nil {
		return
	}
	userInfo = new(Info)
	err = json.Unmarshal(response, userInfo)
	if err != nil {
		return
	}
	if userInfo.ErrCode != 0 {
		err = fmt.Errorf("GetUserInfo Error , errcode=%d , errmsg=%s", userInfo.ErrCode, userInfo.ErrMsg)
		return
	}
	return
}

// UpdateRemark 设置用户备注名
func (user *User) UpdateRemark(openID, remark string) (err error) {
	var accessToken string
	accessToken, err = user.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf(updateRemarkURL, accessToken)
	var response []byte
	response, err = util.PostJSON(uri, map[string]string{"openid": openID, "remark": remark})
	if err != nil {
		return
	}

	return util.DecodeWithCommonError(response, "UpdateRemark")
}
<|endoftext|>


<commit_before>package util

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

//Soap defines a soap interface to simplify the unit tests
type Soap func(request, endpoint, proxy string) (string, error)

//SoapCall executes a soap call to the defined environment executing the xog xml
func SoapCall(request, endpoint, proxy string) (string, error) {
	client := &http.Client{
		Timeout: time.Second * 60,
	}

	if proxy != "" {
		proxyURL, err := url.Parse(proxy)
		if err != nil {
			return "", err
		}
		client.Transport = &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		}
	}

	resp, err := client.Post(endpoint+"/niku/xog", "text/xml; charset=utf-8", bytes.NewBufferString(request))
	if err != nil {
		return "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return BytesToString(body), nil
}
<commit_msg>Increasing soap timeout<commit_after>package util

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

//Soap defines a soap interface to simplify the unit tests
type Soap func(request, endpoint, proxy string) (string, error)

//SoapCall executes a soap call to the defined environment executing the xog xml
func SoapCall(request, endpoint, proxy string) (string, error) {
	client := &http.Client{
		Timeout: time.Second * 600,
	}

	if proxy != "" {
		proxyURL, err := url.Parse(proxy)
		if err != nil {
			return "", err
		}
		client.Transport = &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		}
	}

	resp, err := client.Post(endpoint+"/niku/xog", "text/xml; charset=utf-8", bytes.NewBufferString(request))
	if err != nil {
		return "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return BytesToString(body), nil
}
<|endoftext|>


<commit_before>package util

import (
	"errors"
	"fmt"
	"io/ioutil"

	"golang.org/x/net/context"

	log "github.com/Sirupsen/logrus"
	docker "github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
)

const labelPrefix string = "io.conplicity"

// CheckErr checks for error, logs and optionally exits the program
func CheckErr(err error, msg string, level string) {
	if err != nil {
		switch level {
		case "debug":
			log.Debugf(msg, err)
		case "info":
			log.Infof(msg, err)
		case "warn":
			log.Warnf(msg, err)
		case "error":
			log.Errorf(msg, err)
		case "fatal":
			log.Fatalf(msg, err)
		case "panic":
			log.Panicf(msg, err)
		default:
			log.Panicf("Wrong loglevel '%v', please report this bug", level)
		}
	}
}

// GetVolumeLabel retrieves the value of given key in the io.conplicity
// namespace of the volume labels
func GetVolumeLabel(vol *types.Volume, key string) (value string, err error) {
	//log.Debugf("Getting value for label %s of volume %s", key, vol.Name)
	value, ok := vol.Labels[labelPrefix+"."+key]
	if !ok {
		errMsg := fmt.Sprintf("Key %v not found in labels for volume %v", key, vol.Name)
		err = errors.New(errMsg)
	}
	return
}

// PullImage pulls an image from the registry
func PullImage(c *docker.Client, image string) (err error) {
	if _, _, err = c.ImageInspectWithRaw(context.Background(), image, false); err != nil {
		// TODO: output pull to logs
		log.WithFields(log.Fields{
			"image": image,
		}).Info("Pulling image")
		resp, err := c.ImagePull(context.Background(), image, types.ImagePullOptions{})
		if err != nil {
			log.Errorf("ImagePull returned an error: %v", err)
			return err
		}
		defer resp.Close()
		body, err := ioutil.ReadAll(resp)
		if err != nil {
			log.Errorf("Failed to read from ImagePull response: %v", err)
			return err
		}
		log.Debugf("Pull image response body: %v", string(body))
	} else {
		log.WithFields(log.Fields{
			"image": image,
		}).Debug("Image already pulled, not pulling")
	}

	return nil
}

// RemoveContainer removes a container
func RemoveContainer(c *docker.Client, id string) {
	log.WithFields(log.Fields{
		"container": id,
	}).Infof("Removing container")
	err := c.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{
		Force:         true,
		RemoveVolumes: true,
	})
	CheckErr(err, "Failed to remove container "+id+": %v", "error")
}
<commit_msg>Fix c.ImageInspectWithRaw() call for new version of docker/engine-api<commit_after>package util

import (
	"errors"
	"fmt"
	"io/ioutil"

	"golang.org/x/net/context"

	log "github.com/Sirupsen/logrus"
	docker "github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
)

const labelPrefix string = "io.conplicity"

// CheckErr checks for error, logs and optionally exits the program
func CheckErr(err error, msg string, level string) {
	if err != nil {
		switch level {
		case "debug":
			log.Debugf(msg, err)
		case "info":
			log.Infof(msg, err)
		case "warn":
			log.Warnf(msg, err)
		case "error":
			log.Errorf(msg, err)
		case "fatal":
			log.Fatalf(msg, err)
		case "panic":
			log.Panicf(msg, err)
		default:
			log.Panicf("Wrong loglevel '%v', please report this bug", level)
		}
	}
}

// GetVolumeLabel retrieves the value of given key in the io.conplicity
// namespace of the volume labels
func GetVolumeLabel(vol *types.Volume, key string) (value string, err error) {
	//log.Debugf("Getting value for label %s of volume %s", key, vol.Name)
	value, ok := vol.Labels[labelPrefix+"."+key]
	if !ok {
		errMsg := fmt.Sprintf("Key %v not found in labels for volume %v", key, vol.Name)
		err = errors.New(errMsg)
	}
	return
}

// PullImage pulls an image from the registry
func PullImage(c *docker.Client, image string) (err error) {
	if _, _, err = c.ImageInspectWithRaw(context.Background(), image); err != nil {
		// TODO: output pull to logs
		log.WithFields(log.Fields{
			"image": image,
		}).Info("Pulling image")
		resp, err := c.ImagePull(context.Background(), image, types.ImagePullOptions{})
		if err != nil {
			log.Errorf("ImagePull returned an error: %v", err)
			return err
		}
		defer resp.Close()
		body, err := ioutil.ReadAll(resp)
		if err != nil {
			log.Errorf("Failed to read from ImagePull response: %v", err)
			return err
		}
		log.Debugf("Pull image response body: %v", string(body))
	} else {
		log.WithFields(log.Fields{
			"image": image,
		}).Debug("Image already pulled, not pulling")
	}

	return nil
}

// RemoveContainer removes a container
func RemoveContainer(c *docker.Client, id string) {
	log.WithFields(log.Fields{
		"container": id,
	}).Infof("Removing container")
	err := c.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{
		Force:         true,
		RemoveVolumes: true,
	})
	CheckErr(err, "Failed to remove container "+id+": %v", "error")
}
<|endoftext|>


<commit_before>package graphql

import (
	"github.com/sprucehealth/graphql/gqlerrors"
	"github.com/sprucehealth/graphql/language/ast"
	"github.com/sprucehealth/graphql/language/visitor"
)

type ValidationResult struct {
	IsValid bool
	Errors  []gqlerrors.FormattedError
}

// ValidateDocument implements the "Validation" section of the spec.
//
// Validation runs synchronously, returning an array of encountered errors, or
// an empty array if no errors were encountered and the document is valid.
//
// A list of specific validation rules may be provided. If not provided, the
// default list of rules defined by the GraphQL specification will be used.
//
// Each validation rules is a function which returns a visitor
// (see the language/visitor API). Visitor methods are expected to return
// GraphQLErrors, or Arrays of GraphQLErrors when invalid.
func ValidateDocument(schema *Schema, astDoc *ast.Document, rules []ValidationRuleFn) (vr ValidationResult) {
	if len(rules) == 0 {
		rules = SpecifiedRules
	}

	vr.IsValid = false
	if schema == nil {
		vr.Errors = append(vr.Errors, gqlerrors.NewFormattedError("Must provide schema"))
		return vr
	}
	if astDoc == nil {
		vr.Errors = append(vr.Errors, gqlerrors.NewFormattedError("Must provide document"))
		return vr
	}
	typeInfo := NewTypeInfo(&TypeInfoConfig{
		Schema: schema,
	})
	vr.Errors = VisitUsingRules(schema, typeInfo, astDoc, rules)
	vr.IsValid = len(vr.Errors) == 0
	return vr
}

// VisitUsingRules This uses a specialized visitor which runs multiple visitors in parallel,
// while maintaining the visitor skip and break API.
// @internal
// Had to expose it to unit test experimental customizable validation feature,
// but not meant for public consumption
func VisitUsingRules(schema *Schema, typeInfo *TypeInfo, astDoc *ast.Document, rules []ValidationRuleFn) (errors []gqlerrors.FormattedError) {
	context := NewValidationContext(schema, astDoc, typeInfo)

	var visitInstance func(astNode ast.Node, instance *ValidationRuleInstance)

	visitInstance = func(astNode ast.Node, instance *ValidationRuleInstance) {
		visitor.Visit(astNode, &visitor.VisitorOptions{
			Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
				var action = visitor.ActionNoChange
				var result interface{}
				switch node := p.Node.(type) {
				case ast.Node:
					// Collect type information about the current position in the AST.
					typeInfo.Enter(node)

					// Get the visitor function from the validation instance, and if it
					// exists, call it with the visitor arguments.
					if instance.Enter != nil {
						action, result = instance.Enter(p)
					}

					// If the result is "false" (ie action === Action.Skip), we're not visiting any descendent nodes,
					// but need to update typeInfo.
					if action == visitor.ActionSkip {
						typeInfo.Leave(node)
					}
				}

				return action, result
			},
			Leave: func(p visitor.VisitFuncParams) (string, interface{}) {
				var action = visitor.ActionNoChange
				var result interface{}
				switch node := p.Node.(type) {
				case ast.Node:
					// Get the visitor function from the validation instance, and if it
					// exists, call it with the visitor arguments.
					if instance.Leave != nil {
						action, result = instance.Leave(p)
					}

					// Update typeInfo.
					typeInfo.Leave(node)
				}
				return action, result
			},
		})
	}

	for _, rule := range rules {
		visitInstance(astDoc, rule(context))
	}
	return context.Errors()
}

type HasSelectionSet interface {
	GetLoc() ast.Location
	GetSelectionSet() *ast.SelectionSet
}

var _ HasSelectionSet = (*ast.OperationDefinition)(nil)
var _ HasSelectionSet = (*ast.FragmentDefinition)(nil)

type VariableUsage struct {
	Node *ast.Variable
	Type Input
}

type ValidationContext struct {
	schema                         *Schema
	astDoc                         *ast.Document
	typeInfo                       *TypeInfo
	fragments                      map[string]*ast.FragmentDefinition
	variableUsages                 map[HasSelectionSet][]*VariableUsage
	recursiveVariableUsages        map[*ast.OperationDefinition][]*VariableUsage
	recursivelyReferencedFragments map[*ast.OperationDefinition][]*ast.FragmentDefinition
	fragmentSpreads                map[*ast.SelectionSet][]*ast.FragmentSpread
	errors                         []gqlerrors.FormattedError
}

func NewValidationContext(schema *Schema, astDoc *ast.Document, typeInfo *TypeInfo) *ValidationContext {
	return &ValidationContext{
		schema:                         schema,
		astDoc:                         astDoc,
		typeInfo:                       typeInfo,
		variableUsages:                 make(map[HasSelectionSet][]*VariableUsage),
		recursiveVariableUsages:        make(map[*ast.OperationDefinition][]*VariableUsage),
		recursivelyReferencedFragments: make(map[*ast.OperationDefinition][]*ast.FragmentDefinition),
		fragmentSpreads:                make(map[*ast.SelectionSet][]*ast.FragmentSpread),
	}
}

func (ctx *ValidationContext) ReportError(err error) {
	formattedErr := gqlerrors.FormatError(err)
	ctx.errors = append(ctx.errors, formattedErr)
}

func (ctx *ValidationContext) Errors() []gqlerrors.FormattedError {
	return ctx.errors
}

func (ctx *ValidationContext) Schema() *Schema {
	return ctx.schema
}
func (ctx *ValidationContext) Document() *ast.Document {
	return ctx.astDoc
}

func (ctx *ValidationContext) Fragment(name string) *ast.FragmentDefinition {
	if ctx.fragments == nil {
		if ctx.Document() == nil {
			return nil
		}
		defs := ctx.Document().Definitions
		fragments := make(map[string]*ast.FragmentDefinition)
		for _, def := range defs {
			if def, ok := def.(*ast.FragmentDefinition); ok {
				defName := ""
				if def.Name != nil {
					defName = def.Name.Value
				}
				fragments[defName] = def
			}
		}
		ctx.fragments = fragments
	}
	return ctx.fragments[name]
}

func (ctx *ValidationContext) FragmentSpreads(node *ast.SelectionSet) []*ast.FragmentSpread {
	if spreads, ok := ctx.fragmentSpreads[node]; ok && spreads != nil {
		return spreads
	}

	spreads := []*ast.FragmentSpread{}
	setsToVisit := []*ast.SelectionSet{node}

	for {
		if len(setsToVisit) == 0 {
			break
		}
		var set *ast.SelectionSet
		// pop
		set, setsToVisit = setsToVisit[len(setsToVisit)-1], setsToVisit[:len(setsToVisit)-1]
		if set.Selections != nil {
			for _, selection := range set.Selections {
				switch selection := selection.(type) {
				case *ast.FragmentSpread:
					spreads = append(spreads, selection)
				case *ast.Field:
					if selection.SelectionSet != nil {
						setsToVisit = append(setsToVisit, selection.SelectionSet)
					}
				case *ast.InlineFragment:
					if selection.SelectionSet != nil {
						setsToVisit = append(setsToVisit, selection.SelectionSet)
					}
				}
			}
		}
		ctx.fragmentSpreads[node] = spreads
	}
	return spreads
}

func (ctx *ValidationContext) RecursivelyReferencedFragments(operation *ast.OperationDefinition) []*ast.FragmentDefinition {
	if fragments, ok := ctx.recursivelyReferencedFragments[operation]; ok && fragments != nil {
		return fragments
	}

	fragments := []*ast.FragmentDefinition{}
	collectedNames := map[string]bool{}
	nodesToVisit := []*ast.SelectionSet{operation.SelectionSet}

	for {
		if len(nodesToVisit) == 0 {
			break
		}

		var node *ast.SelectionSet

		node, nodesToVisit = nodesToVisit[len(nodesToVisit)-1], nodesToVisit[:len(nodesToVisit)-1]
		spreads := ctx.FragmentSpreads(node)
		for _, spread := range spreads {
			fragName := ""
			if spread.Name != nil {
				fragName = spread.Name.Value
			}
			if res, ok := collectedNames[fragName]; !ok || !res {
				collectedNames[fragName] = true
				fragment := ctx.Fragment(fragName)
				if fragment != nil {
					fragments = append(fragments, fragment)
					nodesToVisit = append(nodesToVisit, fragment.SelectionSet)
				}
			}

		}
	}

	ctx.recursivelyReferencedFragments[operation] = fragments
	return fragments
}

func (ctx *ValidationContext) VariableUsages(node HasSelectionSet) []*VariableUsage {
	if usages, ok := ctx.variableUsages[node]; ok && usages != nil {
		return usages
	}
	typeInfo := NewTypeInfo(&TypeInfoConfig{
		Schema: ctx.schema,
	})

	var usages []*VariableUsage
	visitor.Visit(node, &visitor.VisitorOptions{
		Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
			if node, ok := p.Node.(ast.Node); ok {
				typeInfo.Enter(node)
				switch node := node.(type) {
				case *ast.VariableDefinition:
					typeInfo.Leave(node)
					return visitor.ActionSkip, nil
				case *ast.Variable:
					usages = append(usages, &VariableUsage{
						Node: node,
						Type: typeInfo.InputType(),
					})
				}
			}
			return visitor.ActionNoChange, nil
		},
		Leave: func(p visitor.VisitFuncParams) (string, interface{}) {
			if node, ok := p.Node.(ast.Node); ok {
				typeInfo.Leave(node)
			}
			return visitor.ActionNoChange, nil
		},
	})

	ctx.variableUsages[node] = usages
	return usages
}

func (ctx *ValidationContext) RecursiveVariableUsages(operation *ast.OperationDefinition) []*VariableUsage {
	if usages, ok := ctx.recursiveVariableUsages[operation]; ok && usages != nil {
		return usages
	}
	usages := ctx.VariableUsages(operation)

	fragments := ctx.RecursivelyReferencedFragments(operation)
	for _, fragment := range fragments {
		fragmentUsages := ctx.VariableUsages(fragment)
		usages = append(usages, fragmentUsages...)
	}

	ctx.recursiveVariableUsages[operation] = usages
	return usages
}

func (ctx *ValidationContext) Type() Output {
	return ctx.typeInfo.Type()
}
func (ctx *ValidationContext) ParentType() Composite {
	return ctx.typeInfo.ParentType()
}
func (ctx *ValidationContext) InputType() Input {
	return ctx.typeInfo.InputType()
}
func (ctx *ValidationContext) FieldDef() *FieldDefinition {
	return ctx.typeInfo.FieldDef()
}
func (ctx *ValidationContext) Directive() *Directive {
	return ctx.typeInfo.Directive()
}
func (ctx *ValidationContext) Argument() *Argument {
	return ctx.typeInfo.Argument()
}
<commit_msg>Minor cleanup<commit_after>package graphql

import (
	"github.com/sprucehealth/graphql/gqlerrors"
	"github.com/sprucehealth/graphql/language/ast"
	"github.com/sprucehealth/graphql/language/visitor"
)

type ValidationResult struct {
	IsValid bool
	Errors  []gqlerrors.FormattedError
}

// ValidateDocument implements the "Validation" section of the spec.
//
// Validation runs synchronously, returning an array of encountered errors, or
// an empty array if no errors were encountered and the document is valid.
//
// A list of specific validation rules may be provided. If not provided, the
// default list of rules defined by the GraphQL specification will be used.
//
// Each validation rules is a function which returns a visitor
// (see the language/visitor API). Visitor methods are expected to return
// GraphQLErrors, or Arrays of GraphQLErrors when invalid.
func ValidateDocument(schema *Schema, astDoc *ast.Document, rules []ValidationRuleFn) (vr ValidationResult) {
	if len(rules) == 0 {
		rules = SpecifiedRules
	}

	vr.IsValid = false
	if schema == nil {
		vr.Errors = append(vr.Errors, gqlerrors.NewFormattedError("Must provide schema"))
		return vr
	}
	if astDoc == nil {
		vr.Errors = append(vr.Errors, gqlerrors.NewFormattedError("Must provide document"))
		return vr
	}
	typeInfo := NewTypeInfo(&TypeInfoConfig{
		Schema: schema,
	})
	vr.Errors = VisitUsingRules(schema, typeInfo, astDoc, rules)
	vr.IsValid = len(vr.Errors) == 0
	return vr
}

// VisitUsingRules This uses a specialized visitor which runs multiple visitors in parallel,
// while maintaining the visitor skip and break API.
// @internal
// Had to expose it to unit test experimental customizable validation feature,
// but not meant for public consumption
func VisitUsingRules(schema *Schema, typeInfo *TypeInfo, astDoc *ast.Document, rules []ValidationRuleFn) (errors []gqlerrors.FormattedError) {
	context := NewValidationContext(schema, astDoc, typeInfo)

	var visitInstance func(astNode ast.Node, instance *ValidationRuleInstance)

	visitInstance = func(astNode ast.Node, instance *ValidationRuleInstance) {
		visitor.Visit(astNode, &visitor.VisitorOptions{
			Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
				node, ok := p.Node.(ast.Node)
				if !ok {
					return visitor.ActionNoChange, nil
				}

				// Collect type information about the current position in the AST.
				typeInfo.Enter(node)

				action := visitor.ActionNoChange
				var result interface{}
				if instance.Enter != nil {
					action, result = instance.Enter(p)
				}

				// If the result is "false" (ie action === Action.Skip), we're not visiting any descendent nodes,
				// but need to update typeInfo.
				if action == visitor.ActionSkip {
					typeInfo.Leave(node)
				}

				return action, result
			},
			Leave: func(p visitor.VisitFuncParams) (string, interface{}) {
				node, ok := p.Node.(ast.Node)
				if !ok {
					return visitor.ActionNoChange, nil
				}

				var action = visitor.ActionNoChange
				var result interface{}
				if instance.Leave != nil {
					action, result = instance.Leave(p)
				}

				typeInfo.Leave(node)

				return action, result
			},
		})
	}

	for _, rule := range rules {
		visitInstance(astDoc, rule(context))
	}
	return context.Errors()
}

type HasSelectionSet interface {
	GetLoc() ast.Location
	GetSelectionSet() *ast.SelectionSet
}

var _ HasSelectionSet = (*ast.OperationDefinition)(nil)
var _ HasSelectionSet = (*ast.FragmentDefinition)(nil)

type VariableUsage struct {
	Node *ast.Variable
	Type Input
}

type ValidationContext struct {
	schema                         *Schema
	astDoc                         *ast.Document
	typeInfo                       *TypeInfo
	fragments                      map[string]*ast.FragmentDefinition
	variableUsages                 map[HasSelectionSet][]*VariableUsage
	recursiveVariableUsages        map[*ast.OperationDefinition][]*VariableUsage
	recursivelyReferencedFragments map[*ast.OperationDefinition][]*ast.FragmentDefinition
	fragmentSpreads                map[*ast.SelectionSet][]*ast.FragmentSpread
	errors                         []gqlerrors.FormattedError
}

func NewValidationContext(schema *Schema, astDoc *ast.Document, typeInfo *TypeInfo) *ValidationContext {
	return &ValidationContext{
		schema:                         schema,
		astDoc:                         astDoc,
		typeInfo:                       typeInfo,
		variableUsages:                 make(map[HasSelectionSet][]*VariableUsage),
		recursiveVariableUsages:        make(map[*ast.OperationDefinition][]*VariableUsage),
		recursivelyReferencedFragments: make(map[*ast.OperationDefinition][]*ast.FragmentDefinition),
		fragmentSpreads:                make(map[*ast.SelectionSet][]*ast.FragmentSpread),
	}
}

func (ctx *ValidationContext) ReportError(err error) {
	formattedErr := gqlerrors.FormatError(err)
	ctx.errors = append(ctx.errors, formattedErr)
}

func (ctx *ValidationContext) Errors() []gqlerrors.FormattedError {
	return ctx.errors
}

func (ctx *ValidationContext) Schema() *Schema {
	return ctx.schema
}
func (ctx *ValidationContext) Document() *ast.Document {
	return ctx.astDoc
}

func (ctx *ValidationContext) Fragment(name string) *ast.FragmentDefinition {
	if ctx.fragments == nil {
		if ctx.Document() == nil {
			return nil
		}
		defs := ctx.Document().Definitions
		fragments := make(map[string]*ast.FragmentDefinition)
		for _, def := range defs {
			if def, ok := def.(*ast.FragmentDefinition); ok {
				defName := ""
				if def.Name != nil {
					defName = def.Name.Value
				}
				fragments[defName] = def
			}
		}
		ctx.fragments = fragments
	}
	return ctx.fragments[name]
}

func (ctx *ValidationContext) FragmentSpreads(node *ast.SelectionSet) []*ast.FragmentSpread {
	if spreads, ok := ctx.fragmentSpreads[node]; ok && spreads != nil {
		return spreads
	}

	spreads := []*ast.FragmentSpread{}
	setsToVisit := []*ast.SelectionSet{node}

	for {
		if len(setsToVisit) == 0 {
			break
		}
		var set *ast.SelectionSet
		// pop
		set, setsToVisit = setsToVisit[len(setsToVisit)-1], setsToVisit[:len(setsToVisit)-1]
		if set.Selections != nil {
			for _, selection := range set.Selections {
				switch selection := selection.(type) {
				case *ast.FragmentSpread:
					spreads = append(spreads, selection)
				case *ast.Field:
					if selection.SelectionSet != nil {
						setsToVisit = append(setsToVisit, selection.SelectionSet)
					}
				case *ast.InlineFragment:
					if selection.SelectionSet != nil {
						setsToVisit = append(setsToVisit, selection.SelectionSet)
					}
				}
			}
		}
		ctx.fragmentSpreads[node] = spreads
	}
	return spreads
}

func (ctx *ValidationContext) RecursivelyReferencedFragments(operation *ast.OperationDefinition) []*ast.FragmentDefinition {
	if fragments, ok := ctx.recursivelyReferencedFragments[operation]; ok && fragments != nil {
		return fragments
	}

	fragments := []*ast.FragmentDefinition{}
	collectedNames := map[string]bool{}
	nodesToVisit := []*ast.SelectionSet{operation.SelectionSet}

	for {
		if len(nodesToVisit) == 0 {
			break
		}

		var node *ast.SelectionSet

		node, nodesToVisit = nodesToVisit[len(nodesToVisit)-1], nodesToVisit[:len(nodesToVisit)-1]
		spreads := ctx.FragmentSpreads(node)
		for _, spread := range spreads {
			fragName := ""
			if spread.Name != nil {
				fragName = spread.Name.Value
			}
			if res, ok := collectedNames[fragName]; !ok || !res {
				collectedNames[fragName] = true
				fragment := ctx.Fragment(fragName)
				if fragment != nil {
					fragments = append(fragments, fragment)
					nodesToVisit = append(nodesToVisit, fragment.SelectionSet)
				}
			}

		}
	}

	ctx.recursivelyReferencedFragments[operation] = fragments
	return fragments
}

func (ctx *ValidationContext) VariableUsages(node HasSelectionSet) []*VariableUsage {
	if usages, ok := ctx.variableUsages[node]; ok && usages != nil {
		return usages
	}
	typeInfo := NewTypeInfo(&TypeInfoConfig{
		Schema: ctx.schema,
	})

	var usages []*VariableUsage
	visitor.Visit(node, &visitor.VisitorOptions{
		Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
			if node, ok := p.Node.(ast.Node); ok {
				typeInfo.Enter(node)
				switch node := node.(type) {
				case *ast.VariableDefinition:
					typeInfo.Leave(node)
					return visitor.ActionSkip, nil
				case *ast.Variable:
					usages = append(usages, &VariableUsage{
						Node: node,
						Type: typeInfo.InputType(),
					})
				}
			}
			return visitor.ActionNoChange, nil
		},
		Leave: func(p visitor.VisitFuncParams) (string, interface{}) {
			if node, ok := p.Node.(ast.Node); ok {
				typeInfo.Leave(node)
			}
			return visitor.ActionNoChange, nil
		},
	})

	ctx.variableUsages[node] = usages
	return usages
}

func (ctx *ValidationContext) RecursiveVariableUsages(operation *ast.OperationDefinition) []*VariableUsage {
	if usages, ok := ctx.recursiveVariableUsages[operation]; ok && usages != nil {
		return usages
	}
	usages := ctx.VariableUsages(operation)

	fragments := ctx.RecursivelyReferencedFragments(operation)
	for _, fragment := range fragments {
		fragmentUsages := ctx.VariableUsages(fragment)
		usages = append(usages, fragmentUsages...)
	}

	ctx.recursiveVariableUsages[operation] = usages
	return usages
}

func (ctx *ValidationContext) Type() Output {
	return ctx.typeInfo.Type()
}
func (ctx *ValidationContext) ParentType() Composite {
	return ctx.typeInfo.ParentType()
}
func (ctx *ValidationContext) InputType() Input {
	return ctx.typeInfo.InputType()
}
func (ctx *ValidationContext) FieldDef() *FieldDefinition {
	return ctx.typeInfo.FieldDef()
}
func (ctx *ValidationContext) Directive() *Directive {
	return ctx.typeInfo.Directive()
}
func (ctx *ValidationContext) Argument() *Argument {
	return ctx.typeInfo.Argument()
}
<|endoftext|>


<commit_before>package reviewdog

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"

	"github.com/reviewdog/reviewdog/diff"
)

var X = 14

// Reviewdog represents review dog application which parses result of compiler
// or linter, get diff and filter the results by diff, and report filtered
// results.
type Reviewdog struct {
	toolname string
	p        Parser
	c        CommentService
	d        DiffService
}

// NewReviewdog returns a new Reviewdog.
func NewReviewdog(toolname string, p Parser, c CommentService, d DiffService) *Reviewdog {
	return &Reviewdog{p: p, c: c, d: d, toolname: toolname}
}

func RunFromResult(ctx context.Context, c CommentService, results []*CheckResult,
	filediffs []*diff.FileDiff, strip int, toolname string) error {
	return (&Reviewdog{c: c, toolname: toolname}).runFromResult(ctx, results, filediffs, strip)
}

// CheckResult represents a checked result of static analysis tools.
// :h error-file-format
type CheckResult struct {
	Path    string   // relative file path
	Lnum    int      // line number
	Col     int      // column number (1 <tab> == 1 character column)
	Message string   // error message
	Lines   []string // Original error lines (often one line)
}

// Parser is an interface which parses compilers, linters, or any tools
// results.
type Parser interface {
	Parse(r io.Reader) ([]*CheckResult, error)
}

// Comment represents a reported result as a comment.
type Comment struct {
	*CheckResult
	Body     string
	LnumDiff int
	ToolName string
}

// CommentService is an interface which posts Comment.
type CommentService interface {
	Post(context.Context, *Comment) error
}

// BulkCommentService posts comments all at once when Flush() is called.
// Flush() will be called at the end of reviewdog run.
type BulkCommentService interface {
	CommentService
	Flush(context.Context) error
}

// DiffService is an interface which get diff.
type DiffService interface {
	Diff(context.Context) ([]byte, error)
	Strip() int
}

func (w *Reviewdog) runFromResult(ctx context.Context, results []*CheckResult,
	filediffs []*diff.FileDiff, strip int) error {
	wd, err := os.Getwd()
	if err != nil {
		return err
	}

	checks := FilterCheck(results, filediffs, strip, wd)
	for _, check := range checks {
		if !check.InDiff {
			continue
		}
		comment := &Comment{
			CheckResult: check.CheckResult,
			Body:        check.Message, // TODO: format message
			LnumDiff:    check.LnumDiff,
			ToolName:    w.toolname,
		}
		if err := w.c.Post(ctx, comment); err != nil {
			return err
		}
	}

	if bulk, ok := w.c.(BulkCommentService); ok {
		return bulk.Flush(ctx)
	}

	return nil
}

// Run runs Reviewdog application.
func (w *Reviewdog) Run(ctx context.Context, r io.Reader) error {
	results, err := w.p.Parse(r)
	if err != nil {
		return fmt.Errorf("parse error: %v", err)
	}

	d, err := w.d.Diff(ctx)
	if err != nil {
		return fmt.Errorf("fail to get diff: %v", err)
	}

	filediffs, err := diff.ParseMultiFile(bytes.NewReader(d))
	if err != nil {
		return fmt.Errorf("fail to parse diff: %v", err)
	}

	return w.runFromResult(ctx, results, filediffs, w.d.Strip())
}
<commit_msg>Remove debug code<commit_after>package reviewdog

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"

	"github.com/reviewdog/reviewdog/diff"
)

// Reviewdog represents review dog application which parses result of compiler
// or linter, get diff and filter the results by diff, and report filtered
// results.
type Reviewdog struct {
	toolname string
	p        Parser
	c        CommentService
	d        DiffService
}

// NewReviewdog returns a new Reviewdog.
func NewReviewdog(toolname string, p Parser, c CommentService, d DiffService) *Reviewdog {
	return &Reviewdog{p: p, c: c, d: d, toolname: toolname}
}

func RunFromResult(ctx context.Context, c CommentService, results []*CheckResult,
	filediffs []*diff.FileDiff, strip int, toolname string) error {
	return (&Reviewdog{c: c, toolname: toolname}).runFromResult(ctx, results, filediffs, strip)
}

// CheckResult represents a checked result of static analysis tools.
// :h error-file-format
type CheckResult struct {
	Path    string   // relative file path
	Lnum    int      // line number
	Col     int      // column number (1 <tab> == 1 character column)
	Message string   // error message
	Lines   []string // Original error lines (often one line)
}

// Parser is an interface which parses compilers, linters, or any tools
// results.
type Parser interface {
	Parse(r io.Reader) ([]*CheckResult, error)
}

// Comment represents a reported result as a comment.
type Comment struct {
	*CheckResult
	Body     string
	LnumDiff int
	ToolName string
}

// CommentService is an interface which posts Comment.
type CommentService interface {
	Post(context.Context, *Comment) error
}

// BulkCommentService posts comments all at once when Flush() is called.
// Flush() will be called at the end of reviewdog run.
type BulkCommentService interface {
	CommentService
	Flush(context.Context) error
}

// DiffService is an interface which get diff.
type DiffService interface {
	Diff(context.Context) ([]byte, error)
	Strip() int
}

func (w *Reviewdog) runFromResult(ctx context.Context, results []*CheckResult,
	filediffs []*diff.FileDiff, strip int) error {
	wd, err := os.Getwd()
	if err != nil {
		return err
	}

	checks := FilterCheck(results, filediffs, strip, wd)
	for _, check := range checks {
		if !check.InDiff {
			continue
		}
		comment := &Comment{
			CheckResult: check.CheckResult,
			Body:        check.Message, // TODO: format message
			LnumDiff:    check.LnumDiff,
			ToolName:    w.toolname,
		}
		if err := w.c.Post(ctx, comment); err != nil {
			return err
		}
	}

	if bulk, ok := w.c.(BulkCommentService); ok {
		return bulk.Flush(ctx)
	}

	return nil
}

// Run runs Reviewdog application.
func (w *Reviewdog) Run(ctx context.Context, r io.Reader) error {
	results, err := w.p.Parse(r)
	if err != nil {
		return fmt.Errorf("parse error: %v", err)
	}

	d, err := w.d.Diff(ctx)
	if err != nil {
		return fmt.Errorf("fail to get diff: %v", err)
	}

	filediffs, err := diff.ParseMultiFile(bytes.NewReader(d))
	if err != nil {
		return fmt.Errorf("fail to parse diff: %v", err)
	}

	return w.runFromResult(ctx, results, filediffs, w.d.Strip())
}
<|endoftext|>


<commit_before>// Copyright 2018 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 volume

import (
	"errors"
	"sync"
	"testing"

	"barista.run/bar"
	"barista.run/base/value"
	"barista.run/outputs"
	testBar "barista.run/testing/bar"

	"golang.org/x/time/rate"
)

type testVolumeImpl struct {
	sync.Mutex
	error
	min, max, vol int64
	mute          bool
	volChan       chan int64
	muteChan      chan bool
}

func (t *testVolumeImpl) setVolume(vol int64) error {
	t.Lock()
	defer t.Unlock()
	if t.error != nil {
		return t.error
	}
	t.vol = vol
	return nil
}

func (t *testVolumeImpl) setMuted(mute bool) error {
	t.Lock()
	defer t.Unlock()
	if t.error != nil {
		return t.error
	}
	t.mute = mute
	return nil
}

func (t *testVolumeImpl) setError(e error) {
	t.Lock()
	defer t.Unlock()
	t.error = e
}

func (t *testVolumeImpl) worker(v *value.ErrorValue) {
	t.Lock()
	for {
		v.SetOrError(Volume{
			Min:        t.min,
			Max:        t.max,
			Vol:        t.vol,
			Mute:       t.mute,
			controller: t,
		}, t.error)
		t.Unlock()
		select {
		case newVol := <-t.volChan:
			t.Lock()
			t.vol = newVol
		case muted := <-t.muteChan:
			t.Lock()
			t.mute = muted
		}
	}
}

func TestModule(t *testing.T) {
	testBar.New(t)
	testImpl := &testVolumeImpl{
		min: 0, max: 50, vol: 40, mute: false,
		volChan: make(chan int64, 1), muteChan: make(chan bool, 1),
	}
	v := createModule(testImpl)

	testBar.Run(v)

	out := testBar.NextOutput("on start")
	out.AssertText([]string{"80%"})

	out.At(0).LeftClick()
	out = testBar.NextOutput("on click")
	out.AssertText([]string{"MUT"})

	out.At(0).LeftClick()
	testBar.AssertNoOutput("click within 20ms")

	// To speed up the tests.
	rateLimiter = rate.NewLimiter(rate.Inf, 0)

	out.At(0).Click(bar.Event{Button: bar.ScrollUp})
	out = testBar.NextOutput("on volume change")
	out.AssertText([]string{"MUT"}, "still muted")

	out.At(0).Click(bar.Event{Button: bar.ButtonLeft})
	out = testBar.NextOutput("on unmute")
	out.AssertText([]string{"82%"}, "volume value updated")

	testImpl.volChan <- -1
	out = testBar.NextOutput("exernal value update")
	out.AssertText([]string{"-1%"}, "vol < min")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	out = testBar.NextOutput("on volume change")
	out.AssertText([]string{"0%"}, "lower volume at <0%")

	testImpl.volChan <- 100
	out = testBar.NextOutput("exernal value update")
	out.AssertText([]string{"200%"}, "vol > max")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	out = testBar.NextOutput("on volume change")
	out.AssertText([]string{"100%"}, "raise volume at >100%")

	testImpl.setError(errors.New("foo"))

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	testBar.AssertNoOutput("error during volume change")

	out.At(0).Click(bar.Event{Button: bar.ButtonLeft})
	testBar.AssertNoOutput("error during mute")

	testImpl.setError(nil)

	v.Output(func(vol Volume) bar.Output {
		return outputs.Textf("%d<%d<%d (%v)", vol.Min, vol.Vol, vol.Max, vol.Mute).
			OnClick(func(e bar.Event) {
				switch e.Button {
				case bar.ButtonLeft:
					vol.SetVolume(0)
				case bar.ButtonMiddle:
					vol.SetVolume(25)
				case bar.ButtonRight:
					vol.SetVolume(50)
				case bar.ScrollDown:
					vol.SetMuted(true)
				case bar.ScrollUp:
					vol.SetMuted(false)
				}
			})
	})

	out = testBar.NextOutput("on output format change")

	out.At(0).Click(bar.Event{Button: bar.ButtonMiddle})
	out = testBar.NextOutput("on volume = 25")

	out.At(0).Click(bar.Event{Button: bar.ButtonMiddle})
	testBar.AssertNoOutput("volume already 25")

	out.At(0).Click(bar.Event{Button: bar.ScrollUp})
	testBar.AssertNoOutput("volume already unmuted")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	out = testBar.NextOutput("on mute")

	out.At(0).Click(bar.Event{Button: bar.ButtonMiddle})
	testBar.AssertNoOutput("volume already 25")

	testImpl.setError(errors.New("some error"))
	testImpl.muteChan <- true

	testBar.NextOutput("on error").AssertError()
}
<commit_msg>Allow -count > 1 for modules/volume test<commit_after>// Copyright 2018 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 volume

import (
	"errors"
	"sync"
	"testing"

	"barista.run/bar"
	"barista.run/base/value"
	"barista.run/outputs"
	testBar "barista.run/testing/bar"

	"golang.org/x/time/rate"
)

type testVolumeImpl struct {
	sync.Mutex
	error
	min, max, vol int64
	mute          bool
	volChan       chan int64
	muteChan      chan bool
}

func (t *testVolumeImpl) setVolume(vol int64) error {
	t.Lock()
	defer t.Unlock()
	if t.error != nil {
		return t.error
	}
	t.vol = vol
	return nil
}

func (t *testVolumeImpl) setMuted(mute bool) error {
	t.Lock()
	defer t.Unlock()
	if t.error != nil {
		return t.error
	}
	t.mute = mute
	return nil
}

func (t *testVolumeImpl) setError(e error) {
	t.Lock()
	defer t.Unlock()
	t.error = e
}

func (t *testVolumeImpl) worker(v *value.ErrorValue) {
	t.Lock()
	for {
		v.SetOrError(Volume{
			Min:        t.min,
			Max:        t.max,
			Vol:        t.vol,
			Mute:       t.mute,
			controller: t,
		}, t.error)
		t.Unlock()
		select {
		case newVol := <-t.volChan:
			t.Lock()
			t.vol = newVol
		case muted := <-t.muteChan:
			t.Lock()
			t.mute = muted
		}
	}
}

func TestModule(t *testing.T) {
	testBar.New(t)
	testImpl := &testVolumeImpl{
		min: 0, max: 50, vol: 40, mute: false,
		volChan: make(chan int64, 1), muteChan: make(chan bool, 1),
	}
	v := createModule(testImpl)

	testBar.Run(v)

	out := testBar.NextOutput("on start")
	out.AssertText([]string{"80%"})

	out.At(0).LeftClick()
	out = testBar.NextOutput("on click")
	out.AssertText([]string{"MUT"})

	out.At(0).LeftClick()
	testBar.AssertNoOutput("click within 20ms")

	oldRateLimiter := rateLimiter
	defer func() { rateLimiter = oldRateLimiter }()
	// To speed up the tests.
	rateLimiter = rate.NewLimiter(rate.Inf, 0)

	out.At(0).Click(bar.Event{Button: bar.ScrollUp})
	out = testBar.NextOutput("on volume change")
	out.AssertText([]string{"MUT"}, "still muted")

	out.At(0).Click(bar.Event{Button: bar.ButtonLeft})
	out = testBar.NextOutput("on unmute")
	out.AssertText([]string{"82%"}, "volume value updated")

	testImpl.volChan <- -1
	out = testBar.NextOutput("exernal value update")
	out.AssertText([]string{"-1%"}, "vol < min")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	out = testBar.NextOutput("on volume change")
	out.AssertText([]string{"0%"}, "lower volume at <0%")

	testImpl.volChan <- 100
	out = testBar.NextOutput("exernal value update")
	out.AssertText([]string{"200%"}, "vol > max")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	out = testBar.NextOutput("on volume change")
	out.AssertText([]string{"100%"}, "raise volume at >100%")

	testImpl.setError(errors.New("foo"))

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	testBar.AssertNoOutput("error during volume change")

	out.At(0).Click(bar.Event{Button: bar.ButtonLeft})
	testBar.AssertNoOutput("error during mute")

	testImpl.setError(nil)

	v.Output(func(vol Volume) bar.Output {
		return outputs.Textf("%d<%d<%d (%v)", vol.Min, vol.Vol, vol.Max, vol.Mute).
			OnClick(func(e bar.Event) {
				switch e.Button {
				case bar.ButtonLeft:
					vol.SetVolume(0)
				case bar.ButtonMiddle:
					vol.SetVolume(25)
				case bar.ButtonRight:
					vol.SetVolume(50)
				case bar.ScrollDown:
					vol.SetMuted(true)
				case bar.ScrollUp:
					vol.SetMuted(false)
				}
			})
	})

	out = testBar.NextOutput("on output format change")

	out.At(0).Click(bar.Event{Button: bar.ButtonMiddle})
	out = testBar.NextOutput("on volume = 25")

	out.At(0).Click(bar.Event{Button: bar.ButtonMiddle})
	testBar.AssertNoOutput("volume already 25")

	out.At(0).Click(bar.Event{Button: bar.ScrollUp})
	testBar.AssertNoOutput("volume already unmuted")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	out = testBar.NextOutput("on mute")

	out.At(0).Click(bar.Event{Button: bar.ButtonMiddle})
	testBar.AssertNoOutput("volume already 25")

	testImpl.setError(errors.New("some error"))
	testImpl.muteChan <- true

	testBar.NextOutput("on error").AssertError()
}
<|endoftext|>