<commit_before>package monitor

import (
	"fmt"
	"time"
	"encoding/json"
	"net/url"
	"reflect"
	"strings"
)

const (
	NameSpaceQceDocker = "qce/docker"
	NameSpaceQceCvm    = "qce/cvm"

	QCloudMonitorAPITimeTemplate = "2006-01-02 15:04:05"
)

type QCloudMonitorAPITime struct {
	time.Time
}

func (qmat *QCloudMonitorAPITime) EncodeStructWithPrefix(prefix string, val reflect.Value, v *url.Values) error {
	ret := fmt.Sprintf("%d-%d-%d %d:%d:%d", qmat.Year(), qmat.Month(), qmat.Day(), qmat.Hour(), qmat.Minute(), qmat.Second())
	v.Set(strings.TrimLeft(prefix, "."), ret)
	return nil
}

func (qmat *QCloudMonitorAPITime) MarshalJSON() ([]byte, error) {

	return []byte(
		fmt.Sprintf(
			"%d-%d-%d %d:%d:%d",
			qmat.Year(),
			qmat.Month(),
			qmat.Day(),
			qmat.Hour(),
			qmat.Minute(),
			qmat.Second()),
	), nil
}

func (qmat *QCloudMonitorAPITime) UnmarshalJSON(b []byte) error {
	var tmp string
	if err := json.Unmarshal(b, &tmp); err != nil {
		return nil
	}

	t, err := time.Parse(QCloudMonitorAPITimeTemplate, tmp)
	if err != nil {
		return err
	}
	qmat.Time = t

	return nil
}

type Response struct {
	Code     int    `json:"code"`
	Message  string `json:"message"`
	CodeDesc string `json:"codeDesc"`
}

type GetMonitorDataArgs struct {
	Namespace  string                `qcloud_arg:"namespace"`
	MetricName string                `qcloud_arg:"metricName"`
	Dimensions []Dimension           `qcloud_arg:"dimensions"`
	Period     *int                  `qcloud_arg:"period,omitempty"`
	StartTime  *QCloudMonitorAPITime `qcloud_arg:"startTime,omitempty"`
	EndTime    *QCloudMonitorAPITime `qcloud_arg:"endTime,omitempty"`
}

type Dimension struct {
	Name  string `qcloud_arg:"name"`
	Value string `qcloud_arg:"value"`
}

type GetMonitorDataResponse struct {
	StartTime  QCloudMonitorAPITime `json:"startTime"`
	EndTime    QCloudMonitorAPITime `json:"endTime"`
	MetricName string               `json:"metricName"`
	Period     int                  `json:"period"`
	DataPoints []float64            `json:"dataPoints"`
}

type BatchGetMonitorDataArgs struct {
	Namespace  string                `qcloud_arg:"namespace"`
	MetricName string                `qcloud_arg:"metricName"`
	Batch      []Batch               `qcloud_arg:"batch"`
	Period     *int                  `qcloud_arg:"period,omitempty"`
	StartTime  *QCloudMonitorAPITime `qcloud_arg:"startTime,omitempty"`
	EndTime    *QCloudMonitorAPITime `qcloud_arg:"endTime,omitempty"`
}

type Batch struct {
	Dimensions []Dimension `qcloud_arg:"dimensions"`
}

type BatchGetMonitorDataResponse struct {
	StartTime  QCloudMonitorAPITime `json:"startTime"`
	EndTime    QCloudMonitorAPITime `json:"endTime"`
	MetricName string               `json:"metricName"`
	Period     int                  `json:"period"`
	DataPoints map[string][]float64 `json:"dataPoints"`
}

func (client *Client) GetMonitorData(args *GetMonitorDataArgs) (*GetMonitorDataResponse, error) {
	response := &GetMonitorDataResponse{}
	err := client.Invoke("GetMonitorData", args, response)
	if err != nil {
		return &GetMonitorDataResponse{}, err
	}
	return response, nil
}

func (client *Client) BatchGetMonitorData(args *BatchGetMonitorDataArgs) (*BatchGetMonitorDataResponse, error) {
	response := &BatchGetMonitorDataResponse{}
	err := client.Invoke("GetMonitorData", args, response)
	if err != nil {
		return &BatchGetMonitorDataResponse{}, err
	}
	return response, nil
}
<commit_msg>fix period type in ret value<commit_after>package monitor

import (
	"encoding/json"
	"fmt"
	"net/url"
	"reflect"
	"strings"
	"time"
)

const (
	NameSpaceQceDocker = "qce/docker"
	NameSpaceQceCvm    = "qce/cvm"

	QCloudMonitorAPITimeTemplate = "2006-01-02 15:04:05"
)

type QCloudMonitorAPITime struct {
	time.Time
}

func (qmat *QCloudMonitorAPITime) EncodeStructWithPrefix(prefix string, val reflect.Value, v *url.Values) error {
	ret := fmt.Sprintf("%d-%d-%d %d:%d:%d", qmat.Year(), qmat.Month(), qmat.Day(), qmat.Hour(), qmat.Minute(), qmat.Second())
	v.Set(strings.TrimLeft(prefix, "."), ret)
	return nil
}

func (qmat *QCloudMonitorAPITime) MarshalJSON() ([]byte, error) {

	return []byte(
		fmt.Sprintf(
			"%d-%d-%d %d:%d:%d",
			qmat.Year(),
			qmat.Month(),
			qmat.Day(),
			qmat.Hour(),
			qmat.Minute(),
			qmat.Second()),
	), nil
}

func (qmat *QCloudMonitorAPITime) UnmarshalJSON(b []byte) error {
	var tmp string
	if err := json.Unmarshal(b, &tmp); err != nil {
		return nil
	}

	t, err := time.Parse(QCloudMonitorAPITimeTemplate, tmp)
	if err != nil {
		return err
	}
	qmat.Time = t

	return nil
}

type Response struct {
	Code     int    `json:"code"`
	Message  string `json:"message"`
	CodeDesc string `json:"codeDesc"`
}

type GetMonitorDataArgs struct {
	Namespace  string                `qcloud_arg:"namespace"`
	MetricName string                `qcloud_arg:"metricName"`
	Dimensions []Dimension           `qcloud_arg:"dimensions"`
	Period     *int                  `qcloud_arg:"period,omitempty"`
	StartTime  *QCloudMonitorAPITime `qcloud_arg:"startTime,omitempty"`
	EndTime    *QCloudMonitorAPITime `qcloud_arg:"endTime,omitempty"`
}

type Dimension struct {
	Name  string `qcloud_arg:"name"`
	Value string `qcloud_arg:"value"`
}

type GetMonitorDataResponse struct {
	StartTime  QCloudMonitorAPITime `json:"startTime"`
	EndTime    QCloudMonitorAPITime `json:"endTime"`
	MetricName string               `json:"metricName"`
	Period     string               `json:"period"`
	DataPoints []float64            `json:"dataPoints"`
}

type BatchGetMonitorDataArgs struct {
	Namespace  string                `qcloud_arg:"namespace"`
	MetricName string                `qcloud_arg:"metricName"`
	Batch      []Batch               `qcloud_arg:"batch"`
	Period     *int                  `qcloud_arg:"period,omitempty"`
	StartTime  *QCloudMonitorAPITime `qcloud_arg:"startTime,omitempty"`
	EndTime    *QCloudMonitorAPITime `qcloud_arg:"endTime,omitempty"`
}

type Batch struct {
	Dimensions []Dimension `qcloud_arg:"dimensions"`
}

type BatchGetMonitorDataResponse struct {
	StartTime  QCloudMonitorAPITime `json:"startTime"`
	EndTime    QCloudMonitorAPITime `json:"endTime"`
	MetricName string               `json:"metricName"`
	Period     string               `json:"period"`
	DataPoints map[string][]float64 `json:"dataPoints"`
}

func (client *Client) GetMonitorData(args *GetMonitorDataArgs) (*GetMonitorDataResponse, error) {
	response := &GetMonitorDataResponse{}
	err := client.Invoke("GetMonitorData", args, response)
	if err != nil {
		return &GetMonitorDataResponse{}, err
	}
	return response, nil
}

func (client *Client) BatchGetMonitorData(args *BatchGetMonitorDataArgs) (*BatchGetMonitorDataResponse, error) {
	response := &BatchGetMonitorDataResponse{}
	err := client.Invoke("GetMonitorData", args, response)
	if err != nil {
		return &BatchGetMonitorDataResponse{}, err
	}
	return response, nil
}
<|endoftext|>


<commit_before>//  Copyright (c) 2013 Couchbase, Inc.

package main

import "flag"
import "fmt"
import "log"
import "os"
import "time"
import "io/ioutil"

import "github.com/prataprc/goparsec"
import "github.com/prataprc/monster"
import "github.com/prataprc/monster/common"

var options struct {
	bagdir  string
	outfile string
	nonterm string
	seed    int
	count   int
	help    bool
	debug   bool
}

func argParse() (string, *os.File) {
	seed := time.Now().UTC().Second()
	flag.StringVar(&options.bagdir, "bagdir", "",
		"directory path containing bags")
	flag.StringVar(&options.nonterm, "nonterm", "",
		"evaluate the non-terminal")
	flag.IntVar(&options.seed, "seed", seed,
		"seed value")
	flag.IntVar(&options.count, "n", 1,
		"generate n combinations")
	flag.StringVar(&options.outfile, "o", "-",
		"specify an output file")
	flag.Parse()

	prodfile := flag.Args()[0]
	if prodfile == "" || options.help {
		usage()
		os.Exit(1)
	}

	var err error
	outfd := os.Stdout
	if options.outfile != "-" && options.outfile != "" {
		outfd, err = os.Create(options.outfile)
		if err != nil {
			log.Fatal(err)
		}
	}
	return prodfile, outfd
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] <production-file> \n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	prodfile, outfd := argParse()

	// read production-file
	text, err := ioutil.ReadFile(prodfile)
	if err != nil {
		log.Fatal(err)
	}
	// compile
	root := compile(parsec.NewScanner(text))
	scope := root.(common.Scope)
	nterms := scope["_nonterminals"].(common.NTForms)
	scope = monster.BuildContext(scope, uint64(options.seed), options.bagdir)
	scope["_prodfile"] = prodfile
	if options.nonterm != "" {
		for i := 0; i < options.count; i++ {
			val := evaluate("root", scope, nterms[options.nonterm])
			outtext := fmt.Sprintf("%v\n", val)
			if _, err := outfd.Write([]byte(outtext)); err != nil {
				log.Fatal(err)
			}
		}

	} else {
		// evaluate
		for i := 0; i < options.count; i++ {
			val := evaluate("root", scope, nterms["s"])
			outtext := fmt.Sprintf("%v\n", val)
			if _, err := outfd.Write([]byte(outtext)); err != nil {
				log.Fatal(err)
			}
		}
	}
}

func compile(s parsec.Scanner) parsec.ParsecNode {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("%v at %v", r, s.GetCursor())
		}
	}()
	root, _ := monster.Y(s)
	return root
}

func evaluate(name string, scope common.Scope, forms []*common.Form) interface{} {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("%v", r)
		}
	}()
	scope = scope.ApplyGlobalForms()
	return monster.EvalForms(name, scope, forms)
}
<commit_msg>Bug fix: use a new random seed every time building the context.<commit_after>//  Copyright (c) 2013 Couchbase, Inc.

package main

import "flag"
import "fmt"
import "log"
import "os"
import "time"
import "io/ioutil"

import "github.com/prataprc/goparsec"
import "github.com/prataprc/monster"
import "github.com/prataprc/monster/common"

var options struct {
	bagdir  string
	outfile string
	nonterm string
	seed    int
	count   int
	help    bool
	debug   bool
}

func argParse() (string, *os.File) {
	seed := time.Now().UTC().Second()
	flag.StringVar(&options.bagdir, "bagdir", "",
		"directory path containing bags")
	flag.StringVar(&options.nonterm, "nonterm", "",
		"evaluate the non-terminal")
	flag.IntVar(&options.seed, "seed", seed,
		"seed value")
	flag.IntVar(&options.count, "n", 1,
		"generate n combinations")
	flag.StringVar(&options.outfile, "o", "-",
		"specify an output file")
	flag.Parse()

	prodfile := flag.Args()[0]
	if prodfile == "" || options.help {
		usage()
		os.Exit(1)
	}

	var err error
	outfd := os.Stdout
	if options.outfile != "-" && options.outfile != "" {
		outfd, err = os.Create(options.outfile)
		if err != nil {
			log.Fatal(err)
		}
	}
	return prodfile, outfd
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] <production-file> \n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	prodfile, outfd := argParse()

	// read production-file
	text, err := ioutil.ReadFile(prodfile)
	if err != nil {
		log.Fatal(err)
	}
	// compile
	root := compile(parsec.NewScanner(text))
	scope := root.(common.Scope)
	nterms := scope["_nonterminals"].(common.NTForms)
	if options.nonterm != "" {
		for i := 0; i < options.count; i++ {
			scope = monster.BuildContext(scope, uint64(i), options.bagdir)
			scope["_prodfile"] = prodfile
			val := evaluate("root", scope, nterms[options.nonterm])
			outtext := fmt.Sprintf("%v\n", val)
			if _, err := outfd.Write([]byte(outtext)); err != nil {
				log.Fatal(err)
			}
		}

	} else {
		// evaluate
		for i := 0; i < options.count; i++ {
			scope = monster.BuildContext(scope, uint64(i), options.bagdir)
			scope["_prodfile"] = prodfile
			val := evaluate("root", scope, nterms["s"])
			outtext := fmt.Sprintf("%v\n", val)
			if _, err := outfd.Write([]byte(outtext)); err != nil {
				log.Fatal(err)
			}
		}
	}
}

func compile(s parsec.Scanner) parsec.ParsecNode {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("%v at %v", r, s.GetCursor())
		}
	}()
	root, _ := monster.Y(s)
	return root
}

func evaluate(name string, scope common.Scope, forms []*common.Form) interface{} {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("%v", r)
		}
	}()
	scope = scope.ApplyGlobalForms()
	return monster.EvalForms(name, scope, forms)
}
<|endoftext|>


<commit_before>// Copyright 2009 The GoMPD Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package mpd

import (
	"testing"
)

func localDial(t *testing.T) (cli *Client) {
	addr := "127.0.0.1:6600"
	cli, err := Dial("tcp", addr)
	if err != nil {
		t.Fatalf("Dial(%q) = %v, %s want PTR, nil", addr, cli, err)
	}
	return
}

func close(cli *Client, t *testing.T) {
	if err := cli.Close(); err != nil {
		t.Errorf("Client.Close() = %s need nil", err)
	}
}

func attrsEqual(left, right Attrs) bool {
	if len(left) != len(right) {
		return false
	}
	for key, lval := range left {
		if rval, ok := right[key]; !ok || lval != rval {
			return false
		}
	}
	return true
}

func TestPlaylistInfo(t *testing.T) {
	cli := localDial(t)
	defer close(cli, t)

	pls, err := cli.PlaylistInfo(-1, -1)
	if err != nil {
		// We can't use t.Fatalf because defer'ed calls won't run
		t.Errorf("Client.PlaylistInfo(-1, -1) = %v, %s need _, nil", pls, err)
		return
	}
	for i, song := range pls {
		if _, ok := song["file"]; !ok {
			t.Errorf(`PlaylistInfo: song %d has no "file" attribute`, i)
		}
		pls1, err := cli.PlaylistInfo(i, -1)
		if err != nil {
			t.Errorf("Client.PlaylistInfo(%d, -1) = %v, %s need _, nil", i, pls1, err)
		}
		if !attrsEqual(pls[i], pls1[0]) {
			t.Errorf("Inconsistent song attribute for song %d", i)
		}
	}
}

func TestCurrentSong(t *testing.T) {
	cli := localDial(t)
	defer close(cli, t)

	attrs, err := cli.CurrentSong()
	if err != nil {
		t.Errorf("Client.CurrentSong() = %v, %s need _, nil", attrs, err)
		return
	}
	if len(attrs) == 0 {
		return // no current song
	}
	_, ok := attrs["file"]
	if !ok {
		t.Errorf("current song (attrs=%v) has no file attribute", attrs)
		return
	}
}

func TestPing(t *testing.T) {
	cli := localDial(t)
	defer close(cli, t)

	err := cli.Ping()
	if err != nil {
		t.Errorf("Client.Ping failed: %s\n", err)
	}
}
<commit_msg>test: better detection of mpd server<commit_after>// Copyright 2009 The GoMPD Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package mpd

import (
	"os"
	"testing"
)

func localDial(t *testing.T) (cli *Client) {
	net := "unix"
	addr := os.Getenv("MPD_HOST")
	if addr == "" {
		addr = "localhost"
	}
	if addr[0] != '/' {
		net = "tcp"
		port := os.Getenv("MPD_PORT")
		if port == "" {
			port = "6600"
		}
		addr += ":" + port
	}
	cli, err := Dial(net, addr)
	if err != nil {
		t.Fatalf("Dial(%q) = %v, %s want PTR, nil", addr, cli, err)
	}
	return
}

func close(cli *Client, t *testing.T) {
	if err := cli.Close(); err != nil {
		t.Errorf("Client.Close() = %s need nil", err)
	}
}

func attrsEqual(left, right Attrs) bool {
	if len(left) != len(right) {
		return false
	}
	for key, lval := range left {
		if rval, ok := right[key]; !ok || lval != rval {
			return false
		}
	}
	return true
}

func TestPlaylistInfo(t *testing.T) {
	cli := localDial(t)
	defer close(cli, t)

	pls, err := cli.PlaylistInfo(-1, -1)
	if err != nil {
		// We can't use t.Fatalf because defer'ed calls won't run
		t.Errorf("Client.PlaylistInfo(-1, -1) = %v, %s need _, nil", pls, err)
		return
	}
	for i, song := range pls {
		if _, ok := song["file"]; !ok {
			t.Errorf(`PlaylistInfo: song %d has no "file" attribute`, i)
		}
		pls1, err := cli.PlaylistInfo(i, -1)
		if err != nil {
			t.Errorf("Client.PlaylistInfo(%d, -1) = %v, %s need _, nil", i, pls1, err)
		}
		if !attrsEqual(pls[i], pls1[0]) {
			t.Errorf("Inconsistent song attribute for song %d", i)
		}
	}
}

func TestCurrentSong(t *testing.T) {
	cli := localDial(t)
	defer close(cli, t)

	attrs, err := cli.CurrentSong()
	if err != nil {
		t.Errorf("Client.CurrentSong() = %v, %s need _, nil", attrs, err)
		return
	}
	if len(attrs) == 0 {
		return // no current song
	}
	_, ok := attrs["file"]
	if !ok {
		t.Errorf("current song (attrs=%v) has no file attribute", attrs)
		return
	}
}

func TestPing(t *testing.T) {
	cli := localDial(t)
	defer close(cli, t)

	err := cli.Ping()
	if err != nil {
		t.Errorf("Client.Ping failed: %s\n", err)
	}
}
<|endoftext|>


<commit_before>/*
Package mqmetric contains a set of routines common to several
commands used to export MQ metrics to different backend
storage mechanisms including Prometheus and InfluxDB.
*/
package mqmetric

/*
  Copyright (c) IBM Corporation 2016, 2018

  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.

   Contributors:
     Mark Taylor - Initial Contribution
*/

import (
	"fmt"
	ibmmq "github.com/ibm-messaging/mq-golang/ibmmq"
	"strings"
	"time"
)

var statusDummy = fmt.Sprintf("dummy")

/*
This file defines types and constructors for elements related to status
of MQ objects that are retrieved via polling commands such as DISPLAY CHSTATUS
*/

type StatusAttribute struct {
	Description string
	MetricName  string
	Pseudo      bool
	pcfAttr     int32
	squash      bool
	delta       bool
	index       int
	Values      map[string]*StatusValue
	prevValues  map[string]int64
}

type StatusSet struct {
	Attributes map[string]*StatusAttribute
}

// All we care about for attributes are ints and strings. Other complex
// PCF datatypes are not currently going to be returned through this mechanism
type StatusValue struct {
	IsInt64     bool
	ValueInt64  int64
	ValueString string
}

// Initialise with default values.
func newStatusAttribute(n string, d string, p int32) *StatusAttribute {
	s := new(StatusAttribute)
	s.MetricName = n
	s.Description = d
	s.pcfAttr = p
	s.squash = false
	s.delta = false
	s.index = -1
	s.Values = make(map[string]*StatusValue)
	s.prevValues = make(map[string]int64)
	s.Pseudo = false
	return s
}

func newPseudoStatusAttribute(n string, d string) *StatusAttribute {
	s := newStatusAttribute(n, d, -1)
	s.Pseudo = true
	return s
}

func newStatusValueInt64(v int64) *StatusValue {
	s := new(StatusValue)
	s.ValueInt64 = v
	s.IsInt64 = true
	return s
}

func newStatusValueString(v string) *StatusValue {
	s := new(StatusValue)
	s.ValueString = v
	s.IsInt64 = false
	return s
}

// Go uses an example-based method for formatting and parsing timestamps
// This layout matches the MQ PutDate and PutTime strings. An additional TZ
// may eventually have to be turned into a config parm. Note the "15" to indicate
// a 24-hour timestamp. There also seems to be two formats for the time layout comnig
// from MQ - TPSTATUS uses a colon format time, QSTATUS uses the dots.
const timeStampLayoutDot = "2006-01-02 15.04.05"
const timeStampLayoutColon = "2006-01-02 15:04:05"

// Convert the MQ Time and Date formats
func statusTimeDiff(now time.Time, d string, t string) int64 {
	var rc int64
	var err error
	var parsedT time.Time

	// If there's any error in parsing the timestamp - perhaps
	// the value has not been set yet, then just return 0
	rc = 0

	timeStampLayout := timeStampLayoutDot
	if len(d) == 10 && len(t) == 8 {
		if strings.Contains(t, ":") {
			timeStampLayout = timeStampLayoutColon
		}
		parsedT, err = time.ParseInLocation(timeStampLayout, d+" "+t, now.Location())
		if err == nil {
			diff := now.Sub(parsedT).Seconds() + tzOffsetSecs

			if diff < 0 { // Cannot have status from the future
				// TODO: Perhaps issue a one-time warning as it might indicate timezone offsets
				// are mismatched between the qmgr and this program
				diff = 0
			}
			rc = int64(diff)
		}
	}
	//fmt.Printf("statusTimeDiff d:%s t:%s diff:%d tzoffset: %f err:%v\n", d, t, rc, tzOffsetSecs, err)
	return rc
}

func statusClearReplyQ() {
	buf := make([]byte, 0)
	// Empty replyQ in case any left over from previous errors
	for ok := true; ok; {
		getmqmd := ibmmq.NewMQMD()
		gmo := ibmmq.NewMQGMO()
		gmo.Options = ibmmq.MQGMO_NO_SYNCPOINT
		gmo.Options |= ibmmq.MQGMO_FAIL_IF_QUIESCING
		gmo.Options |= ibmmq.MQGMO_NO_WAIT
		gmo.Options |= ibmmq.MQGMO_CONVERT
		gmo.Options |= ibmmq.MQGMO_ACCEPT_TRUNCATED_MSG
		_, err := statusReplyQObj.Get(getmqmd, gmo, buf)

		if err != nil && err.(*ibmmq.MQReturn).MQCC == ibmmq.MQCC_FAILED {
			ok = false
		}
	}
	return
}

// Create the control blocks needed to send an admin message to the command
// server. The caller of this function will complete the message contents
// with elements specific to the object type.
func statusSetCommandHeaders() (*ibmmq.MQMD, *ibmmq.MQPMO, *ibmmq.MQCFH, []byte) {
	cfh := ibmmq.NewMQCFH()
	cfh.Version = ibmmq.MQCFH_VERSION_3
	cfh.Type = ibmmq.MQCFT_COMMAND_XR

	putmqmd := ibmmq.NewMQMD()
	pmo := ibmmq.NewMQPMO()

	pmo.Options = ibmmq.MQPMO_NO_SYNCPOINT
	pmo.Options |= ibmmq.MQPMO_NEW_MSG_ID
	pmo.Options |= ibmmq.MQPMO_NEW_CORREL_ID
	pmo.Options |= ibmmq.MQPMO_FAIL_IF_QUIESCING

	putmqmd.Format = "MQADMIN"
	putmqmd.ReplyToQ = statusReplyQObj.Name
	putmqmd.MsgType = ibmmq.MQMT_REQUEST
	putmqmd.Report = ibmmq.MQRO_PASS_DISCARD_AND_EXPIRY

	buf := make([]byte, 0)

	return putmqmd, pmo, cfh, buf
}

// Get a reply from the command server, returning the buffer
// to be parsed. This function is called in a loop until
// it has returned allDone=true (with or without an error)
func statusGetReply() (*ibmmq.MQCFH, []byte, bool, error) {
	var offset int
	var cfh *ibmmq.MQCFH

	replyBuf := make([]byte, 10240)

	getmqmd := ibmmq.NewMQMD()
	gmo := ibmmq.NewMQGMO()
	gmo.Options = ibmmq.MQGMO_NO_SYNCPOINT
	gmo.Options |= ibmmq.MQGMO_FAIL_IF_QUIESCING
	gmo.Options |= ibmmq.MQGMO_WAIT
	gmo.Options |= ibmmq.MQGMO_CONVERT
	gmo.WaitInterval = 3 * 1000 // 3 seconds

	allDone := false
	datalen, err := statusReplyQObj.Get(getmqmd, gmo, replyBuf)
	if err == nil {
		cfh, offset = ibmmq.ReadPCFHeader(replyBuf)

		if cfh.Control == ibmmq.MQCFC_LAST {
			allDone = true
		}

		if cfh.Reason != ibmmq.MQRC_NONE {
			return cfh, nil, allDone, err
		}
		// Returned by z/OS qmgrs but are not interesting
		if cfh.Type == ibmmq.MQCFT_XR_SUMMARY || cfh.Type == ibmmq.MQCFT_XR_MSG {
			return cfh, nil, allDone, err
		}
	} else {
		fmt.Printf("StatusGetReply error : %v\n", err)
		return nil, nil, allDone, err
	}

	return cfh, replyBuf[offset:datalen], allDone, err
}

// Called in a loop for each PCF Parameter element returned from the command
// server messages. We can deal here with the various integer responses; string
// responses need to be handled in the object-specific caller.
func statusGetIntAttributes(s StatusSet, elem *ibmmq.PCFParameter, key string) bool {
	usableValue := false
	if elem.Type == ibmmq.MQCFT_INTEGER || elem.Type == ibmmq.MQCFT_INTEGER64 ||
		elem.Type == ibmmq.MQCFT_INTEGER_LIST || elem.Type == ibmmq.MQCFT_INTEGER64_LIST {
		usableValue = true
	}

	if !usableValue {
		return false
	}

	// Look at the Parameter and loop through all the possible status
	// attributes to find it.We don't break from the loop after finding a match
	// because there might be more than one attribute associated with the
	// attribute (in particular status/status_squash)
	for attr, _ := range s.Attributes {
		if s.Attributes[attr].pcfAttr == elem.Parameter {
			index := s.Attributes[attr].index

			// Some MQ responses (eg QTIME) are arrays which we need to split into
			// individual metrics which we do via the index field describing the
			// metric attribute.
			if index == -1 {
				v := elem.Int64Value[0]
				if s.Attributes[attr].delta {
					// If we have already got a value for this attribute and queue
					// then use it to create the delta. Otherwise make the initial
					// value 0.
					if prevVal, ok := s.Attributes[attr].prevValues[key]; ok {
						s.Attributes[attr].Values[key] = newStatusValueInt64(v - prevVal)
					} else {
						s.Attributes[attr].Values[key] = newStatusValueInt64(0)
					}
					s.Attributes[attr].prevValues[key] = v
				} else {
					// Return the actual number
					s.Attributes[attr].Values[key] = newStatusValueInt64(v)
				}
			} else {
				v := elem.Int64Value
				if s.Attributes[attr].delta {
					// If we have already got a value for this attribute and queue
					// then use it to create the delta. Otherwise make the initial
					// value 0.
					if prevVal, ok := s.Attributes[attr].prevValues[key]; ok {
						s.Attributes[attr].Values[key] = newStatusValueInt64(v[index] - prevVal)
					} else {
						s.Attributes[attr].Values[key] = newStatusValueInt64(0)
					}
					s.Attributes[attr].prevValues[key] = v[index]
				} else {
					// Return the actual number
					s.Attributes[attr].Values[key] = newStatusValueInt64(v[index])
				}
			}
		}
	}

	return true
}

// Common function to turn MQ integer value into a non-negative float. May
// be overridden in specific object types where special processing may be needed.
func statusNormalise(attr *StatusAttribute, v int64) float64 {
	f := float64(v)
	if f < 0 {
		f = 0
	}
	return f
}
<commit_msg>modify debug print statement<commit_after>/*
Package mqmetric contains a set of routines common to several
commands used to export MQ metrics to different backend
storage mechanisms including Prometheus and InfluxDB.
*/
package mqmetric

/*
  Copyright (c) IBM Corporation 2016, 2018

  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.

   Contributors:
     Mark Taylor - Initial Contribution
*/

import (
	"fmt"
	ibmmq "github.com/ibm-messaging/mq-golang/ibmmq"
	"strings"
	"time"
)

var statusDummy = fmt.Sprintf("dummy")

/*
This file defines types and constructors for elements related to status
of MQ objects that are retrieved via polling commands such as DISPLAY CHSTATUS
*/

type StatusAttribute struct {
	Description string
	MetricName  string
	Pseudo      bool
	pcfAttr     int32
	squash      bool
	delta       bool
	index       int
	Values      map[string]*StatusValue
	prevValues  map[string]int64
}

type StatusSet struct {
	Attributes map[string]*StatusAttribute
}

// All we care about for attributes are ints and strings. Other complex
// PCF datatypes are not currently going to be returned through this mechanism
type StatusValue struct {
	IsInt64     bool
	ValueInt64  int64
	ValueString string
}

// Initialise with default values.
func newStatusAttribute(n string, d string, p int32) *StatusAttribute {
	s := new(StatusAttribute)
	s.MetricName = n
	s.Description = d
	s.pcfAttr = p
	s.squash = false
	s.delta = false
	s.index = -1
	s.Values = make(map[string]*StatusValue)
	s.prevValues = make(map[string]int64)
	s.Pseudo = false
	return s
}

func newPseudoStatusAttribute(n string, d string) *StatusAttribute {
	s := newStatusAttribute(n, d, -1)
	s.Pseudo = true
	return s
}

func newStatusValueInt64(v int64) *StatusValue {
	s := new(StatusValue)
	s.ValueInt64 = v
	s.IsInt64 = true
	return s
}

func newStatusValueString(v string) *StatusValue {
	s := new(StatusValue)
	s.ValueString = v
	s.IsInt64 = false
	return s
}

// Go uses an example-based method for formatting and parsing timestamps
// This layout matches the MQ PutDate and PutTime strings. An additional TZ
// may eventually have to be turned into a config parm. Note the "15" to indicate
// a 24-hour timestamp. There also seems to be two formats for the time layout comnig
// from MQ - TPSTATUS uses a colon format time, QSTATUS uses the dots.
const timeStampLayoutDot = "2006-01-02 15.04.05"
const timeStampLayoutColon = "2006-01-02 15:04:05"

// Convert the MQ Time and Date formats
func statusTimeDiff(now time.Time, d string, t string) int64 {
	var rc int64
	var err error
	var parsedT time.Time

	// If there's any error in parsing the timestamp - perhaps
	// the value has not been set yet, then just return 0
	rc = 0

	timeStampLayout := timeStampLayoutDot
	if len(d) == 10 && len(t) == 8 {
		if strings.Contains(t, ":") {
			timeStampLayout = timeStampLayoutColon
		}
		parsedT, err = time.ParseInLocation(timeStampLayout, d+" "+t, now.Location())
		if err == nil {
			diff := now.Sub(parsedT).Seconds() + tzOffsetSecs

			if diff < 0 { // Cannot have status from the future
				// TODO: Perhaps issue a one-time warning as it might indicate timezone offsets
				// are mismatched between the qmgr and this program
				diff = 0
			}
			rc = int64(diff)
		}
	}
	//fmt.Printf("statusTimeDiff d:%s t:%s diff:%d tzoffset: %f err:%v\n", d, t, rc, tzOffsetSecs, err)
	return rc
}

func statusClearReplyQ() {
	buf := make([]byte, 0)
	// Empty replyQ in case any left over from previous errors
	for ok := true; ok; {
		getmqmd := ibmmq.NewMQMD()
		gmo := ibmmq.NewMQGMO()
		gmo.Options = ibmmq.MQGMO_NO_SYNCPOINT
		gmo.Options |= ibmmq.MQGMO_FAIL_IF_QUIESCING
		gmo.Options |= ibmmq.MQGMO_NO_WAIT
		gmo.Options |= ibmmq.MQGMO_CONVERT
		gmo.Options |= ibmmq.MQGMO_ACCEPT_TRUNCATED_MSG
		_, err := statusReplyQObj.Get(getmqmd, gmo, buf)

		if err != nil && err.(*ibmmq.MQReturn).MQCC == ibmmq.MQCC_FAILED {
			ok = false
		}
	}
	return
}

// Create the control blocks needed to send an admin message to the command
// server. The caller of this function will complete the message contents
// with elements specific to the object type.
func statusSetCommandHeaders() (*ibmmq.MQMD, *ibmmq.MQPMO, *ibmmq.MQCFH, []byte) {
	cfh := ibmmq.NewMQCFH()
	cfh.Version = ibmmq.MQCFH_VERSION_3
	cfh.Type = ibmmq.MQCFT_COMMAND_XR

	putmqmd := ibmmq.NewMQMD()
	pmo := ibmmq.NewMQPMO()

	pmo.Options = ibmmq.MQPMO_NO_SYNCPOINT
	pmo.Options |= ibmmq.MQPMO_NEW_MSG_ID
	pmo.Options |= ibmmq.MQPMO_NEW_CORREL_ID
	pmo.Options |= ibmmq.MQPMO_FAIL_IF_QUIESCING

	putmqmd.Format = "MQADMIN"
	putmqmd.ReplyToQ = statusReplyQObj.Name
	putmqmd.MsgType = ibmmq.MQMT_REQUEST
	putmqmd.Report = ibmmq.MQRO_PASS_DISCARD_AND_EXPIRY

	buf := make([]byte, 0)

	return putmqmd, pmo, cfh, buf
}

// Get a reply from the command server, returning the buffer
// to be parsed. This function is called in a loop until
// it has returned allDone=true (with or without an error)
func statusGetReply() (*ibmmq.MQCFH, []byte, bool, error) {
	var offset int
	var cfh *ibmmq.MQCFH

	replyBuf := make([]byte, 10240)

	getmqmd := ibmmq.NewMQMD()
	gmo := ibmmq.NewMQGMO()
	gmo.Options = ibmmq.MQGMO_NO_SYNCPOINT
	gmo.Options |= ibmmq.MQGMO_FAIL_IF_QUIESCING
	gmo.Options |= ibmmq.MQGMO_WAIT
	gmo.Options |= ibmmq.MQGMO_CONVERT
	gmo.WaitInterval = 3 * 1000 // 3 seconds

	allDone := false
	datalen, err := statusReplyQObj.Get(getmqmd, gmo, replyBuf)
	if err == nil {
		cfh, offset = ibmmq.ReadPCFHeader(replyBuf)

		if cfh.Control == ibmmq.MQCFC_LAST {
			allDone = true
		}

		if cfh.Reason != ibmmq.MQRC_NONE {
			return cfh, nil, allDone, err
		}
		// Returned by z/OS qmgrs but are not interesting
		if cfh.Type == ibmmq.MQCFT_XR_SUMMARY || cfh.Type == ibmmq.MQCFT_XR_MSG {
			return cfh, nil, allDone, err
		}
	} else {
		if err.(*ibmmq.MQReturn).MQRC != ibmmq.MQRC_NO_MSG_AVAILABLE {
		  fmt.Printf("StatusGetReply error : %v\n", err)
	  }
		return nil, nil, allDone, err
	}

	return cfh, replyBuf[offset:datalen], allDone, err
}

// Called in a loop for each PCF Parameter element returned from the command
// server messages. We can deal here with the various integer responses; string
// responses need to be handled in the object-specific caller.
func statusGetIntAttributes(s StatusSet, elem *ibmmq.PCFParameter, key string) bool {
	usableValue := false
	if elem.Type == ibmmq.MQCFT_INTEGER || elem.Type == ibmmq.MQCFT_INTEGER64 ||
		elem.Type == ibmmq.MQCFT_INTEGER_LIST || elem.Type == ibmmq.MQCFT_INTEGER64_LIST {
		usableValue = true
	}

	if !usableValue {
		return false
	}

	// Look at the Parameter and loop through all the possible status
	// attributes to find it.We don't break from the loop after finding a match
	// because there might be more than one attribute associated with the
	// attribute (in particular status/status_squash)
	for attr, _ := range s.Attributes {
		if s.Attributes[attr].pcfAttr == elem.Parameter {
			index := s.Attributes[attr].index

			// Some MQ responses (eg QTIME) are arrays which we need to split into
			// individual metrics which we do via the index field describing the
			// metric attribute.
			if index == -1 {
				v := elem.Int64Value[0]
				if s.Attributes[attr].delta {
					// If we have already got a value for this attribute and queue
					// then use it to create the delta. Otherwise make the initial
					// value 0.
					if prevVal, ok := s.Attributes[attr].prevValues[key]; ok {
						s.Attributes[attr].Values[key] = newStatusValueInt64(v - prevVal)
					} else {
						s.Attributes[attr].Values[key] = newStatusValueInt64(0)
					}
					s.Attributes[attr].prevValues[key] = v
				} else {
					// Return the actual number
					s.Attributes[attr].Values[key] = newStatusValueInt64(v)
				}
			} else {
				v := elem.Int64Value
				if s.Attributes[attr].delta {
					// If we have already got a value for this attribute and queue
					// then use it to create the delta. Otherwise make the initial
					// value 0.
					if prevVal, ok := s.Attributes[attr].prevValues[key]; ok {
						s.Attributes[attr].Values[key] = newStatusValueInt64(v[index] - prevVal)
					} else {
						s.Attributes[attr].Values[key] = newStatusValueInt64(0)
					}
					s.Attributes[attr].prevValues[key] = v[index]
				} else {
					// Return the actual number
					s.Attributes[attr].Values[key] = newStatusValueInt64(v[index])
				}
			}
		}
	}

	return true
}

// Common function to turn MQ integer value into a non-negative float. May
// be overridden in specific object types where special processing may be needed.
func statusNormalise(attr *StatusAttribute, v int64) float64 {
	f := float64(v)
	if f < 0 {
		f = 0
	}
	return f
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 mutable

import (
	"fmt"
	"math"
	"os"
	"time"

	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

// A mutable view on some content. Created with an initial read-only view,
// which then can be modified by the user and read back. Keeps track of which
// portion of the content has been dirtied.
//
// External synchronization is required.
type Content interface {
	// Panic if any internal invariants are violated.
	CheckInvariants()

	// Destroy any state used by the object, putting it into an indeterminate
	// state. The object must not be used again.
	Destroy()

	// Read part of the content, with semantics equivalent to io.ReaderAt aside
	// from context support.
	ReadAt(ctx context.Context, buf []byte, offset int64) (n int, err error)

	// Return information about the current state of the content.
	Stat(ctx context.Context) (sr StatResult, err error)

	// Write into the content, with semantics equivalent to io.WriterAt aside from
	// context support.
	WriteAt(ctx context.Context, buf []byte, offset int64) (n int, err error)

	// Truncate our the content to the given number of bytes, extending if n is
	// greater than the current size.
	Truncate(ctx context.Context, n int64) (err error)
}

type StatResult struct {
	// The current size in bytes of the content.
	Size int64

	// It is guaranteed that all bytes in the range [0, DirtyThreshold) are
	// unmodified from the original content with which the mutable content object
	// was created.
	DirtyThreshold int64

	// The time at which the content was last updated, or nil if we've never
	// changed it.
	Mtime *time.Time
}

// Create a mutable content object whose initial contents are given by the
// supplied file. The caller must ensure that the file is not used further by
// anybody else.
func NewContent(
	initialContent *os.File,
	clock timeutil.Clock) (mc Content, err error) {
	// Find the file's size.
	size, err := initialContent.Seek(0, 2)
	if err != nil {
		err = fmt.Errorf("Seek: %v", err)
		return
	}

	// Create the Content.
	mc = &mutableContent{
		clock:          clock,
		contents:       initialContent,
		dirtyThreshold: size,
	}

	return
}

type mutableContent struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	destroyed bool

	// Have we been dirtied since we were created?
	dirty bool

	// A file containing our current contents.
	contents *os.File

	// The lowest byte index that has been modified from the initial contents.
	//
	// INVARIANT: !dirty => Stat().DirtyThreshold == Stat().Size
	dirtyThreshold int64

	// The time at which a method that modifies our contents was last called, or
	// nil if never.
	//
	// INVARIANT: dirty => mtime != nil
	mtime *time.Time
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (mc *mutableContent) CheckInvariants() {
	if mc.destroyed {
		panic("Use of destroyed mutableContent object.")
	}

	// INVARIANT: !dirty => Stat().DirtyThreshold == Stat().Size
	if !mc.dirty {
		sr, err := mc.Stat(context.Background())
		if err != nil {
			panic(fmt.Sprintf("Stat: %v", err))
		}

		if sr.DirtyThreshold != sr.Size {
			panic(fmt.Sprintf("Mismatch: %d vs. %d", sr.DirtyThreshold, sr.Size))
		}
	}

	// INVARIANT: dirty => mtime != nil
	if mc.dirty && mc.mtime == nil {
		panic("Expected a non-nil mtime")
	}
}

func (mc *mutableContent) Destroy() {
	mc.destroyed = true

	// Throw away the file.
	mc.contents.Close()
	mc.contents = nil
}

func (mc *mutableContent) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	n, err = mc.contents.ReadAt(buf, offset)
	return
}

func (mc *mutableContent) Stat(
	ctx context.Context) (sr StatResult, err error) {
	sr.DirtyThreshold = mc.dirtyThreshold
	sr.Mtime = mc.mtime

	// Get the size from the appropriate place.
	if mc.dirty() {
		sr.Size, err = mc.readWriteLease.Size()
		if err != nil {
			return
		}
	} else {
		sr.Size = mc.initialContent.Size()
	}

	return
}

func (mc *mutableContent) WriteAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	// Make sure we have a read/write lease.
	if err = mc.ensureReadWriteLease(ctx); err != nil {
		err = fmt.Errorf("ensureReadWriteLease: %v", err)
		return
	}

	// Update our state regarding being dirty.
	mc.dirtyThreshold = minInt64(mc.dirtyThreshold, offset)

	newMtime := mc.clock.Now()
	mc.mtime = &newMtime

	// Call through.
	n, err = mc.readWriteLease.WriteAt(buf, offset)

	return
}

func (mc *mutableContent) Truncate(
	ctx context.Context,
	n int64) (err error) {
	// Make sure we have a read/write lease.
	if err = mc.ensureReadWriteLease(ctx); err != nil {
		err = fmt.Errorf("ensureReadWriteLease: %v", err)
		return
	}

	// Convert to signed, which is what lease.ReadWriteLease wants.
	if n > math.MaxInt64 {
		err = fmt.Errorf("Illegal offset: %v", n)
		return
	}

	// Update our state regarding being dirty.
	mc.dirtyThreshold = minInt64(mc.dirtyThreshold, n)

	newMtime := mc.clock.Now()
	mc.mtime = &newMtime

	// Call through.
	err = mc.readWriteLease.Truncate(int64(n))

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func minInt64(a int64, b int64) int64 {
	if a < b {
		return a
	}

	return b
}

// Ensure that mc.readWriteLease is non-nil with an authoritative view of mc's
// contents.
func (mc *mutableContent) ensureReadWriteLease(
	ctx context.Context) (err error) {
	// Is there anything to do?
	if mc.readWriteLease != nil {
		return
	}

	// Set up the read/write lease.
	rwl, err := mc.initialContent.Upgrade(ctx)
	if err != nil {
		err = fmt.Errorf("initialContent.Upgrade: %v", err)
		return
	}

	mc.readWriteLease = rwl
	mc.initialContent = nil

	return
}
<commit_msg>mutableContent.Stat<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 mutable

import (
	"fmt"
	"math"
	"os"
	"time"

	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

// A mutable view on some content. Created with an initial read-only view,
// which then can be modified by the user and read back. Keeps track of which
// portion of the content has been dirtied.
//
// External synchronization is required.
type Content interface {
	// Panic if any internal invariants are violated.
	CheckInvariants()

	// Destroy any state used by the object, putting it into an indeterminate
	// state. The object must not be used again.
	Destroy()

	// Read part of the content, with semantics equivalent to io.ReaderAt aside
	// from context support.
	ReadAt(ctx context.Context, buf []byte, offset int64) (n int, err error)

	// Return information about the current state of the content.
	Stat(ctx context.Context) (sr StatResult, err error)

	// Write into the content, with semantics equivalent to io.WriterAt aside from
	// context support.
	WriteAt(ctx context.Context, buf []byte, offset int64) (n int, err error)

	// Truncate our the content to the given number of bytes, extending if n is
	// greater than the current size.
	Truncate(ctx context.Context, n int64) (err error)
}

type StatResult struct {
	// The current size in bytes of the content.
	Size int64

	// It is guaranteed that all bytes in the range [0, DirtyThreshold) are
	// unmodified from the original content with which the mutable content object
	// was created.
	DirtyThreshold int64

	// The time at which the content was last updated, or nil if we've never
	// changed it.
	Mtime *time.Time
}

// Create a mutable content object whose initial contents are given by the
// supplied file. The caller must ensure that the file is not used further by
// anybody else.
func NewContent(
	initialContent *os.File,
	clock timeutil.Clock) (mc Content, err error) {
	// Find the file's size.
	size, err := initialContent.Seek(0, 2)
	if err != nil {
		err = fmt.Errorf("Seek: %v", err)
		return
	}

	// Create the Content.
	mc = &mutableContent{
		clock:          clock,
		contents:       initialContent,
		dirtyThreshold: size,
	}

	return
}

type mutableContent struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	destroyed bool

	// Have we been dirtied since we were created?
	dirty bool

	// A file containing our current contents.
	contents *os.File

	// The lowest byte index that has been modified from the initial contents.
	//
	// INVARIANT: !dirty => Stat().DirtyThreshold == Stat().Size
	dirtyThreshold int64

	// The time at which a method that modifies our contents was last called, or
	// nil if never.
	//
	// INVARIANT: dirty => mtime != nil
	mtime *time.Time
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (mc *mutableContent) CheckInvariants() {
	if mc.destroyed {
		panic("Use of destroyed mutableContent object.")
	}

	// INVARIANT: !dirty => Stat().DirtyThreshold == Stat().Size
	if !mc.dirty {
		sr, err := mc.Stat(context.Background())
		if err != nil {
			panic(fmt.Sprintf("Stat: %v", err))
		}

		if sr.DirtyThreshold != sr.Size {
			panic(fmt.Sprintf("Mismatch: %d vs. %d", sr.DirtyThreshold, sr.Size))
		}
	}

	// INVARIANT: dirty => mtime != nil
	if mc.dirty && mc.mtime == nil {
		panic("Expected a non-nil mtime")
	}
}

func (mc *mutableContent) Destroy() {
	mc.destroyed = true

	// Throw away the file.
	mc.contents.Close()
	mc.contents = nil
}

func (mc *mutableContent) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	n, err = mc.contents.ReadAt(buf, offset)
	return
}

func (mc *mutableContent) Stat(
	ctx context.Context) (sr StatResult, err error) {
	sr.DirtyThreshold = mc.dirtyThreshold
	sr.Mtime = mc.mtime

	// Get the size from the file.
	sr.Size, err = mc.contents.Seek(0, 2)
	if err != nil {
		err = fmt.Errorf("Seek: %v", err)
		return
	}

	return
}

func (mc *mutableContent) WriteAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	// Make sure we have a read/write lease.
	if err = mc.ensureReadWriteLease(ctx); err != nil {
		err = fmt.Errorf("ensureReadWriteLease: %v", err)
		return
	}

	// Update our state regarding being dirty.
	mc.dirtyThreshold = minInt64(mc.dirtyThreshold, offset)

	newMtime := mc.clock.Now()
	mc.mtime = &newMtime

	// Call through.
	n, err = mc.readWriteLease.WriteAt(buf, offset)

	return
}

func (mc *mutableContent) Truncate(
	ctx context.Context,
	n int64) (err error) {
	// Make sure we have a read/write lease.
	if err = mc.ensureReadWriteLease(ctx); err != nil {
		err = fmt.Errorf("ensureReadWriteLease: %v", err)
		return
	}

	// Convert to signed, which is what lease.ReadWriteLease wants.
	if n > math.MaxInt64 {
		err = fmt.Errorf("Illegal offset: %v", n)
		return
	}

	// Update our state regarding being dirty.
	mc.dirtyThreshold = minInt64(mc.dirtyThreshold, n)

	newMtime := mc.clock.Now()
	mc.mtime = &newMtime

	// Call through.
	err = mc.readWriteLease.Truncate(int64(n))

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func minInt64(a int64, b int64) int64 {
	if a < b {
		return a
	}

	return b
}

// Ensure that mc.readWriteLease is non-nil with an authoritative view of mc's
// contents.
func (mc *mutableContent) ensureReadWriteLease(
	ctx context.Context) (err error) {
	// Is there anything to do?
	if mc.readWriteLease != nil {
		return
	}

	// Set up the read/write lease.
	rwl, err := mc.initialContent.Upgrade(ctx)
	if err != nil {
		err = fmt.Errorf("initialContent.Upgrade: %v", err)
		return
	}

	mc.readWriteLease = rwl
	mc.initialContent = nil

	return
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"testing"

	driver "github.com/arangodb/go-driver"
)

// TestReplicationDatabaseInventory tests the Replication.DatabaseInventory method.
func TestReplicationDatabaseInventory(t *testing.T) {
	ctx := context.Background()
	c := createClientFromEnv(t, true)
	if _, err := c.Cluster(ctx); err == nil {
		// Cluster, not supported for this test
		t.Skip("Skipping in cluster")
	} else if !driver.IsPreconditionFailed(err) {
		t.Errorf("Failed to query cluster: %s", describe(err))
	} else {
		// Single server (what we need)
		rep := c.Replication()
		db, err := c.Database(ctx, "_system")
		if err != nil {
			t.Fatalf("Failed to open _system database: %s", describe(err))
		}

		// TODO remove the batchID requirement from rocksdb
		batch, err := rep.CreateBatch(ctx, 1337, db)
		if err != nil {
			t.Fatalf("CreateBatch failed: %s", describe(err))
		}
		defer rep.DeleteBatch(ctx, db, batch.ID)

		ctx2 := driver.WithBatchID(ctx, batch.ID)

		inv, err := rep.DatabaseInventory(ctx2, db)
		if err != nil {
			t.Fatalf("DatabaseInventory failed: %s", describe(err))
		}
		if len(inv.Collections) == 0 {
			t.Error("Expected multiple collections, got 0")
		}
		foundSystemCol := false
		for _, col := range inv.Collections {
			if col.Parameters.Name == "" {
				t.Error("Expected non-empty name")
			}
			if col.Parameters.IsSystem {
				foundSystemCol = true
			}
		}
		if !foundSystemCol {
			t.Error("Expected multiple system collections, found none")
		}
	}
}
<commit_msg>Only use batchId on >= 3.2<commit_after>//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"testing"

	driver "github.com/arangodb/go-driver"
)

// TestReplicationDatabaseInventory tests the Replication.DatabaseInventory method.
func TestReplicationDatabaseInventory(t *testing.T) {
	ctx := context.Background()
	c := createClientFromEnv(t, true)
	if _, err := c.Cluster(ctx); err == nil {
		// Cluster, not supported for this test
		t.Skip("Skipping in cluster")
	} else if !driver.IsPreconditionFailed(err) {
		t.Errorf("Failed to query cluster: %s", describe(err))
	} else {
		// Single server (what we need)
		rep := c.Replication()
		db, err := c.Database(ctx, "_system")
		if err != nil {
			t.Fatalf("Failed to open _system database: %s", describe(err))
		}

		version, err := c.Version(nil)
		if err != nil {
			t.Fatalf("Version failed: %s", describe(err))
		}

		ctx_inv := ctx
		if version.Version.CompareTo("3.2") >= 0 {
			// RocksDB requires batchID
			batch, err := rep.CreateBatch(ctx, 1337, db)
			if err != nil {
				t.Fatalf("CreateBatch failed: %s", describe(err))
			}
			ctx_inv = driver.WithBatchID(ctx, batch.ID)
			defer rep.DeleteBatch(ctx, db, batch.ID)
		}

		inv, err := rep.DatabaseInventory(ctx_inv, db)
		if err != nil {
			t.Fatalf("DatabaseInventory failed: %s", describe(err))
		}
		if len(inv.Collections) == 0 {
			t.Error("Expected multiple collections, got 0")
		}
		foundSystemCol := false
		for _, col := range inv.Collections {
			if col.Parameters.Name == "" {
				t.Error("Expected non-empty name")
			}
			if col.Parameters.IsSystem {
				foundSystemCol = true
			}
		}
		if !foundSystemCol {
			t.Error("Expected multiple system collections, found none")
		}
	}
}
<|endoftext|>


<commit_before>package client

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"strings"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/api/api/proto"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/types"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// AddVote adds a new vote to a release. If isCocoonID is true,
// the id is taken to be a cocoon id and as such the vote is added
// to the latest release. A positive vote is denoted with 1 or 0 for
// negative.
func AddVote(id, vote string, isCocoonID bool) error {

	userSession, err := GetUserSessionToken()
	if err != nil {
		return err
	}

	var releaseID = id
	var cocoon types.Cocoon

	stopSpinner := util.Spinner("Please wait")
	defer stopSpinner()
	conn, err := grpc.Dial(APIAddress, grpc.WithInsecure())
	if err != nil {
		stopSpinner()
		return fmt.Errorf("unable to connect to cluster. please try again")
	}
	defer conn.Close()

	client := proto.NewAPIClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	defer cancel()
	ctx = metadata.NewContext(ctx, metadata.Pairs("access_token", userSession.Token))

	// if id is a cocoon id, get the cocoon's most recent release
	if isCocoonID {

		resp, err := client.GetCocoon(ctx, &proto.GetCocoonRequest{ID: id})
		if err != nil {
			stopSpinner()
			if common.CompareErr(err, types.ErrCocoonNotFound) == 0 {
				return fmt.Errorf("%s: cocoon does not exists", common.GetShortID(id))
			}
			return err
		}

		util.FromJSON(resp.Body, &cocoon)

		// set id to latest release
		releaseID = cocoon.Releases[len(cocoon.Releases)-1]
	}

	stopSpinner()
	time.Sleep(100 * time.Millisecond)

	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Printf("Are you sure you? Y/n: ")
		text, _ := reader.ReadString('\n')
		v := strings.TrimSpace(strings.ToLower(text))
		if v == "n" {
			log.Info("Aborted")
			return nil
		}
		if v == "y" {
			break
		}
	}

	stopSpinner = util.Spinner("Please wait")

	_, err = client.AddVote(ctx, &proto.AddVoteRequest{
		ReleaseID: releaseID,
		Vote:      vote,
	})
	if err != nil {
		stopSpinner()
		if common.CompareErr(err, fmt.Errorf("release not found")) == 0 {
			return fmt.Errorf("release (%s) was not found", common.GetShortID(releaseID))
		}
		return err
	}

	stopSpinner()

	voteTxt := ""
	switch vote {
	case "1":
		voteTxt = "Approve"
	case "0":
		voteTxt = "Deny"
	}

	log.Info("==> You have successfully voted")
	log.Infof("==> Your vote: %s (%s)", vote, voteTxt)

	return nil
}

// GetReleases fetches one or more releases and logs them
func GetReleases(ids []string) error {

	if len(ids) > MaxBulkObjCount {
		return fmt.Errorf("max number of objects exceeded. Expects a maximum of %d", MaxBulkObjCount)
	}

	var releases []types.Release
	var err error
	var resp *proto.Response
	conn, err := grpc.Dial(APIAddress, grpc.WithInsecure())
	if err != nil {
		return fmt.Errorf("unable to connect to cluster. please try again")
	}
	defer conn.Close()

	for _, id := range ids {
		stopSpinner := util.Spinner("Please wait")
		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
		defer cancel()
		cl := proto.NewAPIClient(conn)
		resp, err = cl.GetRelease(ctx, &proto.GetReleaseRequest{
			ID: id,
		})
		if err != nil {
			if common.CompareErr(err, types.ErrTxNotFound) == 0 {
				stopSpinner()
				err = fmt.Errorf("No such object: %s", id)
				break
			}
			stopSpinner()
			break
		}

		var release types.Release
		if err = util.FromJSON(resp.Body, &release); err != nil {
			return common.JSONCoerceErr("cocoon", err)
		}

		releases = append(releases, release)
		stopSpinner()
	}

	bs, _ := json.MarshalIndent(releases, "", "   ")
	log.Infof("%s", bs)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>check if length is more than 0<commit_after>package client

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"strings"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/api/api/proto"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/types"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// AddVote adds a new vote to a release. If isCocoonID is true,
// the id is taken to be a cocoon id and as such the vote is added
// to the latest release. A positive vote is denoted with 1 or 0 for
// negative.
func AddVote(id, vote string, isCocoonID bool) error {

	userSession, err := GetUserSessionToken()
	if err != nil {
		return err
	}

	var releaseID = id
	var cocoon types.Cocoon

	stopSpinner := util.Spinner("Please wait")
	defer stopSpinner()
	conn, err := grpc.Dial(APIAddress, grpc.WithInsecure())
	if err != nil {
		stopSpinner()
		return fmt.Errorf("unable to connect to cluster. please try again")
	}
	defer conn.Close()

	client := proto.NewAPIClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	defer cancel()
	ctx = metadata.NewContext(ctx, metadata.Pairs("access_token", userSession.Token))

	// if id is a cocoon id, get the cocoon's most recent release
	if isCocoonID {

		resp, err := client.GetCocoon(ctx, &proto.GetCocoonRequest{ID: id})
		if err != nil {
			stopSpinner()
			if common.CompareErr(err, types.ErrCocoonNotFound) == 0 {
				return fmt.Errorf("%s: cocoon does not exists", common.GetShortID(id))
			}
			return err
		}

		util.FromJSON(resp.Body, &cocoon)

		// set id to latest release
		releaseID = cocoon.Releases[len(cocoon.Releases)-1]
	}

	stopSpinner()
	time.Sleep(100 * time.Millisecond)

	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Printf("Are you sure you? Y/n: ")
		text, _ := reader.ReadString('\n')
		v := strings.TrimSpace(strings.ToLower(text))
		if v == "n" {
			log.Info("Aborted")
			return nil
		}
		if v == "y" {
			break
		}
	}

	stopSpinner = util.Spinner("Please wait")

	_, err = client.AddVote(ctx, &proto.AddVoteRequest{
		ReleaseID: releaseID,
		Vote:      vote,
	})
	if err != nil {
		stopSpinner()
		if common.CompareErr(err, fmt.Errorf("release not found")) == 0 {
			return fmt.Errorf("release (%s) was not found", common.GetShortID(releaseID))
		}
		return err
	}

	stopSpinner()

	voteTxt := ""
	switch vote {
	case "1":
		voteTxt = "Approve"
	case "0":
		voteTxt = "Deny"
	}

	log.Info("==> You have successfully voted")
	log.Infof("==> Your vote: %s (%s)", vote, voteTxt)

	return nil
}

// GetReleases fetches one or more releases and logs them
func GetReleases(ids []string) error {

	if len(ids) > MaxBulkObjCount {
		return fmt.Errorf("max number of objects exceeded. Expects a maximum of %d", MaxBulkObjCount)
	}

	var releases []types.Release
	var err error
	var resp *proto.Response
	conn, err := grpc.Dial(APIAddress, grpc.WithInsecure())
	if err != nil {
		return fmt.Errorf("unable to connect to cluster. please try again")
	}
	defer conn.Close()

	for _, id := range ids {
		stopSpinner := util.Spinner("Please wait")
		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
		defer cancel()
		cl := proto.NewAPIClient(conn)
		resp, err = cl.GetRelease(ctx, &proto.GetReleaseRequest{
			ID: id,
		})
		if err != nil {
			if common.CompareErr(err, types.ErrTxNotFound) == 0 {
				stopSpinner()
				err = fmt.Errorf("No such object: %s", id)
				break
			}
			stopSpinner()
			break
		}

		var release types.Release
		if err = util.FromJSON(resp.Body, &release); err != nil {
			return common.JSONCoerceErr("cocoon", err)
		}

		releases = append(releases, release)
		stopSpinner()
	}

	if len(releases) > 0 {
		bs, _ := json.MarshalIndent(releases, "", "   ")
		log.Infof("%s", bs)
	}
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package testing

import (
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"
)

func testFilesFor(t testing.TB, dirname, prefix string) []string {
	files, err := ioutil.ReadDir(dirname)

	if err != nil {
		t.Fatal(err)
	}

	testFiles := make([]string, 0)

	for _, file := range files {

		if file.IsDir() {
			continue
		}

		if !strings.HasSuffix(file.Name(), ".json") {
			continue
		}

		if !strings.HasPrefix(file.Name(), formattedPrefix(prefix)) {
			continue
		}

		if !isTestCaseAllowed(file.Name()) {
			continue
		}

		if !file.IsDir() {
			testFiles = append(testFiles, filepath.Join(dirname, file.Name()))
		}

	}

	return testFiles
}

func formattedPrefix(prefix string) string {
	return strings.ToLower(prefix)
}
<commit_msg>use rgx match instead of prefix based topic lookup for test cases list generation<commit_after>package testing

import (
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
)

func testFilesFor(t testing.TB, dirname, topic string) []string {
	files, err := ioutil.ReadDir(dirname)

	if err != nil {
		t.Fatal(err)
	}

	testFiles := make([]string, 0)

	for _, file := range files {

		if file.IsDir() {
			continue
		}

		if !strings.HasSuffix(file.Name(), ".json") {
			continue
		}

		rgx := regexp.MustCompile(regexp.QuoteMeta(formattedPrefix(topic)))

		if !rgx.MatchString(file.Name()) {
			continue
		}

		if !isTestCaseAllowed(file.Name()) {
			continue
		}

		if !file.IsDir() {
			testFiles = append(testFiles, filepath.Join(dirname, file.Name()))
		}

	}

	return testFiles
}

func formattedPrefix(prefix string) string {
	return strings.ToLower(prefix)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/conformal/goleveldb/leveldb/opt"
	"github.com/conformal/goleveldb/leveldb/testutil"
)

var _ = testutil.Defer(func() {
	Describe("Leveldb external", func() {
		o := &opt.Options{
			BlockCache:           opt.NoCache,
			BlockRestartInterval: 5,
			BlockSize:            50,
			Compression:          opt.NoCompression,
			MaxOpenFiles:         0,
			Strict:               opt.StrictAll,
			WriteBuffer:          1000,
		}

		Describe("write test", func() {
			It("should do write correctly", func(done Done) {
				db := newTestingDB(o, nil, nil)
				t := testutil.DBTesting{
					DB:      db,
					Deleted: testutil.KeyValue_Generate(nil, 500, 1, 50, 5, 5).Clone(),
				}
				testutil.DoDBTesting(&t)
				db.TestClose()
				done <- true
			}, 9.0)
		})

		Describe("read test", func() {
			testutil.AllKeyValueTesting(nil, func(kv testutil.KeyValue) testutil.DB {
				// Building the DB.
				db := newTestingDB(o, nil, nil)
				kv.IterateShuffled(nil, func(i int, key, value []byte) {
					err := db.TestPut(key, value)
					Expect(err).NotTo(HaveOccurred())
				})
				testutil.Defer("teardown", func() {
					db.TestClose()
				})

				return db
			})
		})
	})
})
<commit_msg>leveldb: Increase 'write test' timeout to 20 secs<commit_after>// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/conformal/goleveldb/leveldb/opt"
	"github.com/conformal/goleveldb/leveldb/testutil"
)

var _ = testutil.Defer(func() {
	Describe("Leveldb external", func() {
		o := &opt.Options{
			BlockCache:           opt.NoCache,
			BlockRestartInterval: 5,
			BlockSize:            50,
			Compression:          opt.NoCompression,
			MaxOpenFiles:         0,
			Strict:               opt.StrictAll,
			WriteBuffer:          1000,
		}

		Describe("write test", func() {
			It("should do write correctly", func(done Done) {
				db := newTestingDB(o, nil, nil)
				t := testutil.DBTesting{
					DB:      db,
					Deleted: testutil.KeyValue_Generate(nil, 500, 1, 50, 5, 5).Clone(),
				}
				testutil.DoDBTesting(&t)
				db.TestClose()
				done <- true
			}, 20.0)
		})

		Describe("read test", func() {
			testutil.AllKeyValueTesting(nil, func(kv testutil.KeyValue) testutil.DB {
				// Building the DB.
				db := newTestingDB(o, nil, nil)
				kv.IterateShuffled(nil, func(i int, key, value []byte) {
					err := db.TestPut(key, value)
					Expect(err).NotTo(HaveOccurred())
				})
				testutil.Defer("teardown", func() {
					db.TestClose()
				})

				return db
			})
		})
	})
})
<|endoftext|>


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

// Most of the code in this file was initially borrowed from the Go
// standard library and modified; It had this copyright notice:
// Copyright 2011 The Go Authors

package reverseproxy

import (
	"context"
	"io"
	"mime"
	"net/http"
	"sync"
	"time"

	"go.uber.org/zap"
)

func (h Handler) handleUpgradeResponse(rw http.ResponseWriter, req *http.Request, res *http.Response) {
	reqUpType := upgradeType(req.Header)
	resUpType := upgradeType(res.Header)
	if reqUpType != resUpType {
		// TODO: figure out our own error handling
		// p.getErrorHandler()(rw, req, fmt.Errorf("backend tried to switch protocol %q when %q was requested", resUpType, reqUpType))
		return
	}

	copyHeader(res.Header, rw.Header())

	hj, ok := rw.(http.Hijacker)
	if !ok {
		// p.getErrorHandler()(rw, req, fmt.Errorf("can't switch protocols using non-Hijacker ResponseWriter type %T", rw))
		return
	}
	backConn, ok := res.Body.(io.ReadWriteCloser)
	if !ok {
		// p.getErrorHandler()(rw, req, fmt.Errorf("internal error: 101 switching protocols response with non-writable body"))
		return
	}

	// adopted from https://github.com/golang/go/commit/8bcf2834afdf6a1f7937390903a41518715ef6f5
	backConnCloseCh := make(chan struct{})
	go func() {
		// Ensure that the cancelation of a request closes the backend.
		// See issue https://golang.org/issue/35559.
		select {
		case <-req.Context().Done():
		case <-backConnCloseCh:
		}
		backConn.Close()
	}()
	defer close(backConnCloseCh)

	conn, brw, err := hj.Hijack()
	if err != nil {
		// p.getErrorHandler()(rw, req, fmt.Errorf("Hijack failed on protocol switch: %v", err))
		return
	}
	defer conn.Close()
	res.Body = nil // so res.Write only writes the headers; we have res.Body in backConn above
	if err := res.Write(brw); err != nil {
		// p.getErrorHandler()(rw, req, fmt.Errorf("response write: %v", err))
		return
	}
	if err := brw.Flush(); err != nil {
		// p.getErrorHandler()(rw, req, fmt.Errorf("response flush: %v", err))
		return
	}
	errc := make(chan error, 1)
	spc := switchProtocolCopier{user: conn, backend: backConn}
	go spc.copyToBackend(errc)
	go spc.copyFromBackend(errc)
	<-errc
}

// flushInterval returns the p.FlushInterval value, conditionally
// overriding its value for a specific request/response.
func (h Handler) flushInterval(req *http.Request, res *http.Response) time.Duration {
	resCTHeader := res.Header.Get("Content-Type")
	resCT, _, err := mime.ParseMediaType(resCTHeader)

	// For Server-Sent Events responses, flush immediately.
	// The MIME type is defined in https://www.w3.org/TR/eventsource/#text-event-stream
	if err == nil && resCT == "text/event-stream" {
		return -1 // negative means immediately
	}

	// for h2 and h2c upstream streaming data to client (issues #3556 and #3606)
	if h.isBidirectionalStream(req, res) {
		return -1
	}

	// TODO: more specific cases? e.g. res.ContentLength == -1? (this TODO is from the std lib, but
	// strangely similar to our isBidirectionalStream function that we implemented ourselves)
	return time.Duration(h.FlushInterval)
}

// isBidirectionalStream returns whether we should work in bi-directional stream mode.
//
// See https://github.com/caddyserver/caddy/pull/3620 for discussion of nuances.
func (h Handler) isBidirectionalStream(req *http.Request, res *http.Response) bool {
	// We have to check the encoding here; only flush headers with identity encoding.
	// Non-identity encoding might combine with "encode" directive, and in that case,
	// if body size larger than enc.MinLength, upper level encode handle might have
	// Content-Encoding header to write.
	// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
	ae := req.Header.Get("Accept-Encoding")

	return req.ProtoMajor == 2 &&
		res.ProtoMajor == 2 &&
		res.ContentLength == -1 &&
		(ae == "identity" || ae == "")
}

func (h Handler) copyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) error {
	if flushInterval != 0 {
		if wf, ok := dst.(writeFlusher); ok {
			mlw := &maxLatencyWriter{
				dst:     wf,
				latency: flushInterval,
			}
			defer mlw.stop()
			dst = mlw
		}
	}

	buf := streamingBufPool.Get().([]byte)
	defer streamingBufPool.Put(buf)
	_, err := h.copyBuffer(dst, src, buf)
	return err
}

// copyBuffer returns any write errors or non-EOF read errors, and the amount
// of bytes written.
func (h Handler) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) {
	if len(buf) == 0 {
		buf = make([]byte, defaultBufferSize)
	}
	var written int64
	for {
		nr, rerr := src.Read(buf)
		if rerr != nil && rerr != io.EOF && rerr != context.Canceled {
			// TODO: this could be useful to know (indeed, it revealed an error in our
			// fastcgi PoC earlier; but it's this single error report here that necessitates
			// a function separate from io.CopyBuffer, since io.CopyBuffer does not distinguish
			// between read or write errors; in a reverse proxy situation, write errors are not
			// something we need to report to the client, but read errors are a problem on our
			// end for sure. so we need to decide what we want.)
			// p.logf("copyBuffer: ReverseProxy read error during body copy: %v", rerr)
			h.logger.Error("reading from backend", zap.Error(rerr))
		}
		if nr > 0 {
			nw, werr := dst.Write(buf[:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if werr != nil {
				return written, werr
			}
			if nr != nw {
				return written, io.ErrShortWrite
			}
		}
		if rerr != nil {
			if rerr == io.EOF {
				rerr = nil
			}
			return written, rerr
		}
	}
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration // non-zero; negative means to flush immediately

	mu           sync.Mutex // protects t, flushPending, and dst.Flush
	t            *time.Timer
	flushPending bool
}

func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	n, err = m.dst.Write(p)
	if m.latency < 0 {
		m.dst.Flush()
		return
	}
	if m.flushPending {
		return
	}
	if m.t == nil {
		m.t = time.AfterFunc(m.latency, m.delayedFlush)
	} else {
		m.t.Reset(m.latency)
	}
	m.flushPending = true
	return
}

func (m *maxLatencyWriter) delayedFlush() {
	m.mu.Lock()
	defer m.mu.Unlock()
	if !m.flushPending { // if stop was called but AfterFunc already started this goroutine
		return
	}
	m.dst.Flush()
	m.flushPending = false
}

func (m *maxLatencyWriter) stop() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.flushPending = false
	if m.t != nil {
		m.t.Stop()
	}
}

// switchProtocolCopier exists so goroutines proxying data back and
// forth have nice names in stacks.
type switchProtocolCopier struct {
	user, backend io.ReadWriter
}

func (c switchProtocolCopier) copyFromBackend(errc chan<- error) {
	_, err := io.Copy(c.user, c.backend)
	errc <- err
}

func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
	_, err := io.Copy(c.backend, c.user)
	errc <- err
}

var streamingBufPool = sync.Pool{
	New: func() interface{} {
		return make([]byte, defaultBufferSize)
	},
}

const defaultBufferSize = 32 * 1024
<commit_msg>reverseproxy: Logging for streaming and upgrades (#3689)<commit_after>// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Most of the code in this file was initially borrowed from the Go
// standard library and modified; It had this copyright notice:
// Copyright 2011 The Go Authors

package reverseproxy

import (
	"context"
	"io"
	"mime"
	"net/http"
	"sync"
	"time"

	"go.uber.org/zap"
)

func (h Handler) handleUpgradeResponse(rw http.ResponseWriter, req *http.Request, res *http.Response) {
	reqUpType := upgradeType(req.Header)
	resUpType := upgradeType(res.Header)
	if reqUpType != resUpType {
		h.logger.Debug("backend tried to switch to unexpected protocol via Upgrade header",
			zap.String("backend_upgrade", resUpType),
			zap.String("requested_upgrade", reqUpType))
		return
	}

	copyHeader(res.Header, rw.Header())

	hj, ok := rw.(http.Hijacker)
	if !ok {
		h.logger.Sugar().Errorf("can't switch protocols using non-Hijacker ResponseWriter type %T", rw)
		return
	}
	backConn, ok := res.Body.(io.ReadWriteCloser)
	if !ok {
		h.logger.Error("internal error: 101 switching protocols response with non-writable body")
		return
	}

	// adopted from https://github.com/golang/go/commit/8bcf2834afdf6a1f7937390903a41518715ef6f5
	backConnCloseCh := make(chan struct{})
	go func() {
		// Ensure that the cancelation of a request closes the backend.
		// See issue https://golang.org/issue/35559.
		select {
		case <-req.Context().Done():
		case <-backConnCloseCh:
		}
		backConn.Close()
	}()
	defer close(backConnCloseCh)

	conn, brw, err := hj.Hijack()
	if err != nil {
		h.logger.Error("Hijack failed on protocol switch", zap.Error(err))
		return
	}
	defer conn.Close()
	res.Body = nil // so res.Write only writes the headers; we have res.Body in backConn above
	if err := res.Write(brw); err != nil {
		h.logger.Debug("response write", zap.Error(err))
		return
	}
	if err := brw.Flush(); err != nil {
		h.logger.Debug("response flush", zap.Error(err))
		return
	}
	errc := make(chan error, 1)
	spc := switchProtocolCopier{user: conn, backend: backConn}
	go spc.copyToBackend(errc)
	go spc.copyFromBackend(errc)
	<-errc
}

// flushInterval returns the p.FlushInterval value, conditionally
// overriding its value for a specific request/response.
func (h Handler) flushInterval(req *http.Request, res *http.Response) time.Duration {
	resCTHeader := res.Header.Get("Content-Type")
	resCT, _, err := mime.ParseMediaType(resCTHeader)

	// For Server-Sent Events responses, flush immediately.
	// The MIME type is defined in https://www.w3.org/TR/eventsource/#text-event-stream
	if err == nil && resCT == "text/event-stream" {
		return -1 // negative means immediately
	}

	// for h2 and h2c upstream streaming data to client (issues #3556 and #3606)
	if h.isBidirectionalStream(req, res) {
		return -1
	}

	// TODO: more specific cases? e.g. res.ContentLength == -1? (this TODO is from the std lib, but
	// strangely similar to our isBidirectionalStream function that we implemented ourselves)
	return time.Duration(h.FlushInterval)
}

// isBidirectionalStream returns whether we should work in bi-directional stream mode.
//
// See https://github.com/caddyserver/caddy/pull/3620 for discussion of nuances.
func (h Handler) isBidirectionalStream(req *http.Request, res *http.Response) bool {
	// We have to check the encoding here; only flush headers with identity encoding.
	// Non-identity encoding might combine with "encode" directive, and in that case,
	// if body size larger than enc.MinLength, upper level encode handle might have
	// Content-Encoding header to write.
	// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
	ae := req.Header.Get("Accept-Encoding")

	return req.ProtoMajor == 2 &&
		res.ProtoMajor == 2 &&
		res.ContentLength == -1 &&
		(ae == "identity" || ae == "")
}

func (h Handler) copyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) error {
	if flushInterval != 0 {
		if wf, ok := dst.(writeFlusher); ok {
			mlw := &maxLatencyWriter{
				dst:     wf,
				latency: flushInterval,
			}
			defer mlw.stop()
			dst = mlw
		}
	}

	buf := streamingBufPool.Get().([]byte)
	defer streamingBufPool.Put(buf)
	_, err := h.copyBuffer(dst, src, buf)
	return err
}

// copyBuffer returns any write errors or non-EOF read errors, and the amount
// of bytes written.
func (h Handler) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) {
	if len(buf) == 0 {
		buf = make([]byte, defaultBufferSize)
	}
	var written int64
	for {
		nr, rerr := src.Read(buf)
		if rerr != nil && rerr != io.EOF && rerr != context.Canceled {
			// TODO: this could be useful to know (indeed, it revealed an error in our
			// fastcgi PoC earlier; but it's this single error report here that necessitates
			// a function separate from io.CopyBuffer, since io.CopyBuffer does not distinguish
			// between read or write errors; in a reverse proxy situation, write errors are not
			// something we need to report to the client, but read errors are a problem on our
			// end for sure. so we need to decide what we want.)
			// p.logf("copyBuffer: ReverseProxy read error during body copy: %v", rerr)
			h.logger.Error("reading from backend", zap.Error(rerr))
		}
		if nr > 0 {
			nw, werr := dst.Write(buf[:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if werr != nil {
				return written, werr
			}
			if nr != nw {
				return written, io.ErrShortWrite
			}
		}
		if rerr != nil {
			if rerr == io.EOF {
				rerr = nil
			}
			return written, rerr
		}
	}
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration // non-zero; negative means to flush immediately

	mu           sync.Mutex // protects t, flushPending, and dst.Flush
	t            *time.Timer
	flushPending bool
}

func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	n, err = m.dst.Write(p)
	if m.latency < 0 {
		m.dst.Flush()
		return
	}
	if m.flushPending {
		return
	}
	if m.t == nil {
		m.t = time.AfterFunc(m.latency, m.delayedFlush)
	} else {
		m.t.Reset(m.latency)
	}
	m.flushPending = true
	return
}

func (m *maxLatencyWriter) delayedFlush() {
	m.mu.Lock()
	defer m.mu.Unlock()
	if !m.flushPending { // if stop was called but AfterFunc already started this goroutine
		return
	}
	m.dst.Flush()
	m.flushPending = false
}

func (m *maxLatencyWriter) stop() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.flushPending = false
	if m.t != nil {
		m.t.Stop()
	}
}

// switchProtocolCopier exists so goroutines proxying data back and
// forth have nice names in stacks.
type switchProtocolCopier struct {
	user, backend io.ReadWriter
}

func (c switchProtocolCopier) copyFromBackend(errc chan<- error) {
	_, err := io.Copy(c.user, c.backend)
	errc <- err
}

func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
	_, err := io.Copy(c.backend, c.user)
	errc <- err
}

var streamingBufPool = sync.Pool{
	New: func() interface{} {
		return make([]byte, defaultBufferSize)
	},
}

const defaultBufferSize = 32 * 1024
<|endoftext|>


<commit_before>package conf

import (
	"fmt"
	"github.com/jacobstr/confer"
	"path/filepath"
)

// TODO:
// - allow command line arguments to be passed
// - eg. --APP_PORT=1234, should be bubbled to the right place

var configuration *confer.Config

func init() {
	loadDefaultConfig()
}

func loadDefaultConfig() {
	configuration = confer.NewConfig()
	seek := []string{
		"dev.yaml",
		"config.yaml",
		"./config/dev.yaml",
		"./config/config.yaml",
	}
	var err error
	for _, file := range seek {
		err := configuration.ReadPaths(file)
		if err == nil {
			abs, _ := filepath.Abs(file)
			fmt.Println("Configuration loaded:", abs)
			break
		}
	}
	if err != nil {
		fmt.Println("No configuration file found")
	}
}

func Get(key string, defValue interface{}) interface{} {
	if configuration.IsSet(key) {
		return configuration.Get(key)
	} else {
		return defValue
	}
}

func Int(key string, defValue int) int {
	if Exists(key) {
		return configuration.GetInt(key)
	} else {
		return defValue
	}
}

func String(key string, defValue string) string {
	if Exists(key) {
		return configuration.GetString(key)
	} else {
		return defValue
	}
}

func StringSlice(key string, defValue []string) []string {
	if Exists(key) {
		return configuration.GetStringSlice(key)
	} else {
		return defValue
	}
}

func Bool(key string, defValue bool) bool {
	if Exists(key) {
		return configuration.GetBool(key)
	} else {
		return defValue
	}
}

func Exists(key string) bool {
	return configuration.IsSet(key)
}
<commit_msg>Incorporate production.yaml as well, and load it first<commit_after>package conf

import (
	"fmt"
	"github.com/jacobstr/confer"
	"path/filepath"
)

// TODO:
// - allow command line arguments to be passed
// - eg. --APP_PORT=1234, should be bubbled to the right place

var configuration *confer.Config

func init() {
	loadDefaultConfig()
}

func loadDefaultConfig() {
	configuration = confer.NewConfig()
	seek := []string{
		"production.yaml",
		"dev.yaml",
		"config.yaml",
		"./config/production.yaml",
		"./config/dev.yaml",
		"./config/config.yaml",
	}
	var err error
	for _, file := range seek {
		err := configuration.ReadPaths(file)
		if err == nil {
			abs, _ := filepath.Abs(file)
			fmt.Println("Configuration loaded:", abs)
			break
		}
	}
	if err != nil {
		fmt.Println("No configuration file found")
	}
}

func Get(key string, defValue interface{}) interface{} {
	if configuration.IsSet(key) {
		return configuration.Get(key)
	} else {
		return defValue
	}
}

func Int(key string, defValue int) int {
	if Exists(key) {
		return configuration.GetInt(key)
	} else {
		return defValue
	}
}

func String(key string, defValue string) string {
	if Exists(key) {
		return configuration.GetString(key)
	} else {
		return defValue
	}
}

func StringSlice(key string, defValue []string) []string {
	if Exists(key) {
		return configuration.GetStringSlice(key)
	} else {
		return defValue
	}
}

func Bool(key string, defValue bool) bool {
	if Exists(key) {
		return configuration.GetBool(key)
	} else {
		return defValue
	}
}

func Exists(key string) bool {
	return configuration.IsSet(key)
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rec

import (
	"testing"

	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/dbtest"
	"gopkg.in/check.v1"
)

func Test(t *testing.T) {
	check.TestingT(t)
}

var _ = check.Suite(RecSuite{})

type RecSuite struct{}

func (RecSuite) SetUpSuite(c *check.C) {
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "tsuru_rec_test")
}

func (RecSuite) TearDownSuite(c *check.C) {
	conn, _ := db.Conn()
	defer conn.Close()
	dbtest.ClearAllCollections(conn.Apps().Database)
}

func (RecSuite) TestLog(c *check.C) {
	ch := Log("user@tsuru.io", "run-command", "ls", "-ltr")
	_, ok := <-ch
	c.Assert(ok, check.Equals, false)
	conn, err := db.Conn()
	c.Assert(err, check.IsNil)
	defer conn.Close()
	query := map[string]interface{}{
		"user":   "user@tsuru.io",
		"action": "run-command",
		"extra":  []interface{}{"ls", "-ltr"},
	}
	defer conn.UserActions().RemoveAll(query)
	count, err := conn.UserActions().Find(query).Count()
	c.Assert(err, check.IsNil)
	c.Assert(count, check.Equals, 1)
}

func (RecSuite) TestLogConnError(c *check.C) {
	old, _ := config.Get("database:url")
	defer config.Set("database:url", old)
	config.Set("database:url", "127.0.0.1:29999")
	ch := Log("user@tsuru.io", "run-command", "ls", "-ltr")
	err, ok := <-ch
	c.Assert(ok, check.Equals, true)
	c.Assert(err, check.NotNil)
	config.Set("database:url", old)
	conn, err := db.Conn()
	c.Assert(err, check.IsNil)
	defer conn.Close()
	query := map[string]interface{}{
		"user":   "user@tsuru.io",
		"action": "run-command",
		"extra":  []interface{}{"ls", "-ltr"},
	}
	defer conn.UserActions().RemoveAll(query)
	count, err := conn.UserActions().Find(query).Count()
	c.Assert(err, check.IsNil)
	c.Assert(count, check.Equals, 0)
}

func (RecSuite) TestLogInvalidData(c *check.C) {
	var tests = []struct {
		user     string
		action   string
		extra    []interface{}
		expected error
	}{
		{
			user:     "",
			action:   "",
			extra:    nil,
			expected: ErrMissingUser,
		},
		{
			user:     "gopher@golang.org",
			action:   "",
			extra:    nil,
			expected: ErrMissingAction,
		},
		{
			user:     "gopher@golang.org",
			action:   "do-something",
			extra:    nil,
			expected: nil,
		},
	}
	for _, t := range tests {
		ch := Log(t.user, t.action, t.extra...)
		err := <-ch
		c.Check(err, check.Equals, t.expected)
	}
	conn, err := db.Conn()
	c.Assert(err, check.IsNil)
	defer conn.Close()
	var action userAction
	err = conn.UserActions().Find(nil).One(&action)
	c.Assert(err, check.IsNil)
	c.Assert(action.User, check.Equals, "gopher@golang.org")
	c.Assert(action.Action, check.Equals, "do-something")
}
<commit_msg>Remove test that check a db connection error<commit_after>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rec

import (
	"testing"

	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/dbtest"
	"gopkg.in/check.v1"
)

func Test(t *testing.T) {
	check.TestingT(t)
}

var _ = check.Suite(RecSuite{})

type RecSuite struct{}

func (RecSuite) SetUpSuite(c *check.C) {
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "tsuru_rec_test")
}

func (RecSuite) TearDownSuite(c *check.C) {
	conn, err := db.Conn()
	c.Assert(err, check.IsNil)
	defer conn.Close()
	err = dbtest.ClearAllCollections(conn.Apps().Database)
	c.Assert(err, check.IsNil)
}

func (RecSuite) TestLog(c *check.C) {
	ch := Log("user@tsuru.io", "run-command", "ls", "-ltr")
	_, ok := <-ch
	c.Assert(ok, check.Equals, false)
	conn, err := db.Conn()
	c.Assert(err, check.IsNil)
	defer conn.Close()
	query := map[string]interface{}{
		"user":   "user@tsuru.io",
		"action": "run-command",
		"extra":  []interface{}{"ls", "-ltr"},
	}
	defer conn.UserActions().RemoveAll(query)
	count, err := conn.UserActions().Find(query).Count()
	c.Assert(err, check.IsNil)
	c.Assert(count, check.Equals, 1)
}

func (RecSuite) TestLogInvalidData(c *check.C) {
	var tests = []struct {
		user     string
		action   string
		extra    []interface{}
		expected error
	}{
		{
			user:     "",
			action:   "",
			extra:    nil,
			expected: ErrMissingUser,
		},
		{
			user:     "gopher@golang.org",
			action:   "",
			extra:    nil,
			expected: ErrMissingAction,
		},
		{
			user:     "gopher@golang.org",
			action:   "do-something",
			extra:    nil,
			expected: nil,
		},
	}
	for _, t := range tests {
		ch := Log(t.user, t.action, t.extra...)
		err := <-ch
		c.Check(err, check.Equals, t.expected)
	}
	conn, err := db.Conn()
	c.Assert(err, check.IsNil)
	defer conn.Close()
	var action userAction
	err = conn.UserActions().Find(nil).One(&action)
	c.Assert(err, check.IsNil)
	c.Assert(action.User, check.Equals, "gopher@golang.org")
	c.Assert(action.Action, check.Equals, "do-something")
}
<|endoftext|>


<commit_before>package jsonpointer

import (
	"reflect"
	"testing"
)

type address struct {
	Street string `json:"street"`
	Zip    string
}

type person struct {
	Name               string `json:"name,omitempty"`
	Twitter            string
	Aliases            []string   `json:"aliases"`
	Addresses          []*address `json:"addresses"`
	NameTildeContained string     `json:"name~contained"`
	NameSlashContained string     `json:"name/contained"`
	AnActualArray      [4]int
	NestedMap          map[string]float64 `json:"nestedmap"`
	MapIntKey          map[int]string
	MapUintKey         map[uint]string
	MapFloatKey        map[float64]string
}

var input = &person{
	Name:    "marty",
	Twitter: "mschoch",
	Aliases: []string{
		"jabroni",
		"beer",
	},
	Addresses: []*address{
		&address{
			Street: "123 Sesame St.",
			Zip:    "99099",
		},
	},
	NameTildeContained: "yessir",
	NameSlashContained: "nosir",
	AnActualArray:      [4]int{0, 1, 2, 3},
	NestedMap: map[string]float64{
		"pi":          3.14,
		"back/saidhe": 2.71,
		"till~duh":    1.41,
	},
	MapIntKey: map[int]string{
		1: "one",
		2: "two",
	},
	MapUintKey: map[uint]string{
		3: "three",
		4: "four",
	},
	MapFloatKey: map[float64]string{
		3.14: "pi",
		4.15: "notpi",
	},
}

func compareStringArrayIgnoringOrder(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	tmp := make(map[string]bool, len(a))
	for _, av := range a {
		tmp[av] = true
	}
	for _, bv := range b {
		if tmp[bv] != true {
			return false
		}
	}
	return true
}

func TestReflectListPointers(t *testing.T) {
	pointers, err := ReflectListPointers(input)
	if err != nil {
		t.Fatal(err)
	}
	expect := []string{"", "/name", "/Twitter", "/aliases",
		"/aliases/0", "/aliases/1", "/addresses", "/addresses/0",
		"/addresses/0/street", "/addresses/0/Zip",
		"/name~0contained", "/name~1contained", "/AnActualArray",
		"/AnActualArray/0", "/AnActualArray/1", "/AnActualArray/2",
		"/AnActualArray/3", "/nestedmap", "/nestedmap/pi",
		"/nestedmap/back~1saidhe", "/nestedmap/till~0duh",
		"/MapIntKey", "/MapIntKey/1", "/MapIntKey/2", "/MapUintKey",
		"/MapUintKey/3", "/MapUintKey/4", "/MapFloatKey",
		"/MapFloatKey/3.14", "/MapFloatKey/4.15"}
	if !compareStringArrayIgnoringOrder(expect, pointers) {
		t.Fatalf("expected %#v, got %#v", expect, pointers)
	}
}

func TestReflectNonObjectOrSlice(t *testing.T) {
	got := Reflect(36, "/test")
	if got != nil {
		t.Errorf("expected nil, got %#v", got)
	}
}

type structThatCanBeUsedAsKey struct {
	name   string
	domain string
}

func TestReflectMapThatWontWork(t *testing.T) {

	amapthatwontwork := map[structThatCanBeUsedAsKey]string{}
	akey := structThatCanBeUsedAsKey{name: "marty", domain: "couchbase"}
	amapthatwontwork[akey] = "verycontrived"

	got := Reflect(amapthatwontwork, "/anykey")
	if got != nil {
		t.Errorf("expected nil, got %#v", got)
	}
}

func TestReflect(t *testing.T) {

	tests := []struct {
		path string
		exp  interface{}
	}{
		{
			path: "",
			exp:  input,
		},
		{
			path: "/name",
			exp:  "marty",
		},
		{
			path: "/Name",
			exp:  "marty",
		},
		{
			path: "/Twitter",
			exp:  "mschoch",
		},
		{
			path: "/aliases/0",
			exp:  "jabroni",
		},
		{
			path: "/Aliases/0",
			exp:  "jabroni",
		},
		{
			path: "/addresses/0/street",
			exp:  "123 Sesame St.",
		},
		{
			path: "/addresses/4/street",
			exp:  nil,
		},
		{
			path: "/doesntexist",
			exp:  nil,
		},
		{
			path: "/does/not/exit",
			exp:  nil,
		},
		{
			path: "/doesntexist/7",
			exp:  nil,
		},
		{
			path: "/name~0contained",
			exp:  "yessir",
		},
		{
			path: "/name~1contained",
			exp:  "nosir",
		},
		{
			path: "/AnActualArray/2",
			exp:  2,
		},
		{
			path: "/AnActualArray/5",
			exp:  nil,
		},
		{
			path: "/nestedmap/pi",
			exp:  3.14,
		},
		{
			path: "/nestedmap/back~1saidhe",
			exp:  2.71,
		},
		{
			path: "/nestedmap/till~0duh",
			exp:  1.41,
		},
		{
			path: "/MapIntKey/1",
			exp:  "one",
		},
		{
			path: "/MapUintKey/3",
			exp:  "three",
		},
		{
			path: "/MapFloatKey/3.14",
			exp:  "pi",
		},
	}

	for _, test := range tests {
		output := Reflect(input, test.path)
		if !reflect.DeepEqual(output, test.exp) {
			t.Errorf("Expected %#v, got %#v", test.exp, output)
		}
	}
}
<commit_msg>Some reflect benchmarks<commit_after>package jsonpointer

import (
	"reflect"
	"testing"
)

type address struct {
	Street string `json:"street"`
	Zip    string
}

type person struct {
	Name               string `json:"name,omitempty"`
	Twitter            string
	Aliases            []string   `json:"aliases"`
	Addresses          []*address `json:"addresses"`
	NameTildeContained string     `json:"name~contained"`
	NameSlashContained string     `json:"name/contained"`
	AnActualArray      [4]int
	NestedMap          map[string]float64 `json:"nestedmap"`
	MapIntKey          map[int]string
	MapUintKey         map[uint]string
	MapFloatKey        map[float64]string
}

var input = &person{
	Name:    "marty",
	Twitter: "mschoch",
	Aliases: []string{
		"jabroni",
		"beer",
	},
	Addresses: []*address{
		&address{
			Street: "123 Sesame St.",
			Zip:    "99099",
		},
	},
	NameTildeContained: "yessir",
	NameSlashContained: "nosir",
	AnActualArray:      [4]int{0, 1, 2, 3},
	NestedMap: map[string]float64{
		"pi":          3.14,
		"back/saidhe": 2.71,
		"till~duh":    1.41,
	},
	MapIntKey: map[int]string{
		1: "one",
		2: "two",
	},
	MapUintKey: map[uint]string{
		3: "three",
		4: "four",
	},
	MapFloatKey: map[float64]string{
		3.14: "pi",
		4.15: "notpi",
	},
}

func benchReflect(b *testing.B, path string) {
	for i := 0; i < b.N; i++ {
		if Reflect(input, path) == nil {
			b.FailNow()
		}
	}
}

func BenchmarkReflectRoot(b *testing.B) {
	benchReflect(b, "/")
}

func BenchmarkReflectToplevelExact(b *testing.B) {
	benchReflect(b, "/Twitter")
}

func BenchmarkReflectToplevelTagged(b *testing.B) {
	benchReflect(b, "/Name")
}

func BenchmarkReflectToplevelTaggedLower(b *testing.B) {
	benchReflect(b, "/name")
}

func BenchmarkReflectDeep(b *testing.B) {
	benchReflect(b, "/addresses/0/Zip")
}

func compareStringArrayIgnoringOrder(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	tmp := make(map[string]bool, len(a))
	for _, av := range a {
		tmp[av] = true
	}
	for _, bv := range b {
		if tmp[bv] != true {
			return false
		}
	}
	return true
}

func TestReflectListPointers(t *testing.T) {
	pointers, err := ReflectListPointers(input)
	if err != nil {
		t.Fatal(err)
	}
	expect := []string{"", "/name", "/Twitter", "/aliases",
		"/aliases/0", "/aliases/1", "/addresses", "/addresses/0",
		"/addresses/0/street", "/addresses/0/Zip",
		"/name~0contained", "/name~1contained", "/AnActualArray",
		"/AnActualArray/0", "/AnActualArray/1", "/AnActualArray/2",
		"/AnActualArray/3", "/nestedmap", "/nestedmap/pi",
		"/nestedmap/back~1saidhe", "/nestedmap/till~0duh",
		"/MapIntKey", "/MapIntKey/1", "/MapIntKey/2", "/MapUintKey",
		"/MapUintKey/3", "/MapUintKey/4", "/MapFloatKey",
		"/MapFloatKey/3.14", "/MapFloatKey/4.15"}
	if !compareStringArrayIgnoringOrder(expect, pointers) {
		t.Fatalf("expected %#v, got %#v", expect, pointers)
	}
}

func TestReflectNonObjectOrSlice(t *testing.T) {
	got := Reflect(36, "/test")
	if got != nil {
		t.Errorf("expected nil, got %#v", got)
	}
}

type structThatCanBeUsedAsKey struct {
	name   string
	domain string
}

func TestReflectMapThatWontWork(t *testing.T) {

	amapthatwontwork := map[structThatCanBeUsedAsKey]string{}
	akey := structThatCanBeUsedAsKey{name: "marty", domain: "couchbase"}
	amapthatwontwork[akey] = "verycontrived"

	got := Reflect(amapthatwontwork, "/anykey")
	if got != nil {
		t.Errorf("expected nil, got %#v", got)
	}
}

func TestReflect(t *testing.T) {

	tests := []struct {
		path string
		exp  interface{}
	}{
		{
			path: "",
			exp:  input,
		},
		{
			path: "/name",
			exp:  "marty",
		},
		{
			path: "/Name",
			exp:  "marty",
		},
		{
			path: "/Twitter",
			exp:  "mschoch",
		},
		{
			path: "/aliases/0",
			exp:  "jabroni",
		},
		{
			path: "/Aliases/0",
			exp:  "jabroni",
		},
		{
			path: "/addresses/0/street",
			exp:  "123 Sesame St.",
		},
		{
			path: "/addresses/4/street",
			exp:  nil,
		},
		{
			path: "/doesntexist",
			exp:  nil,
		},
		{
			path: "/does/not/exit",
			exp:  nil,
		},
		{
			path: "/doesntexist/7",
			exp:  nil,
		},
		{
			path: "/name~0contained",
			exp:  "yessir",
		},
		{
			path: "/name~1contained",
			exp:  "nosir",
		},
		{
			path: "/AnActualArray/2",
			exp:  2,
		},
		{
			path: "/AnActualArray/5",
			exp:  nil,
		},
		{
			path: "/nestedmap/pi",
			exp:  3.14,
		},
		{
			path: "/nestedmap/back~1saidhe",
			exp:  2.71,
		},
		{
			path: "/nestedmap/till~0duh",
			exp:  1.41,
		},
		{
			path: "/MapIntKey/1",
			exp:  "one",
		},
		{
			path: "/MapUintKey/3",
			exp:  "three",
		},
		{
			path: "/MapFloatKey/3.14",
			exp:  "pi",
		},
	}

	for _, test := range tests {
		output := Reflect(input, test.path)
		if !reflect.DeepEqual(output, test.exp) {
			t.Errorf("Expected %#v, got %#v", test.exp, output)
		}
	}
}
<|endoftext|>