<commit_before>package vppcalls

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	govppapi "git.fd.io/govpp.git/api"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/bin_api/vpe"
)

// VersionInfo contains values returned from ShowVersion
type VersionInfo struct {
	Program        string
	Version        string
	BuildDate      string
	BuildDirectory string
}

// GetVersionInfo retrieves version info
func GetVersionInfo(vppChan *govppapi.Channel) (*VersionInfo, error) {
	req := &vpe.ShowVersion{}
	reply := &vpe.ShowVersionReply{}

	if err := vppChan.SendRequest(req).ReceiveReply(reply); err != nil {
		return nil, err
	} else if reply.Retval != 0 {
		return nil, fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	info := &VersionInfo{
		Program:        string(cleanBytes(reply.Program)),
		Version:        string(cleanBytes(reply.Version)),
		BuildDate:      string(cleanBytes(reply.BuildDate)),
		BuildDirectory: string(cleanBytes(reply.BuildDirectory)),
	}

	return info, nil
}

// RunCliCommand executes CLI command and returns output
func RunCliCommand(vppChan *govppapi.Channel, cmd string) ([]byte, error) {
	req := &vpe.CliInband{
		Cmd:    []byte(cmd),
		Length: uint32(len(cmd)),
	}
	reply := &vpe.CliInbandReply{}

	if err := vppChan.SendRequest(req).ReceiveReply(reply); err != nil {
		return nil, err
	} else if reply.Retval != 0 {
		return nil, fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return reply.Reply[:reply.Length], nil
}

// MemoryInfo contains values returned from 'show memory'
type MemoryInfo struct {
	Threads []MemoryThread `json:"threads"`
}

// MemoryThread represents single thread memory counters
type MemoryThread struct {
	ID        uint   `json:"id"`
	Name      string `json:"name"`
	Objects   uint64 `json:"objects"`
	Used      uint64 `json:"used"`
	Total     uint64 `json:"total"`
	Free      uint64 `json:"free"`
	Reclaimed uint64 `json:"reclaimed"`
	Overhead  uint64 `json:"overhead"`
	Capacity  uint64 `json:"capacity"`
}

var memoryRe = regexp.MustCompile(`Thread\s+(\d+)\s+(\w+).?\s+(\d+) objects, (\d+k?) of (\d+k?) used, (\d+k?) free, (\d+k?) reclaimed, (\d+k?) overhead, (\d+k?) capacity`)

// GetNodeCounters retrieves node counters info
func GetMemory(vppChan *govppapi.Channel) (*MemoryInfo, error) {
	data, err := RunCliCommand(vppChan, "show memory")
	if err != nil {
		return nil, err
	}

	var threads []MemoryThread

	threadMatches := memoryRe.FindAllStringSubmatch(string(data), -1)
	for _, matches := range threadMatches {
		fields := matches[1:]
		if len(fields) != 9 {
			return nil, fmt.Errorf("invalid memory data for thread: %q", matches[0])
		}
		id, err := strconv.ParseUint(fields[0], 10, 64)
		if err != nil {
			return nil, err
		}
		thread := &MemoryThread{
			ID:        uint(id),
			Name:      fields[1],
			Objects:   strToUint64(fields[2]),
			Used:      strToUint64(fields[3]),
			Total:     strToUint64(fields[4]),
			Free:      strToUint64(fields[5]),
			Reclaimed: strToUint64(fields[6]),
			Overhead:  strToUint64(fields[7]),
			Capacity:  strToUint64(fields[8]),
		}
		threads = append(threads, *thread)
	}

	info := &MemoryInfo{
		Threads: threads,
	}

	return info, nil
}

// NodeCounterInfo contains values returned from 'show node counters'
type NodeCounterInfo struct {
	Counters []NodeCounter `json:"counters"`
}

// NodeCounter represents single node counter
type NodeCounter struct {
	Count  uint64 `json:"count"`
	Node   string `json:"node"`
	Reason string `json:"reason"`
}

// GetNodeCounters retrieves node counters info
func GetNodeCounters(vppChan *govppapi.Channel) (*NodeCounterInfo, error) {
	data, err := RunCliCommand(vppChan, "show node counters")
	if err != nil {
		return nil, err
	}

	var counters []NodeCounter

	for _, line := range strings.Split(string(data), "\n") {
		fields := strings.Fields(line)
		if len(fields) == 3 {
			if fields[0] == "Count" {
				counters = []NodeCounter{}
				continue
			}
			if counters != nil {
				counters = append(counters, NodeCounter{
					Count:  strToUint64(fields[0]),
					Node:   fields[1],
					Reason: fields[2],
				})
			}
		}
	}

	info := &NodeCounterInfo{
		Counters: counters,
	}

	return info, nil
}

// RuntimeInfo contains values returned from 'show runtime'
type RuntimeInfo struct {
	Threads []RuntimeThread `json:"threads"`
}

// RuntimeThread represents single runtime thread
type RuntimeThread struct {
	ID                uint          `json:"id"`
	Name              string        `json:"name"`
	Time              float64       `json:"time"`
	AvgVectorsPerNode float64       `json:"avg_vectors_per_node"`
	LastMainLoops     uint64        `json:"last_main_loops"`
	PerNode           float64       `json:"per_node"`
	VectorRates       float64       `json:"vector_rates"`
	In                float64       `json:"in"`
	Out               float64       `json:"out"`
	Drop              float64       `json:"drop"`
	Punt              float64       `json:"punt"`
	Items             []RuntimeItem `json:"items"`
}

// RuntimeItem represents single runtime item
type RuntimeItem struct {
	Name           string  `json:"name"`
	State          string  `json:"state"`
	Calls          uint64  `json:"calls"`
	Vectors        uint64  `json:"vectors"`
	Suspends       uint64  `json:"suspends"`
	Clocks         float64 `json:"clocks"`
	VectorsPerCall float64 `json:"vectors_per_call"`
}

var (
	runtimeRe = regexp.MustCompile(`(?:-+\n)?(?:Thread (\d+) (\w+)(?: \(lcore \d+\))?\n)?` +
		`Time ([0-9\.e]+), average vectors/node ([0-9\.e]+), last (\d+) main loops ([0-9\.e]+) per node ([0-9\.e]+)\s+` +
		`vector rates in ([0-9\.e]+), out ([0-9\.e]+), drop ([0-9\.e]+), punt ([0-9\.e]+)\n` +
		`\s+Name\s+State\s+Calls\s+Vectors\s+Suspends\s+Clocks\s+Vectors/Call\s+` +
		`((?:[\w-:\.]+\s+\w+(?:[ -]\w+)*\s+\d+\s+\d+\s+\d+\s+[0-9\.e]+\s+[0-9\.e]+\s+)+)`)
	runtimeItemsRe = regexp.MustCompile(`([\w-:\.]+)\s+(\w+(?:[ -]\w+)*)\s+(\d+)\s+(\d+)\s+(\d+)\s+([0-9\.e]+)\s+([0-9\.e]+)\s+`)
)

// GetNodeCounters retrieves node counters info
func GetRuntimeInfo(vppChan *govppapi.Channel) (*RuntimeInfo, error) {
	data, err := RunCliCommand(vppChan, "show runtime")
	if err != nil {
		return nil, err
	}

	var threads []RuntimeThread

	threadMatches := runtimeRe.FindAllStringSubmatch(string(data), -1)
	for _, matches := range threadMatches {
		fields := matches[1:]
		if len(fields) != 12 {
			return nil, fmt.Errorf("invalid runtime data for thread: %q", matches[0])
		}
		thread := RuntimeThread{
			ID:                uint(strToUint64(fields[0])),
			Name:              fields[1],
			Time:              strToFloat64(fields[2]),
			AvgVectorsPerNode: strToFloat64(fields[3]),
			LastMainLoops:     strToUint64(fields[4]),
			PerNode:           strToFloat64(fields[5]),
			VectorRates:       strToFloat64(fields[6]),
			In:                strToFloat64(fields[7]),
			Out:               strToFloat64(fields[8]),
			Drop:              strToFloat64(fields[9]),
			Punt:              strToFloat64(fields[10]),
		}

		itemMatches := runtimeItemsRe.FindAllStringSubmatch(string(fields[11]), -1)
		for _, matches := range itemMatches {
			fields := matches[1:]
			if len(fields) != 7 {
				return nil, fmt.Errorf("invalid runtime data for thread item: %q", matches[0])
			}
			thread.Items = append(thread.Items, RuntimeItem{
				Name:           fields[0],
				State:          fields[1],
				Calls:          strToUint64(fields[2]),
				Vectors:        strToUint64(fields[3]),
				Suspends:       strToUint64(fields[4]),
				Clocks:         strToFloat64(fields[5]),
				VectorsPerCall: strToFloat64(fields[6]),
			})
		}

		threads = append(threads, thread)
	}

	info := &RuntimeInfo{
		Threads: threads,
	}

	return info, nil
}

// BuffersInfo contains values returned from 'show buffers'
type BuffersInfo struct {
	Items []BuffersItem `json:"items"`
}

// BuffersItem represents single buffers item
type BuffersItem struct {
	ThreadID uint   `json:"thread_id"`
	Name     string `json:"name"`
	Index    uint   `json:"index"`
	Size     uint64 `json:"size"`
	Alloc    uint64 `json:"alloc"`
	Free     uint64 `json:"free"`
	NumAlloc uint64 `json:"num_alloc"`
	NumFree  uint64 `json:"num_free"`
}

var buffersRe = regexp.MustCompile(`^\s+(\d+)\s+(\w+(?:[ \-]\w+)*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+).*$`)

// GetBuffersInfo retrieves buffers info
func GetBuffersInfo(vppChan *govppapi.Channel) (*BuffersInfo, error) {
	data, err := RunCliCommand(vppChan, "show buffers")
	if err != nil {
		return nil, err
	}

	var items []BuffersItem

	for i, line := range strings.Split(string(data), "\n") {
		// Skip empty lines
		if strings.TrimSpace(line) == "" {
			continue
		}
		// Check first line
		if i == 0 {
			fields := strings.Fields(line)
			// Verify header
			if len(fields) != 8 || fields[0] != "Thread" {
				return nil, fmt.Errorf("invalid header for `show buffers` received: %q", line)
			}
			continue
		}

		// Parse lines using regexp
		matches := buffersRe.FindStringSubmatch(line)
		if len(matches)-1 != 8 {
			return nil, fmt.Errorf("parsing failed for `show buffers` line: %q", line)
		}
		fields := matches[1:]

		items = append(items, BuffersItem{
			ThreadID: uint(strToUint64(fields[0])),
			Name:     fields[1],
			Index:    uint(strToUint64(fields[2])),
			Size:     strToUint64(fields[3]),
			Alloc:    strToUint64(fields[4]),
			Free:     strToUint64(fields[5]),
			NumAlloc: strToUint64(fields[6]),
			NumFree:  strToUint64(fields[7]),
		})
	}

	info := &BuffersInfo{
		Items: items,
	}

	return info, nil
}

func strToFloat64(s string) float64 {
	num, err := strconv.ParseFloat(s, 10)
	if err != nil {
		return 0
	}
	return num
}

func strToUint64(s string) uint64 {
	s = strings.Replace(s, "k", "000", 1)
	num, err := strconv.ParseUint(s, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func cleanBytes(b []byte) []byte {
	return bytes.SplitN(b, []byte{0x00}, 2)[0]
}
<commit_msg>Add missing copyright<commit_after>//  Copyright (c) 2018 Cisco and/or its affiliates.
//
//  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 vppcalls

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	govppapi "git.fd.io/govpp.git/api"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/bin_api/vpe"
)

// VersionInfo contains values returned from ShowVersion
type VersionInfo struct {
	Program        string
	Version        string
	BuildDate      string
	BuildDirectory string
}

// GetVersionInfo retrieves version info
func GetVersionInfo(vppChan *govppapi.Channel) (*VersionInfo, error) {
	req := &vpe.ShowVersion{}
	reply := &vpe.ShowVersionReply{}

	if err := vppChan.SendRequest(req).ReceiveReply(reply); err != nil {
		return nil, err
	} else if reply.Retval != 0 {
		return nil, fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	info := &VersionInfo{
		Program:        string(cleanBytes(reply.Program)),
		Version:        string(cleanBytes(reply.Version)),
		BuildDate:      string(cleanBytes(reply.BuildDate)),
		BuildDirectory: string(cleanBytes(reply.BuildDirectory)),
	}

	return info, nil
}

// RunCliCommand executes CLI command and returns output
func RunCliCommand(vppChan *govppapi.Channel, cmd string) ([]byte, error) {
	req := &vpe.CliInband{
		Cmd:    []byte(cmd),
		Length: uint32(len(cmd)),
	}
	reply := &vpe.CliInbandReply{}

	if err := vppChan.SendRequest(req).ReceiveReply(reply); err != nil {
		return nil, err
	} else if reply.Retval != 0 {
		return nil, fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return reply.Reply[:reply.Length], nil
}

// MemoryInfo contains values returned from 'show memory'
type MemoryInfo struct {
	Threads []MemoryThread `json:"threads"`
}

// MemoryThread represents single thread memory counters
type MemoryThread struct {
	ID        uint   `json:"id"`
	Name      string `json:"name"`
	Objects   uint64 `json:"objects"`
	Used      uint64 `json:"used"`
	Total     uint64 `json:"total"`
	Free      uint64 `json:"free"`
	Reclaimed uint64 `json:"reclaimed"`
	Overhead  uint64 `json:"overhead"`
	Capacity  uint64 `json:"capacity"`
}

var memoryRe = regexp.MustCompile(`Thread\s+(\d+)\s+(\w+).?\s+(\d+) objects, (\d+k?) of (\d+k?) used, (\d+k?) free, (\d+k?) reclaimed, (\d+k?) overhead, (\d+k?) capacity`)

// GetNodeCounters retrieves node counters info
func GetMemory(vppChan *govppapi.Channel) (*MemoryInfo, error) {
	data, err := RunCliCommand(vppChan, "show memory")
	if err != nil {
		return nil, err
	}

	var threads []MemoryThread

	threadMatches := memoryRe.FindAllStringSubmatch(string(data), -1)
	for _, matches := range threadMatches {
		fields := matches[1:]
		if len(fields) != 9 {
			return nil, fmt.Errorf("invalid memory data for thread: %q", matches[0])
		}
		id, err := strconv.ParseUint(fields[0], 10, 64)
		if err != nil {
			return nil, err
		}
		thread := &MemoryThread{
			ID:        uint(id),
			Name:      fields[1],
			Objects:   strToUint64(fields[2]),
			Used:      strToUint64(fields[3]),
			Total:     strToUint64(fields[4]),
			Free:      strToUint64(fields[5]),
			Reclaimed: strToUint64(fields[6]),
			Overhead:  strToUint64(fields[7]),
			Capacity:  strToUint64(fields[8]),
		}
		threads = append(threads, *thread)
	}

	info := &MemoryInfo{
		Threads: threads,
	}

	return info, nil
}

// NodeCounterInfo contains values returned from 'show node counters'
type NodeCounterInfo struct {
	Counters []NodeCounter `json:"counters"`
}

// NodeCounter represents single node counter
type NodeCounter struct {
	Count  uint64 `json:"count"`
	Node   string `json:"node"`
	Reason string `json:"reason"`
}

// GetNodeCounters retrieves node counters info
func GetNodeCounters(vppChan *govppapi.Channel) (*NodeCounterInfo, error) {
	data, err := RunCliCommand(vppChan, "show node counters")
	if err != nil {
		return nil, err
	}

	var counters []NodeCounter

	for _, line := range strings.Split(string(data), "\n") {
		fields := strings.Fields(line)
		if len(fields) == 3 {
			if fields[0] == "Count" {
				counters = []NodeCounter{}
				continue
			}
			if counters != nil {
				counters = append(counters, NodeCounter{
					Count:  strToUint64(fields[0]),
					Node:   fields[1],
					Reason: fields[2],
				})
			}
		}
	}

	info := &NodeCounterInfo{
		Counters: counters,
	}

	return info, nil
}

// RuntimeInfo contains values returned from 'show runtime'
type RuntimeInfo struct {
	Threads []RuntimeThread `json:"threads"`
}

// RuntimeThread represents single runtime thread
type RuntimeThread struct {
	ID                uint          `json:"id"`
	Name              string        `json:"name"`
	Time              float64       `json:"time"`
	AvgVectorsPerNode float64       `json:"avg_vectors_per_node"`
	LastMainLoops     uint64        `json:"last_main_loops"`
	PerNode           float64       `json:"per_node"`
	VectorRates       float64       `json:"vector_rates"`
	In                float64       `json:"in"`
	Out               float64       `json:"out"`
	Drop              float64       `json:"drop"`
	Punt              float64       `json:"punt"`
	Items             []RuntimeItem `json:"items"`
}

// RuntimeItem represents single runtime item
type RuntimeItem struct {
	Name           string  `json:"name"`
	State          string  `json:"state"`
	Calls          uint64  `json:"calls"`
	Vectors        uint64  `json:"vectors"`
	Suspends       uint64  `json:"suspends"`
	Clocks         float64 `json:"clocks"`
	VectorsPerCall float64 `json:"vectors_per_call"`
}

var (
	runtimeRe = regexp.MustCompile(`(?:-+\n)?(?:Thread (\d+) (\w+)(?: \(lcore \d+\))?\n)?` +
		`Time ([0-9\.e]+), average vectors/node ([0-9\.e]+), last (\d+) main loops ([0-9\.e]+) per node ([0-9\.e]+)\s+` +
		`vector rates in ([0-9\.e]+), out ([0-9\.e]+), drop ([0-9\.e]+), punt ([0-9\.e]+)\n` +
		`\s+Name\s+State\s+Calls\s+Vectors\s+Suspends\s+Clocks\s+Vectors/Call\s+` +
		`((?:[\w-:\.]+\s+\w+(?:[ -]\w+)*\s+\d+\s+\d+\s+\d+\s+[0-9\.e]+\s+[0-9\.e]+\s+)+)`)
	runtimeItemsRe = regexp.MustCompile(`([\w-:\.]+)\s+(\w+(?:[ -]\w+)*)\s+(\d+)\s+(\d+)\s+(\d+)\s+([0-9\.e]+)\s+([0-9\.e]+)\s+`)
)

// GetNodeCounters retrieves node counters info
func GetRuntimeInfo(vppChan *govppapi.Channel) (*RuntimeInfo, error) {
	data, err := RunCliCommand(vppChan, "show runtime")
	if err != nil {
		return nil, err
	}

	var threads []RuntimeThread

	threadMatches := runtimeRe.FindAllStringSubmatch(string(data), -1)
	for _, matches := range threadMatches {
		fields := matches[1:]
		if len(fields) != 12 {
			return nil, fmt.Errorf("invalid runtime data for thread: %q", matches[0])
		}
		thread := RuntimeThread{
			ID:                uint(strToUint64(fields[0])),
			Name:              fields[1],
			Time:              strToFloat64(fields[2]),
			AvgVectorsPerNode: strToFloat64(fields[3]),
			LastMainLoops:     strToUint64(fields[4]),
			PerNode:           strToFloat64(fields[5]),
			VectorRates:       strToFloat64(fields[6]),
			In:                strToFloat64(fields[7]),
			Out:               strToFloat64(fields[8]),
			Drop:              strToFloat64(fields[9]),
			Punt:              strToFloat64(fields[10]),
		}

		itemMatches := runtimeItemsRe.FindAllStringSubmatch(string(fields[11]), -1)
		for _, matches := range itemMatches {
			fields := matches[1:]
			if len(fields) != 7 {
				return nil, fmt.Errorf("invalid runtime data for thread item: %q", matches[0])
			}
			thread.Items = append(thread.Items, RuntimeItem{
				Name:           fields[0],
				State:          fields[1],
				Calls:          strToUint64(fields[2]),
				Vectors:        strToUint64(fields[3]),
				Suspends:       strToUint64(fields[4]),
				Clocks:         strToFloat64(fields[5]),
				VectorsPerCall: strToFloat64(fields[6]),
			})
		}

		threads = append(threads, thread)
	}

	info := &RuntimeInfo{
		Threads: threads,
	}

	return info, nil
}

// BuffersInfo contains values returned from 'show buffers'
type BuffersInfo struct {
	Items []BuffersItem `json:"items"`
}

// BuffersItem represents single buffers item
type BuffersItem struct {
	ThreadID uint   `json:"thread_id"`
	Name     string `json:"name"`
	Index    uint   `json:"index"`
	Size     uint64 `json:"size"`
	Alloc    uint64 `json:"alloc"`
	Free     uint64 `json:"free"`
	NumAlloc uint64 `json:"num_alloc"`
	NumFree  uint64 `json:"num_free"`
}

var buffersRe = regexp.MustCompile(`^\s+(\d+)\s+(\w+(?:[ \-]\w+)*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+).*$`)

// GetBuffersInfo retrieves buffers info
func GetBuffersInfo(vppChan *govppapi.Channel) (*BuffersInfo, error) {
	data, err := RunCliCommand(vppChan, "show buffers")
	if err != nil {
		return nil, err
	}

	var items []BuffersItem

	for i, line := range strings.Split(string(data), "\n") {
		// Skip empty lines
		if strings.TrimSpace(line) == "" {
			continue
		}
		// Check first line
		if i == 0 {
			fields := strings.Fields(line)
			// Verify header
			if len(fields) != 8 || fields[0] != "Thread" {
				return nil, fmt.Errorf("invalid header for `show buffers` received: %q", line)
			}
			continue
		}

		// Parse lines using regexp
		matches := buffersRe.FindStringSubmatch(line)
		if len(matches)-1 != 8 {
			return nil, fmt.Errorf("parsing failed for `show buffers` line: %q", line)
		}
		fields := matches[1:]

		items = append(items, BuffersItem{
			ThreadID: uint(strToUint64(fields[0])),
			Name:     fields[1],
			Index:    uint(strToUint64(fields[2])),
			Size:     strToUint64(fields[3]),
			Alloc:    strToUint64(fields[4]),
			Free:     strToUint64(fields[5]),
			NumAlloc: strToUint64(fields[6]),
			NumFree:  strToUint64(fields[7]),
		})
	}

	info := &BuffersInfo{
		Items: items,
	}

	return info, nil
}

func strToFloat64(s string) float64 {
	num, err := strconv.ParseFloat(s, 10)
	if err != nil {
		return 0
	}
	return num
}

func strToUint64(s string) uint64 {
	s = strings.Replace(s, "k", "000", 1)
	num, err := strconv.ParseUint(s, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func cleanBytes(b []byte) []byte {
	return bytes.SplitN(b, []byte{0x00}, 2)[0]
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 trivago GmbH
//
// 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 format

import (
	"github.com/trivago/gollum/core"
	"github.com/trivago/tgo/ttesting"
	"testing"
)

func TestBase64(t *testing.T) {
	expect := ttesting.NewExpect(t)

	config := core.NewPluginConfig("", "format.Base64Encode")
	pluginEncode, err := core.NewPlugin(config)
	expect.NoError(err)

	config = core.NewPluginConfig("", "format.Base64Decode")
	pluginDecode, err := core.NewPlugin(config)
	expect.NoError(err)

	encoder, castedEncoder := pluginEncode.(*Base64Encode)
	expect.True(castedEncoder)
	decoder, castedDecoder := pluginDecode.(*Base64Decode)
	expect.True(castedDecoder)

	msg := core.NewMessage(nil, []byte("test"), 0)
	encoder.Format(msg)
	decoder.Format(msg)

	expect.Equal("test", string(msg.Data))
}
<commit_msg>Made format/base64_test compatible with v0.5.0<commit_after>// Copyright 2015-2016 trivago GmbH
//
// 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 format

import (
	"testing"

	"github.com/trivago/gollum/core"
	"github.com/trivago/tgo/ttesting"
)

func TestBase64(t *testing.T) {
	expect := ttesting.NewExpect(t)

	config := core.NewPluginConfig("", "format.Base64Encode")
	pluginEncode, err := core.NewPlugin(config)
	expect.NoError(err)

	config = core.NewPluginConfig("", "format.Base64Decode")
	pluginDecode, err := core.NewPlugin(config)
	expect.NoError(err)

	encoder, castedEncoder := pluginEncode.(*Base64Encode)
	expect.True(castedEncoder)
	decoder, castedDecoder := pluginDecode.(*Base64Decode)
	expect.True(castedDecoder)

	msg := core.NewMessage(nil, []byte("test"), 0, core.InvalidStreamID)
	result := encoder.Modulate(msg)
	expect.Equal(core.ModulateResultContinue, result)

	result = decoder.Modulate(msg)
	expect.Equal(core.ModulateResultContinue, result)

	expect.Equal("test", string(msg.Data()))
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 ebiten

import (
	"image"
	"math"

	"github.com/hajimehoshi/ebiten/internal/endian"
	"github.com/hajimehoshi/ebiten/internal/graphics"
)

// An ImagePart is deprecated (as of 1.1.0-alpha): Use ImageParts instead.
type ImagePart struct {
	Dst image.Rectangle
	Src image.Rectangle
}

// An ImageParts represents the parts of the destination image and the parts of the source image.
type ImageParts interface {
	Len() int
	Dst(i int) (x0, y0, x1, y1 int)
	Src(i int) (x0, y0, x1, y1 int)
}

// NOTE: Remove this in the future.
type imageParts []ImagePart

func (p imageParts) Len() int {
	return len(p)
}

func (p imageParts) Dst(i int) (x0, y0, x1, y1 int) {
	dst := &p[i].Dst
	return dst.Min.X, dst.Min.Y, dst.Max.X, dst.Max.Y
}

func (p imageParts) Src(i int) (x0, y0, x1, y1 int) {
	src := &p[i].Src
	return src.Min.X, src.Min.Y, src.Max.X, src.Max.Y
}

type wholeImage struct {
	width  int
	height int
}

func (w *wholeImage) Len() int {
	return 1
}

func (w *wholeImage) Dst(i int) (x0, y0, x1, y1 int) {
	return 0, 0, w.width, w.height
}

func (w *wholeImage) Src(i int) (x0, y0, x1, y1 int) {
	return 0, 0, w.width, w.height
}

func u(x, width2p int) int16 {
	return int16(math.MaxInt16 * x / width2p)
}

func v(y, height2p int) int16 {
	return int16(math.MaxInt16 * y / height2p)
}

type textureQuads struct {
	parts  ImageParts
	width  int
	height int
}

func (t *textureQuads) vertices() []uint8 {
	l := t.parts.Len()
	vertices := make([]uint8, 0, l*32)
	p := t.parts
	w, h := t.width, t.height
	width2p := int(graphics.NextPowerOf2Int32(int32(w)))
	height2p := int(graphics.NextPowerOf2Int32(int32(h)))
	for i := 0; i < l; i++ {
		dx0, dy0, dx1, dy1 := p.Dst(i)
		if dx0 == dx1 || dy0 == dy1 {
			continue
		}
		x0, y0, x1, y1 := int16(dx0), int16(dy0), int16(dx1), int16(dy1)
		sx0, sy0, sx1, sy1 := p.Src(i)
		if sx0 == sx1 || sy0 == sy1 {
			continue
		}
		u0, v0, u1, v1 := u(sx0, width2p), v(sy0, height2p), u(sx1, width2p), v(sy1, height2p)
		if endian.IsLittle() {
			vertices = append(vertices,
				uint8(x0), uint8(x0>>8), uint8(y0), uint8(y0>>8),
				uint8(u0), uint8(u0>>8), uint8(v0), uint8(v0>>8),
				uint8(x1), uint8(x1>>8), uint8(y0), uint8(y0>>8),
				uint8(u1), uint8(u1>>8), uint8(v0), uint8(v0>>8),
				uint8(x0), uint8(x0>>8), uint8(y1), uint8(y1>>8),
				uint8(u0), uint8(u0>>8), uint8(v1), uint8(v1>>8),
				uint8(x1), uint8(x1>>8), uint8(y1), uint8(y1>>8),
				uint8(u1), uint8(u1>>8), uint8(v1), uint8(v1>>8))
		} else {
			vertices = append(vertices,
				uint8(x0>>8), uint8(x0), uint8(y0>>8), uint8(y0),
				uint8(u0>>8), uint8(u0), uint8(v0>>8), uint8(v0),
				uint8(x1>>8), uint8(x1), uint8(y0>>8), uint8(y0),
				uint8(u1>>8), uint8(u1), uint8(v0>>8), uint8(v0),
				uint8(x0>>8), uint8(x0), uint8(y1>>8), uint8(y1),
				uint8(u0>>8), uint8(u0), uint8(v1>>8), uint8(v1),
				uint8(x1>>8), uint8(x1), uint8(y1>>8), uint8(y1),
				uint8(u1>>8), uint8(u1), uint8(v1>>8), uint8(v1))
		}
	}
	return vertices
}
<commit_msg>graphics: Optimization mainly for browsers<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 ebiten

import (
	"image"
	"math"

	"github.com/hajimehoshi/ebiten/internal/endian"
	"github.com/hajimehoshi/ebiten/internal/graphics"
)

// An ImagePart is deprecated (as of 1.1.0-alpha): Use ImageParts instead.
type ImagePart struct {
	Dst image.Rectangle
	Src image.Rectangle
}

// An ImageParts represents the parts of the destination image and the parts of the source image.
type ImageParts interface {
	Len() int
	Dst(i int) (x0, y0, x1, y1 int)
	Src(i int) (x0, y0, x1, y1 int)
}

// NOTE: Remove this in the future.
type imageParts []ImagePart

func (p imageParts) Len() int {
	return len(p)
}

func (p imageParts) Dst(i int) (x0, y0, x1, y1 int) {
	dst := &p[i].Dst
	return dst.Min.X, dst.Min.Y, dst.Max.X, dst.Max.Y
}

func (p imageParts) Src(i int) (x0, y0, x1, y1 int) {
	src := &p[i].Src
	return src.Min.X, src.Min.Y, src.Max.X, src.Max.Y
}

type wholeImage struct {
	width  int
	height int
}

func (w *wholeImage) Len() int {
	return 1
}

func (w *wholeImage) Dst(i int) (x0, y0, x1, y1 int) {
	return 0, 0, w.width, w.height
}

func (w *wholeImage) Src(i int) (x0, y0, x1, y1 int) {
	return 0, 0, w.width, w.height
}

func u(x, width2p int) int16 {
	return int16(math.MaxInt16 * x / width2p)
}

func v(y, height2p int) int16 {
	return int16(math.MaxInt16 * y / height2p)
}

type textureQuads struct {
	parts  ImageParts
	width  int
	height int
}

func (t *textureQuads) vertices() []uint8 {
	l := t.parts.Len()
	vertices := make([]uint8, l*32)
	p := t.parts
	w, h := t.width, t.height
	width2p := int(graphics.NextPowerOf2Int32(int32(w)))
	height2p := int(graphics.NextPowerOf2Int32(int32(h)))
	n := 0
	for i := 0; i < l; i++ {
		dx0, dy0, dx1, dy1 := p.Dst(i)
		if dx0 == dx1 || dy0 == dy1 {
			continue
		}
		x0, y0, x1, y1 := int16(dx0), int16(dy0), int16(dx1), int16(dy1)
		sx0, sy0, sx1, sy1 := p.Src(i)
		if sx0 == sx1 || sy0 == sy1 {
			continue
		}
		u0, v0, u1, v1 := u(sx0, width2p), v(sy0, height2p), u(sx1, width2p), v(sy1, height2p)
		// Use direct assign here. `append` function might be slow on browsers.
		if endian.IsLittle() {
			vertices[32*n] = uint8(x0)
			vertices[32*n+1] = uint8(x0 >> 8)
			vertices[32*n+2] = uint8(y0)
			vertices[32*n+3] = uint8(y0 >> 8)
			vertices[32*n+4] = uint8(u0)
			vertices[32*n+5] = uint8(u0 >> 8)
			vertices[32*n+6] = uint8(v0)
			vertices[32*n+7] = uint8(v0 >> 8)
			vertices[32*n+8] = uint8(x1)
			vertices[32*n+9] = uint8(x1 >> 8)
			vertices[32*n+10] = uint8(y0)
			vertices[32*n+11] = uint8(y0 >> 8)
			vertices[32*n+12] = uint8(u1)
			vertices[32*n+13] = uint8(u1 >> 8)
			vertices[32*n+14] = uint8(v0)
			vertices[32*n+15] = uint8(v0 >> 8)
			vertices[32*n+16] = uint8(x0)
			vertices[32*n+17] = uint8(x0 >> 8)
			vertices[32*n+18] = uint8(y1)
			vertices[32*n+19] = uint8(y1 >> 8)
			vertices[32*n+20] = uint8(u0)
			vertices[32*n+21] = uint8(u0 >> 8)
			vertices[32*n+22] = uint8(v1)
			vertices[32*n+23] = uint8(v1 >> 8)
			vertices[32*n+24] = uint8(x1)
			vertices[32*n+25] = uint8(x1 >> 8)
			vertices[32*n+26] = uint8(y1)
			vertices[32*n+27] = uint8(y1 >> 8)
			vertices[32*n+28] = uint8(u1)
			vertices[32*n+29] = uint8(u1 >> 8)
			vertices[32*n+30] = uint8(v1)
			vertices[32*n+31] = uint8(v1 >> 8)
		} else {
			vertices[32*n] = uint8(x0 >> 8)
			vertices[32*n+1] = uint8(x0)
			vertices[32*n+2] = uint8(y0 >> 8)
			vertices[32*n+3] = uint8(y0)
			vertices[32*n+4] = uint8(u0 >> 8)
			vertices[32*n+5] = uint8(u0)
			vertices[32*n+6] = uint8(v0 >> 8)
			vertices[32*n+7] = uint8(v0)
			vertices[32*n+8] = uint8(x1 >> 8)
			vertices[32*n+9] = uint8(x1)
			vertices[32*n+10] = uint8(y0 >> 8)
			vertices[32*n+11] = uint8(y0)
			vertices[32*n+12] = uint8(u1 >> 8)
			vertices[32*n+13] = uint8(u1)
			vertices[32*n+14] = uint8(v0 >> 8)
			vertices[32*n+15] = uint8(v0)
			vertices[32*n+16] = uint8(x0 >> 8)
			vertices[32*n+17] = uint8(x0)
			vertices[32*n+18] = uint8(y1 >> 8)
			vertices[32*n+19] = uint8(y1)
			vertices[32*n+20] = uint8(u0 >> 8)
			vertices[32*n+21] = uint8(u0)
			vertices[32*n+22] = uint8(v1 >> 8)
			vertices[32*n+23] = uint8(v1)
			vertices[32*n+24] = uint8(x1 >> 8)
			vertices[32*n+25] = uint8(x1)
			vertices[32*n+26] = uint8(y1 >> 8)
			vertices[32*n+27] = uint8(y1)
			vertices[32*n+28] = uint8(u1 >> 8)
			vertices[32*n+29] = uint8(u1)
			vertices[32*n+30] = uint8(v1 >> 8)
			vertices[32*n+31] = uint8(v1)
		}
		n++
	}
	return vertices[:n*32]
}
<|endoftext|>


<commit_before>package pgx

import (
	"bufio"
	"encoding/binary"
	"errors"
	"io"
)

// msgReader is a helper that reads values from a PostgreSQL message.
type msgReader struct {
	reader            *bufio.Reader
	buf               [128]byte
	msgBytesRemaining int32
	err               error
	log               func(lvl int, msg string, ctx ...interface{})
	shouldLog         func(lvl int) bool
}

// Err returns any error that the msgReader has experienced
func (r *msgReader) Err() error {
	return r.err
}

// fatal tells r that a Fatal error has occurred
func (r *msgReader) fatal(err error) {
	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.fatal", "error", err, "msgBytesRemaining", r.msgBytesRemaining)
	}
	r.err = err
}

// rxMsg reads the type and size of the next message.
func (r *msgReader) rxMsg() (byte, error) {
	if r.err != nil {
		return 0, r.err
	}

	if r.msgBytesRemaining > 0 {
		if r.shouldLog(LogLevelTrace) {
			r.log(LogLevelTrace, "msgReader.rxMsg discarding unread previous message", "msgBytesRemaining", r.msgBytesRemaining)
		}

		_, err := r.reader.Discard(int(r.msgBytesRemaining))
		if err != nil {
			return 0, err
		}
	}

	b := r.buf[0:5]
	_, err := io.ReadFull(r.reader, b)
	r.msgBytesRemaining = int32(binary.BigEndian.Uint32(b[1:])) - 4
	return b[0], err
}

func (r *msgReader) readByte() byte {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 1
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b, err := r.reader.ReadByte()
	if err != nil {
		r.fatal(err)
		return 0
	}

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readByte", "value", b, "byteAsString", string(b), "msgBytesRemaining", r.msgBytesRemaining)
	}

	return b
}

func (r *msgReader) readInt16() int16 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 2
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:2]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	n := int16(binary.BigEndian.Uint16(b))

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readInt16", "value", n, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return n
}

func (r *msgReader) readInt32() int32 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 4
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:4]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	n := int32(binary.BigEndian.Uint32(b))

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readInt32", "value", n, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return n
}

func (r *msgReader) readInt64() int64 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 8
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:8]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	n := int64(binary.BigEndian.Uint64(b))

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readInt64", "value", n, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return n
}

func (r *msgReader) readOid() Oid {
	return Oid(r.readInt32())
}

// readCString reads a null terminated string
func (r *msgReader) readCString() string {
	if r.err != nil {
		return ""
	}

	b, err := r.reader.ReadBytes(0)
	if err != nil {
		r.fatal(err)
		return ""
	}

	r.msgBytesRemaining -= int32(len(b))
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	s := string(b[0 : len(b)-1])

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readCString", "value", s, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return s
}

// readString reads count bytes and returns as string
func (r *msgReader) readString(count int32) string {
	if r.err != nil {
		return ""
	}

	r.msgBytesRemaining -= count
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	var b []byte
	if count <= int32(len(r.buf)) {
		b = r.buf[0:int(count)]
	} else {
		b = make([]byte, int(count))
	}

	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return ""
	}

	s := string(b)

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readString", "value", s, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return s
}

// readBytes reads count bytes and returns as []byte
func (r *msgReader) readBytes(count int32) []byte {
	if r.err != nil {
		return nil
	}

	r.msgBytesRemaining -= count
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return nil
	}

	b := make([]byte, int(count))

	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return nil
	}

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readBytes", "value", b, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return b
}
<commit_msg>Remove unnecessary buf from msgReader<commit_after>package pgx

import (
	"bufio"
	"encoding/binary"
	"errors"
	"io"
)

// msgReader is a helper that reads values from a PostgreSQL message.
type msgReader struct {
	reader            *bufio.Reader
	msgBytesRemaining int32
	err               error
	log               func(lvl int, msg string, ctx ...interface{})
	shouldLog         func(lvl int) bool
}

// Err returns any error that the msgReader has experienced
func (r *msgReader) Err() error {
	return r.err
}

// fatal tells r that a Fatal error has occurred
func (r *msgReader) fatal(err error) {
	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.fatal", "error", err, "msgBytesRemaining", r.msgBytesRemaining)
	}
	r.err = err
}

// rxMsg reads the type and size of the next message.
func (r *msgReader) rxMsg() (byte, error) {
	if r.err != nil {
		return 0, r.err
	}

	if r.msgBytesRemaining > 0 {
		if r.shouldLog(LogLevelTrace) {
			r.log(LogLevelTrace, "msgReader.rxMsg discarding unread previous message", "msgBytesRemaining", r.msgBytesRemaining)
		}

		_, err := r.reader.Discard(int(r.msgBytesRemaining))
		if err != nil {
			return 0, err
		}
	}

	b, err := r.reader.Peek(5)
	if err != nil {
		r.fatal(err)
		return 0, err
	}
	msgType := b[0]
	r.msgBytesRemaining = int32(binary.BigEndian.Uint32(b[1:])) - 4
	r.reader.Discard(5)
	return msgType, nil
}

func (r *msgReader) readByte() byte {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 1
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b, err := r.reader.ReadByte()
	if err != nil {
		r.fatal(err)
		return 0
	}

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readByte", "value", b, "byteAsString", string(b), "msgBytesRemaining", r.msgBytesRemaining)
	}

	return b
}

func (r *msgReader) readInt16() int16 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 2
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b, err := r.reader.Peek(2)
	if err != nil {
		r.fatal(err)
		return 0
	}

	n := int16(binary.BigEndian.Uint16(b))

	r.reader.Discard(2)

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readInt16", "value", n, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return n
}

func (r *msgReader) readInt32() int32 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 4
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b, err := r.reader.Peek(4)
	if err != nil {
		r.fatal(err)
		return 0
	}

	n := int32(binary.BigEndian.Uint32(b))

	r.reader.Discard(4)

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readInt32", "value", n, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return n
}

func (r *msgReader) readInt64() int64 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 8
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b, err := r.reader.Peek(8)
	if err != nil {
		r.fatal(err)
		return 0
	}

	n := int64(binary.BigEndian.Uint64(b))

	r.reader.Discard(8)

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readInt64", "value", n, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return n
}

func (r *msgReader) readOid() Oid {
	return Oid(r.readInt32())
}

// readCString reads a null terminated string
func (r *msgReader) readCString() string {
	if r.err != nil {
		return ""
	}

	b, err := r.reader.ReadBytes(0)
	if err != nil {
		r.fatal(err)
		return ""
	}

	r.msgBytesRemaining -= int32(len(b))
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	s := string(b[0 : len(b)-1])

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readCString", "value", s, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return s
}

// readString reads count bytes and returns as string
func (r *msgReader) readString(countI32 int32) string {
	if r.err != nil {
		return ""
	}

	r.msgBytesRemaining -= countI32
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	count := int(countI32)
	var s string

	if r.reader.Buffered() >= count {
		buf, _ := r.reader.Peek(count)
		s = string(buf)
		r.reader.Discard(count)
	} else {
		buf := make([]byte, int(count))
		_, err := io.ReadFull(r.reader, buf)
		if err != nil {
			r.fatal(err)
			return ""
		}
		s = string(buf)
	}

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readString", "value", s, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return s
}

// readBytes reads count bytes and returns as []byte
func (r *msgReader) readBytes(count int32) []byte {
	if r.err != nil {
		return nil
	}

	r.msgBytesRemaining -= count
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return nil
	}

	b := make([]byte, int(count))

	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return nil
	}

	if r.shouldLog(LogLevelTrace) {
		r.log(LogLevelTrace, "msgReader.readBytes", "value", b, "msgBytesRemaining", r.msgBytesRemaining)
	}

	return b
}
<|endoftext|>


<commit_before>package goprocess

import (
	"sync"
)

// process implements Process
type process struct {
	children map[*processLink]struct{} // process to close with us
	waitfors map[*processLink]struct{} // process to only wait for
	waiters  []*processLink            // processes that wait for us. for gc.

	teardown TeardownFunc  // called to run the teardown logic.
	waiting  chan struct{} // closed when CloseAfterChildrenClosed is called.
	closing  chan struct{} // closed once close starts.
	closed   chan struct{} // closed once close is done.
	closeErr error         // error to return to clients of Close()

	sync.Mutex
}

// newProcess constructs and returns a Process.
// It will call tf TeardownFunc exactly once:
//  **after** all children have fully Closed,
//  **after** entering <-Closing(), and
//  **before** <-Closed().
func newProcess(tf TeardownFunc) *process {
	return &process{
		teardown: tf,
		closed:   make(chan struct{}),
		closing:  make(chan struct{}),
		waitfors: make(map[*processLink]struct{}),
		children: make(map[*processLink]struct{}),
	}
}

func (p *process) WaitFor(q Process) {
	if q == nil {
		panic("waiting for nil process")
	}

	p.Lock()
	defer p.Unlock()

	select {
	case <-p.Closed():
		panic("Process cannot wait after being closed")
	default:
	}

	pl := newProcessLink(p, q)
	if p.waitfors == nil {
		// This may be nil when we're closing. In close, we'll keep
		// reading this map till it stays nil.
		p.waitfors = make(map[*processLink]struct{}, 1)
	}
	p.waitfors[pl] = struct{}{}
	go pl.AddToChild()
}

func (p *process) AddChildNoWait(child Process) {
	if child == nil {
		panic("adding nil child process")
	}

	p.Lock()
	defer p.Unlock()

	select {
	case <-p.Closing():
		// Either closed or closing, close child immediately. This is
		// correct because we aren't asked to _wait_ on this child.
		go child.Close()
		// Wait for the child to start closing so the child is in the
		// "correct" state after this function finishes (see #17).
		<-child.Closing()
		return
	default:
	}

	pl := newProcessLink(p, child)
	p.children[pl] = struct{}{}
	go pl.AddToChild()
}

func (p *process) AddChild(child Process) {
	if child == nil {
		panic("adding nil child process")
	}

	p.Lock()
	defer p.Unlock()

	pl := newProcessLink(p, child)

	select {
	case <-p.Closed():
		// AddChild must not be called on a dead process. Maybe that's
		// too strict?
		panic("Process cannot add children after being closed")
	default:
	}

	select {
	case <-p.Closing():
		// Already closing, close child in background.
		go child.Close()
		// Wait for the child to start closing so the child is in the
		// "correct" state after this function finishes (see #17).
		<-child.Closing()
	default:
		// Only add the child when not closing. When closing, just add
		// it to the "waitfors" list.
		p.children[pl] = struct{}{}
	}

	if p.waitfors == nil {
		// This may be be nil when we're closing. In close, we'll keep
		// reading this map till it stays nil.
		p.waitfors = make(map[*processLink]struct{}, 1)
	}
	p.waitfors[pl] = struct{}{}
	go pl.AddToChild()
}

func (p *process) Go(f ProcessFunc) Process {
	child := newProcess(nil)
	waitFor := newProcess(nil)
	child.WaitFor(waitFor) // prevent child from closing

	// add child last, to prevent a closing parent from
	// closing all of them prematurely, before running the func.
	p.AddChild(child)
	go func() {
		f(child)
		waitFor.Close()            // allow child to close.
		child.CloseAfterChildren() // close to tear down.
	}()
	return child
}

// SetTeardown to assign a teardown function
func (p *process) SetTeardown(tf TeardownFunc) {
	if tf == nil {
		panic("cannot set nil TeardownFunc")
	}

	p.Lock()
	if p.teardown != nil {
		panic("cannot SetTeardown twice")
	}

	p.teardown = tf
	select {
	case <-p.Closed():
		p.closeErr = tf()
	default:
	}
	p.Unlock()
}

// Close is the external close function.
// it's a wrapper around internalClose that waits on Closed()
func (p *process) Close() error {
	p.Lock()

	// if already closing, or closed, get out. (but wait!)
	select {
	case <-p.Closing():
		p.Unlock()
		<-p.Closed()
		return p.closeErr
	default:
	}

	p.doClose()
	p.Unlock()
	return p.closeErr
}

func (p *process) Closing() <-chan struct{} {
	return p.closing
}

func (p *process) Closed() <-chan struct{} {
	return p.closed
}

func (p *process) Err() error {
	<-p.Closed()
	return p.closeErr
}

// the _actual_ close process.
func (p *process) doClose() {
	// this function is only be called once (protected by p.Lock()).
	// and it will panic (on closing channels) otherwise.

	close(p.closing) // signal that we're shutting down (Closing)

	// We won't add any children after we start closing so we can do this
	// once.
	for plc, _ := range p.children {
		child := plc.Child()
		if child != nil { // check because child may already have been removed.
			go child.Close() // force all children to shut down
		}

		// safe to call multiple times per link
		plc.ParentClear()
	}
	p.children = nil // clear them. release memory.

	// We may repeatedly continue to add waiters while we wait to close so
	// we have to do this in a loop.
	for len(p.waitfors) > 0 {
		// we must be careful not to iterate over waitfors directly, as it may
		// change under our feet.
		wf := p.waitfors
		p.waitfors = nil // clear them. release memory.
		for w, _ := range wf {
			// Here, we wait UNLOCKED, so that waitfors who are in the middle of
			// adding a child to us can finish. we will immediately close the child.
			p.Unlock()
			<-w.ChildClosed() // wait till all waitfors are fully closed (before teardown)
			p.Lock()

			// safe to call multiple times per link
			w.ParentClear()
		}
	}

	if p.teardown != nil {
		p.closeErr = p.teardown() // actually run the close logic (ok safe to teardown)
	}
	close(p.closed) // signal that we're shut down (Closed)

	// go remove all the parents from the process links. optimization.
	go func(waiters []*processLink) {
		for _, pl := range waiters {
			pl.ClearChild()
			pr, ok := pl.Parent().(*process)
			if !ok {
				// parent has already been called to close
				continue
			}
			pr.Lock()
			delete(pr.waitfors, pl)
			delete(pr.children, pl)
			pr.Unlock()
		}
	}(p.waiters) // pass in so
	p.waiters = nil // clear them. release memory.
}

// We will only wait on the children we have now.
// We will not wait on children added subsequently.
// this may change in the future.
func (p *process) CloseAfterChildren() error {
	p.Lock()
	select {
	case <-p.Closed():
		p.Unlock()
		return p.Close() // get error. safe, after p.Closed()
	case <-p.waiting: // already called it.
		p.Unlock()
		<-p.Closed()
		return p.Close() // get error. safe, after p.Closed()
	default:
	}
	p.Unlock()

	// here only from one goroutine.

	nextToWaitFor := func() Process {
		p.Lock()
		defer p.Unlock()
		for e, _ := range p.waitfors {
			c := e.Child()
			if c == nil {
				continue
			}

			select {
			case <-c.Closed():
			default:
				return c
			}
		}
		return nil
	}

	// wait for all processes we're waiting for are closed.
	// the semantics here are simple: we will _only_ close
	// if there are no processes currently waiting for.
	for next := nextToWaitFor(); next != nil; next = nextToWaitFor() {
		<-next.Closed()
	}

	// YAY! we're done. close
	return p.Close()
}
<commit_msg>remove unused channel<commit_after>package goprocess

import (
	"sync"
)

// process implements Process
type process struct {
	children map[*processLink]struct{} // process to close with us
	waitfors map[*processLink]struct{} // process to only wait for
	waiters  []*processLink            // processes that wait for us. for gc.

	teardown TeardownFunc  // called to run the teardown logic.
	closing  chan struct{} // closed once close starts.
	closed   chan struct{} // closed once close is done.
	closeErr error         // error to return to clients of Close()

	sync.Mutex
}

// newProcess constructs and returns a Process.
// It will call tf TeardownFunc exactly once:
//  **after** all children have fully Closed,
//  **after** entering <-Closing(), and
//  **before** <-Closed().
func newProcess(tf TeardownFunc) *process {
	return &process{
		teardown: tf,
		closed:   make(chan struct{}),
		closing:  make(chan struct{}),
		waitfors: make(map[*processLink]struct{}),
		children: make(map[*processLink]struct{}),
	}
}

func (p *process) WaitFor(q Process) {
	if q == nil {
		panic("waiting for nil process")
	}

	p.Lock()
	defer p.Unlock()

	select {
	case <-p.Closed():
		panic("Process cannot wait after being closed")
	default:
	}

	pl := newProcessLink(p, q)
	if p.waitfors == nil {
		// This may be nil when we're closing. In close, we'll keep
		// reading this map till it stays nil.
		p.waitfors = make(map[*processLink]struct{}, 1)
	}
	p.waitfors[pl] = struct{}{}
	go pl.AddToChild()
}

func (p *process) AddChildNoWait(child Process) {
	if child == nil {
		panic("adding nil child process")
	}

	p.Lock()
	defer p.Unlock()

	select {
	case <-p.Closing():
		// Either closed or closing, close child immediately. This is
		// correct because we aren't asked to _wait_ on this child.
		go child.Close()
		// Wait for the child to start closing so the child is in the
		// "correct" state after this function finishes (see #17).
		<-child.Closing()
		return
	default:
	}

	pl := newProcessLink(p, child)
	p.children[pl] = struct{}{}
	go pl.AddToChild()
}

func (p *process) AddChild(child Process) {
	if child == nil {
		panic("adding nil child process")
	}

	p.Lock()
	defer p.Unlock()

	pl := newProcessLink(p, child)

	select {
	case <-p.Closed():
		// AddChild must not be called on a dead process. Maybe that's
		// too strict?
		panic("Process cannot add children after being closed")
	default:
	}

	select {
	case <-p.Closing():
		// Already closing, close child in background.
		go child.Close()
		// Wait for the child to start closing so the child is in the
		// "correct" state after this function finishes (see #17).
		<-child.Closing()
	default:
		// Only add the child when not closing. When closing, just add
		// it to the "waitfors" list.
		p.children[pl] = struct{}{}
	}

	if p.waitfors == nil {
		// This may be be nil when we're closing. In close, we'll keep
		// reading this map till it stays nil.
		p.waitfors = make(map[*processLink]struct{}, 1)
	}
	p.waitfors[pl] = struct{}{}
	go pl.AddToChild()
}

func (p *process) Go(f ProcessFunc) Process {
	child := newProcess(nil)
	waitFor := newProcess(nil)
	child.WaitFor(waitFor) // prevent child from closing

	// add child last, to prevent a closing parent from
	// closing all of them prematurely, before running the func.
	p.AddChild(child)
	go func() {
		f(child)
		waitFor.Close()            // allow child to close.
		child.CloseAfterChildren() // close to tear down.
	}()
	return child
}

// SetTeardown to assign a teardown function
func (p *process) SetTeardown(tf TeardownFunc) {
	if tf == nil {
		panic("cannot set nil TeardownFunc")
	}

	p.Lock()
	if p.teardown != nil {
		panic("cannot SetTeardown twice")
	}

	p.teardown = tf
	select {
	case <-p.Closed():
		p.closeErr = tf()
	default:
	}
	p.Unlock()
}

// Close is the external close function.
// it's a wrapper around internalClose that waits on Closed()
func (p *process) Close() error {
	p.Lock()

	// if already closing, or closed, get out. (but wait!)
	select {
	case <-p.Closing():
		p.Unlock()
		<-p.Closed()
		return p.closeErr
	default:
	}

	p.doClose()
	p.Unlock()
	return p.closeErr
}

func (p *process) Closing() <-chan struct{} {
	return p.closing
}

func (p *process) Closed() <-chan struct{} {
	return p.closed
}

func (p *process) Err() error {
	<-p.Closed()
	return p.closeErr
}

// the _actual_ close process.
func (p *process) doClose() {
	// this function is only be called once (protected by p.Lock()).
	// and it will panic (on closing channels) otherwise.

	close(p.closing) // signal that we're shutting down (Closing)

	// We won't add any children after we start closing so we can do this
	// once.
	for plc, _ := range p.children {
		child := plc.Child()
		if child != nil { // check because child may already have been removed.
			go child.Close() // force all children to shut down
		}

		// safe to call multiple times per link
		plc.ParentClear()
	}
	p.children = nil // clear them. release memory.

	// We may repeatedly continue to add waiters while we wait to close so
	// we have to do this in a loop.
	for len(p.waitfors) > 0 {
		// we must be careful not to iterate over waitfors directly, as it may
		// change under our feet.
		wf := p.waitfors
		p.waitfors = nil // clear them. release memory.
		for w, _ := range wf {
			// Here, we wait UNLOCKED, so that waitfors who are in the middle of
			// adding a child to us can finish. we will immediately close the child.
			p.Unlock()
			<-w.ChildClosed() // wait till all waitfors are fully closed (before teardown)
			p.Lock()

			// safe to call multiple times per link
			w.ParentClear()
		}
	}

	if p.teardown != nil {
		p.closeErr = p.teardown() // actually run the close logic (ok safe to teardown)
	}
	close(p.closed) // signal that we're shut down (Closed)

	// go remove all the parents from the process links. optimization.
	go func(waiters []*processLink) {
		for _, pl := range waiters {
			pl.ClearChild()
			pr, ok := pl.Parent().(*process)
			if !ok {
				// parent has already been called to close
				continue
			}
			pr.Lock()
			delete(pr.waitfors, pl)
			delete(pr.children, pl)
			pr.Unlock()
		}
	}(p.waiters) // pass in so
	p.waiters = nil // clear them. release memory.
}

// We will only wait on the children we have now.
// We will not wait on children added subsequently.
// this may change in the future.
func (p *process) CloseAfterChildren() error {
	p.Lock()
	select {
	case <-p.Closed():
		p.Unlock()
		return p.Close() // get error. safe, after p.Closed()
	default:
	}
	p.Unlock()

	// here only from one goroutine.

	nextToWaitFor := func() Process {
		p.Lock()
		defer p.Unlock()
		for e, _ := range p.waitfors {
			c := e.Child()
			if c == nil {
				continue
			}

			select {
			case <-c.Closed():
			default:
				return c
			}
		}
		return nil
	}

	// wait for all processes we're waiting for are closed.
	// the semantics here are simple: we will _only_ close
	// if there are no processes currently waiting for.
	for next := nextToWaitFor(); next != nil; next = nextToWaitFor() {
		<-next.Closed()
	}

	// YAY! we're done. close
	return p.Close()
}
<|endoftext|>


<commit_before>// Package types is where exportable API structures go. This is so we can share
// the XML marshalling and unmarshalling with the services.
package types

import (
	"encoding/xml"
	"time"
)

type Version struct {
	XMLName xml.Name `xml:"version"`
	App     *App     `xml:"app"`
	Package *Package `xml:"package"`
}

type App struct {
	XMLName  xml.Name  `xml:"app"`
	Id       string    `xml:"id,attr"`
	Version  string    `xml:"version,attr"`
	Track    string    `xml:"track,attr"`
	Date     time.Time `xml:"-"`
	IsActive time.Time `xml:"-"`
}

type Package struct {
	XMLName              xml.Name `xml:"package"`
	Name                 string   `xml:"name,attr"`      // Package filename
	Size                 string   `xml:"size,attr"`      // Size of the file (in bytes)
	Path                 string   `xml:"path,attr"`      // Path from the root to the file
	Sha1Sum              string   `xml:"sha1sum,attr"`   // SHA-1 hash of the file
	Sha256Sum            string   `xml:"sha256sum,attr"` // Sha-256 hash of the file (extension)
	Required             bool     `xml:"required,attr"`
	MetadataSignatureRsa string   `xml:"MetadataSignatureRsa,attr,omitempty" json:"metadata_signature_rsa"`
	MetadataSize         string   `xml:"MetadataSize,attr,omitempty" json:"metadata_size"`
}
<commit_msg>fix(update/types): IsActive is a boolean<commit_after>// Package types is where exportable API structures go. This is so we can share
// the XML marshalling and unmarshalling with the services.
package types

import (
	"encoding/xml"
	"time"
)

type Version struct {
	XMLName xml.Name `xml:"version"`
	App     *App     `xml:"app"`
	Package *Package `xml:"package"`
}

type App struct {
	XMLName  xml.Name  `xml:"app"`
	Id       string    `xml:"id,attr"`
	Version  string    `xml:"version,attr"`
	Track    string    `xml:"track,attr"`
	Date     time.Time `xml:"-"`
	IsActive bool      `xml:"-"`
}

type Package struct {
	XMLName              xml.Name `xml:"package"`
	Name                 string   `xml:"name,attr"`      // Package filename
	Size                 string   `xml:"size,attr"`      // Size of the file (in bytes)
	Path                 string   `xml:"path,attr"`      // Path from the root to the file
	Sha1Sum              string   `xml:"sha1sum,attr"`   // SHA-1 hash of the file
	Sha256Sum            string   `xml:"sha256sum,attr"` // Sha-256 hash of the file (extension)
	Required             bool     `xml:"required,attr"`
	MetadataSignatureRsa string   `xml:"MetadataSignatureRsa,attr,omitempty" json:"metadata_signature_rsa"`
	MetadataSize         string   `xml:"MetadataSize,attr,omitempty" json:"metadata_size"`
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"os"
	_ "strconv"
	"strings"

	car "github.com/mohae/carchivum"
	"github.com/mohae/contour"
	jww "github.com/spf13/jwalterweatherman"
)

func Create(destination string, sources ...string) (string, error) {
	var err error
	var message string

	fmt.Printf("\nCreate %q from %v\n", destination, sources)

	switch contour.GetString(Format) {
	case "tar":
		message, err = createTar(destination, sources...)
	case "zip":
		message, err = createZip(destination, sources...)
	default:
		err = fmt.Errorf("%s not supported", contour.GetString(Format))
	}

	if err != nil {
		jww.ERROR.Print(err)
		return "", err
	}

	return message, nil
}

func createZip(destination string, sources ...string) (string, error) {
	var err error

	jww.INFO.Printf("Creating zip: %s from %s", destination, sources)
	zipper := car.NewZip()
	zipper.Car.Name = destination
	zipper.UseFullpath = contour.GetBool("usefullpath")
	_, err = zipper.Create(destination, sources...)
	if err != nil {
		jww.ERROR.Print(err)
		return "", err
	}

	return zipper.Message(), nil
}

func createTar(destination string, sources ...string) (string, error) {
	var err error

	jww.INFO.Printf("Creating tar: %s from %s", destination, sources)
	tballer := car.NewTar()
	t := contour.GetString("type")
	if t != "" {
		f := car.FormatFromString(t)
		if f == car.UnsupportedFmt {
			fmt.Errorf("Unuspported format: %s", t)
			jww.ERROR.Print(err)
			return "", err
		}
		tballer.Format = f
	}
	fmt.Println(t)
	tballer.Name = destination
	tballer.Owner = contour.GetInt("owner")
	tballer.Group = contour.GetInt("group")
	tballer.Mode = os.FileMode(contour.GetInt64("mode"))

	//	tabller.Exclude = contour.GetString("exclude")
	tballer.ExcludeAnchored = contour.GetString("exclude-anchored")
	temp := contour.GetString("exclude-ext")
	if temp != "" {
		tballer.ExcludeExt = strings.Split(temp, ",")
		tballer.ExcludeExtCount = len(tballer.ExcludeExt)
	}

	tballer.IncludeAnchored = contour.GetString("include-anchored")
	temp = contour.GetString("include-ext")
	if temp != "" {
		tballer.IncludeExt = strings.Split(temp, ",")
		tballer.IncludeExtCount = len(tballer.IncludeExt)
	}

	// TODO figure out how to convert the incoming time info to time.Time
	// tballer.NewerMTime = contour.GetTime("newer-mtime")
	//	tballer.UseFullpath = contour.GetBool("usefullpath")
	_, err = tballer.Create(destination, sources...)
	if err != nil {
		jww.ERROR.Print(err)
		return "", err
	}

	return tballer.Message(), nil
}
<commit_msg>tar.Mode -> tar.FileMode<commit_after>package app

import (
	"fmt"
	"os"
	_ "strconv"
	"strings"

	car "github.com/mohae/carchivum"
	"github.com/mohae/contour"
	jww "github.com/spf13/jwalterweatherman"
)

func Create(destination string, sources ...string) (string, error) {
	var err error
	var message string

	fmt.Printf("\nCreate %q from %v\n", destination, sources)

	switch contour.GetString(Format) {
	case "tar":
		message, err = createTar(destination, sources...)
	case "zip":
		message, err = createZip(destination, sources...)
	default:
		err = fmt.Errorf("%s not supported", contour.GetString(Format))
	}

	if err != nil {
		jww.ERROR.Print(err)
		return "", err
	}

	return message, nil
}

func createZip(destination string, sources ...string) (string, error) {
	var err error

	jww.INFO.Printf("Creating zip: %s from %s", destination, sources)
	zipper := car.NewZip()
	zipper.Car.Name = destination
	zipper.UseFullpath = contour.GetBool("usefullpath")
	_, err = zipper.Create(destination, sources...)
	if err != nil {
		jww.ERROR.Print(err)
		return "", err
	}

	return zipper.Message(), nil
}

func createTar(destination string, sources ...string) (string, error) {
	var err error

	jww.INFO.Printf("Creating tar: %s from %s", destination, sources)
	tballer := car.NewTar()
	t := contour.GetString("type")
	if t != "" {
		f := car.FormatFromString(t)
		if f == car.UnsupportedFmt {
			fmt.Errorf("Unuspported format: %s", t)
			jww.ERROR.Print(err)
			return "", err
		}
		tballer.Format = f
	}
	fmt.Println(t)
	tballer.Name = destination
	tballer.Owner = contour.GetInt("owner")
	tballer.Group = contour.GetInt("group")
	tballer.FileMode = os.FileMode(contour.GetInt64("mode"))

	//	tabller.Exclude = contour.GetString("exclude")
	tballer.ExcludeAnchored = contour.GetString("exclude-anchored")
	temp := contour.GetString("exclude-ext")
	if temp != "" {
		tballer.ExcludeExt = strings.Split(temp, ",")
		tballer.ExcludeExtCount = len(tballer.ExcludeExt)
	}

	tballer.IncludeAnchored = contour.GetString("include-anchored")
	temp = contour.GetString("include-ext")
	if temp != "" {
		tballer.IncludeExt = strings.Split(temp, ",")
		tballer.IncludeExtCount = len(tballer.IncludeExt)
	}

	// TODO figure out how to convert the incoming time info to time.Time
	// tballer.NewerMTime = contour.GetTime("newer-mtime")
	//	tballer.UseFullpath = contour.GetBool("usefullpath")
	_, err = tballer.Create(destination, sources...)
	if err != nil {
		jww.ERROR.Print(err)
		return "", err
	}

	return tballer.Message(), nil
}
<|endoftext|>


<commit_before>package Golf

import (
	"bytes"
	"fmt"
	"testing"
)

func TestConfig(t *testing.T) {
	cases := []struct {
		key   string
		value interface{}
	}{
		{"foo", "bar"},
		{"foo", 123},
		{"foo", true},
		{"foo", 56.23},
		{"foo/bar", "bar"},
		{"123/foo/bar", "bar"},
		{"foo/bar/bar/bar", "bar"},
	}

	defaultValue := "None"

	for _, c := range cases {
		app := New()
		config := NewConfig(app)
		config.Set(c.key, c.value)

		value, err := config.Get(c.key, defaultValue)
		if err != nil {
			t.Error(err)
		}
		if value != c.value {
			t.Errorf("Value not match: %q != %q, key: %q", value, c.value, c.key)
		}
	}
}

func TestConfigWithMultipleEntires(t *testing.T) {
	settings := []struct {
		key, value string
	}{
		{"foo/bar/bar", "bar"},
		{"foo/bar/bar2", "bar2"},
		{"foo/bar3", "bar3"},
		{"foo2", "bar4"},
	}

	app := New()
	config := NewConfig(app)

	for _, c := range settings {
		config.Set(c.key, c.value)
	}

	for _, c := range settings {
		value, err := config.Get(c.key, "None")
		if err != nil {
			t.Error(err)
		}
		if value != c.value {
			t.Errorf("Value not match: %q != %q, key: %q", value, c.value, c.key)
		}
	}
}

func TestFromJSON(t *testing.T) {
	reader := bytes.NewReader([]byte(`{"cool" : {"foo" : "bar"}}`))
	config, err := ConfigFromJSON(reader)
	if err != nil {
		t.Error(err)
		t.Fail()
	}
	value, err := config.GetString("cool/foo", "")
	if err != nil {
		t.Error(err)
		t.Fail()
	}
	if value != "bar" {
		t.Errorf("expected value to be abc but it was %v", value)
	}
}
<commit_msg>[fix] Fixed imported and not used.<commit_after>package Golf

import (
	"bytes"
	"testing"
)

func TestConfig(t *testing.T) {
	cases := []struct {
		key   string
		value interface{}
	}{
		{"foo", "bar"},
		{"foo", 123},
		{"foo", true},
		{"foo", 56.23},
		{"foo/bar", "bar"},
		{"123/foo/bar", "bar"},
		{"foo/bar/bar/bar", "bar"},
	}

	defaultValue := "None"

	for _, c := range cases {
		app := New()
		config := NewConfig(app)
		config.Set(c.key, c.value)

		value, err := config.Get(c.key, defaultValue)
		if err != nil {
			t.Error(err)
		}
		if value != c.value {
			t.Errorf("Value not match: %q != %q, key: %q", value, c.value, c.key)
		}
	}
}

func TestConfigWithMultipleEntires(t *testing.T) {
	settings := []struct {
		key, value string
	}{
		{"foo/bar/bar", "bar"},
		{"foo/bar/bar2", "bar2"},
		{"foo/bar3", "bar3"},
		{"foo2", "bar4"},
	}

	app := New()
	config := NewConfig(app)

	for _, c := range settings {
		config.Set(c.key, c.value)
	}

	for _, c := range settings {
		value, err := config.Get(c.key, "None")
		if err != nil {
			t.Error(err)
		}
		if value != c.value {
			t.Errorf("Value not match: %q != %q, key: %q", value, c.value, c.key)
		}
	}
}

func TestFromJSON(t *testing.T) {
	reader := bytes.NewReader([]byte(`{"cool" : {"foo" : "bar"}}`))
	config, err := ConfigFromJSON(reader)
	if err != nil {
		t.Error(err)
		t.Fail()
	}
	value, err := config.GetString("cool/foo", "")
	if err != nil {
		t.Error(err)
		t.Fail()
	}
	if value != "bar" {
		t.Errorf("expected value to be abc but it was %v", value)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func TestSplitEnvArg(t *testing.T) {
	o := splitEnvArg("Charles  ,  Dickens")
	if o[0] != "Charles" {
		t.Fail()
	}
	if o[1] != "Dickens" {
		t.Fail()
	}
	spacedString := splitEnvArg("Charles     Dickens")
	if spacedString[0] != "Charles" {
		t.Errorf("Expecting Charles got %s", spacedString)
	}

	if v := splitEnvArg(" charles  dickens"); v[0] != "charles" {
		t.Errorf("Expected charles got %s", v)
	}

	if v := splitEnvArg(" charles  "); v[0] != "charles" {
		t.Errorf("Expected charles got %s", v)
	}

	multiLine := splitEnvArg(`
	 s3://charles/dickens |

	    sm://oliver/twist
	`)

	if multiLine[0] != "s3://charles/dickers" &&
		multiLine[1] != "sm://oliver/twist" {
		t.Errorf("Error splitting with newlines %s", multiLine)
	}
}

func TestGetSources(t *testing.T) {
	emptyArgs := []string{}
	emptyEnv := ""
	sourcesArgs := []string{
		"s3://bucket/one.json",
		"s3://bucket/two.json",
	}
	sourcesEnv := "s3://bucket/one.json, s3://bucket/two.json"
	config := NewConfig()

	// Passing in no sources is fine
	config.SetSources(emptyArgs, emptyEnv)
	if config.LenSources() != 0 {
		t.Errorf("Expected an error parsing empty args and env")
	}

	if config.SetSources(sourcesArgs, ""); config.sources[0].Host != "bucket" {
		t.Errorf("Host is actually %s", config.sources[0].Host)
	}
	if config.SetSources(emptyArgs, sourcesEnv); config.sources[1].Path != "/two.json" {
		t.Errorf("Path is actually %s", config.sources[1].Path)
	}

	err := config.SetSources([]string{":"}, "")
	if err == nil || config.LenSources() != 0 {
		t.Errorf("Expected a parsing url for the : url")
	}
}
<commit_msg>Add test for quoted source<commit_after>package main

import (
	"testing"
)

func TestSplitEnvArg(t *testing.T) {
	o := splitEnvArg("Charles  ,  Dickens")
	if o[0] != "Charles" {
		t.Fail()
	}
	if o[1] != "Dickens" {
		t.Fail()
	}
	spacedString := splitEnvArg("Charles     Dickens")
	if spacedString[0] != "Charles" {
		t.Errorf("Expecting Charles got %s", spacedString)
	}

	if v := splitEnvArg(" charles  dickens"); v[0] != "charles" {
		t.Errorf("Expected charles got %s", v)
	}

	if v := splitEnvArg(" charles  "); v[0] != "charles" {
		t.Errorf("Expected charles got %s", v)
	}

	multiLine := splitEnvArg(`
	 s3://charles/dickens |

	    sm://oliver/twist
	`)

	if multiLine[0] != "s3://charles/dickers" &&
		multiLine[1] != "sm://oliver/twist" {
		t.Errorf("Error splitting with newlines %s", multiLine)
	}
}

func TestGetSources(t *testing.T) {
	emptyArgs := []string{}
	emptyEnv := ""
	sourcesArgs := []string{
		"s3://bucket/one.json",
		"s3://bucket/two.json",
	}
	sourcesEnv := "s3://bucket/one.json, s3://bucket/two.json"
	config := NewConfig()

	// Passing in no sources is fine
	config.SetSources(emptyArgs, emptyEnv)
	if config.LenSources() != 0 {
		t.Errorf("Expected an error parsing empty args and env")
	}

	if config.SetSources(sourcesArgs, ""); config.sources[0].Host != "bucket" {
		t.Errorf("Host is actually %s", config.sources[0].Host)
	}
	if config.SetSources(emptyArgs, sourcesEnv); config.sources[1].Path != "/two.json" {
		t.Errorf("Path is actually %s", config.sources[1].Path)
	}

	err := config.SetSources([]string{":"}, "")
	if err == nil || config.LenSources() != 0 {
		t.Errorf("Expected a parsing url for the : url")
	}

	err = config.SetSources([]string{}, `"sm://nicholas/nickleby, sm://esther/summerson`)
	if err == nil || config.LenSources() != 0 {
		t.Errorf("Expected a parsing url for the : url")
	}
}
<|endoftext|>


<commit_before>package main

import "testing"
import "fmt"

func Test_DefaultConfig_1(t *testing.T) {
	if defaultConfig.ImageName == "busybox" {
		t.Log("default ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("default ImageName failed: expected busybox got %s", defaultConfig.ImageName))
	}
}

func Test_SimpleConfig_1(t *testing.T) {
	c, err := loadConfigFromString([]byte(``), "fred")
	if err != nil {
		t.Error(err)
	}
	c, err = loadConfigFromString([]byte(`[dockersh]
imagename = testimage`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.ImageName == "testimage" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
}

func Test_UserConfig_1(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
shell = someshell

[user "fred"]
imagename = fredsimage
containerusername = bill`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.Shell == "someshell" {
		t.Log("set Shell in dockersh config passed.")
	} else {
		t.Error(fmt.Sprintf("Expected Shell dockersg got %s", c.Shell))
	}
	if c.ContainerUsername == "bill" {
		t.Log("set ContainerUserName in user config passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ContainerUserName bill got %s", c.ContainerUsername))
	}
	if c.ImageName == "fredsimage" {
		t.Log("set ImageName in user config passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName fredsimage got %s", c.ImageName))
	}
}

func Test_IniConfig_2(t *testing.T) {
	c := Configuration{BlacklistUserConfig: []string{"containerusername"}, ContainerUsername: "default_contun", ImageName: "default"}
	n, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
containerusername = shouldbeblacklisted`), "fred")
	c = mergeConfigs(c, n, true)
	if err != nil {
		t.Error(err)
	}
	if c.ImageName == "testimage" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
	if c.ContainerUsername == "default_contun" {
		t.Log("blacklising worked, value not changed")
	} else {
		t.Error("blacklisting failed")
	}
}

func Test_Config_3(t *testing.T) {
	c := Configuration{BlacklistUserConfig: []string{}, ContainerUsername: "default_contun", Shell: "default_shell"}
	c, err := loadConfigFromString([]byte(`[dockersh]
shell = global_default
containerusername = global_default
mounthometo = somewhere
blacklistuserconfig = containerusername
blacklistuserconfig = mounthometo`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.Shell != "global_default" {
		t.Error("Set shell to global_default failed")
	}
	if c.ContainerUsername != "global_default" {
		t.Error("Set un to global default failed")
	}
	if c.MountHomeTo != "somewhere" {
		t.Error("Set mounthome to global default failed")
	}
	newc, err := loadConfigFromString([]byte(`[dockersh]
shell = user_value
containerusername = user_value
mounthometo = somewhere_else`), "fred")
	if err != nil {
		t.Error(err)
	}
	c = mergeConfigs(c, newc, true)
	if c.Shell != "user_value" {
		t.Error("Local defaults not applying over global defaults")
	} else {
		t.Log("c.shell not overridden")
	}
	if c.ContainerUsername != "global_default" {
		t.Error("Blacklist of container_username in global config failed")
	}
	if c.MountHomeTo != "somewhere" {
		t.Error("Blacklist mounthome in global config failed")
	}
}

func Test_IniConfig_4(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
blacklistuserconfig = containerusername
containerusername = default_contun
imagename = default`), "fred")
	newc, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
containerusername = shouldbeblacklisted`), "fred")
	if err != nil {
		t.Error(err)
	}
	c = mergeConfigs(c, newc, true)
	if c.ImageName == "testimage" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
	if c.ContainerUsername != "default_contun" {
		t.Error("blacklising disabled, value changed")
	} else {
		t.Log("blacklisting enabled, value has not changed")
	}
}

func Test_IniConfig_5(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
blacklistuserconfig = containerusername
blacklistuserconfig = imagename
containerusername = default_contun
imagename = default`), "fred")
	newc, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
containerusername = shouldbeblacklisted`), "fred")
	if err != nil {
		t.Error(err)
	}
	c = mergeConfigs(c, newc, true)
	if c.ImageName == "default" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName default got %s", c.ImageName))
	}
	if c.ContainerUsername != "default_contun" {
		t.Error("blacklising disabled, value changed")
	} else {
		t.Log("blacklisting enabled, value has not changed")
	}
}

func Test_IniConfig_6(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
blacklistuserconfig = imagename
imagename = default

[user "fred"]
imagename = testimage
`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.ImageName == "testimage" {
		t.Log("set ImageName in user section when blacklisted in [dockersh] passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
}
<commit_msg>Remove eliminated element from tests, but they fail currently<commit_after>package main

import "testing"
import "fmt"

func Test_DefaultConfig_1(t *testing.T) {
	if defaultConfig.ImageName == "busybox" {
		t.Log("default ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("default ImageName failed: expected busybox got %s", defaultConfig.ImageName))
	}
}

func Test_SimpleConfig_1(t *testing.T) {
	c, err := loadConfigFromString([]byte(``), "fred")
	if err != nil {
		t.Error(err)
	}
	c, err = loadConfigFromString([]byte(`[dockersh]
imagename = testimage`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.ImageName == "testimage" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
}

func Test_UserConfig_1(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
shell = someshell

[user "fred"]
imagename = fredsimage
containerusername = bill`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.Shell == "someshell" {
		t.Log("set Shell in dockersh config passed.")
	} else {
		t.Error(fmt.Sprintf("Expected Shell dockersg got %s", c.Shell))
	}
	if c.ContainerUsername == "bill" {
		t.Log("set ContainerUserName in user config passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ContainerUserName bill got %s", c.ContainerUsername))
	}
	if c.ImageName == "fredsimage" {
		t.Log("set ImageName in user config passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName fredsimage got %s", c.ImageName))
	}
}

func Test_IniConfig_2(t *testing.T) {
	c := Configuration{ContainerUsername: "default_contun", ImageName: "default"}
	n, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
containerusername = shouldbeblacklisted`), "fred")
	c = mergeConfigs(c, n, true)
	if err != nil {
		t.Error(err)
	}
	if c.ImageName == "testimage" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
	if c.ContainerUsername == "default_contun" {
		t.Log("blacklising worked, value not changed")
	} else {
		t.Error("blacklisting failed")
	}
}

func Test_Config_3(t *testing.T) {
	c := Configuration{ContainerUsername: "default_contun", Shell: "default_shell"}
	c, err := loadConfigFromString([]byte(`[dockersh]
shell = global_default
containerusername = global_default
mounthometo = somewhere
blacklistuserconfig = containerusername
blacklistuserconfig = mounthometo`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.Shell != "global_default" {
		t.Error("Set shell to global_default failed")
	}
	if c.ContainerUsername != "global_default" {
		t.Error("Set un to global default failed")
	}
	if c.MountHomeTo != "somewhere" {
		t.Error("Set mounthome to global default failed")
	}
	newc, err := loadConfigFromString([]byte(`[dockersh]
shell = user_value
containerusername = user_value
mounthometo = somewhere_else`), "fred")
	if err != nil {
		t.Error(err)
	}
	c = mergeConfigs(c, newc, true)
	if c.Shell != "user_value" {
		t.Error("Local defaults not applying over global defaults")
	} else {
		t.Log("c.shell not overridden")
	}
	if c.ContainerUsername != "global_default" {
		t.Error("Blacklist of container_username in global config failed")
	}
	if c.MountHomeTo != "somewhere" {
		t.Error("Blacklist mounthome in global config failed")
	}
}

func Test_IniConfig_4(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
blacklistuserconfig = containerusername
containerusername = default_contun
imagename = default`), "fred")
	newc, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
containerusername = shouldbeblacklisted`), "fred")
	if err != nil {
		t.Error(err)
	}
	c = mergeConfigs(c, newc, true)
	if c.ImageName == "testimage" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
	if c.ContainerUsername != "default_contun" {
		t.Error("blacklising disabled, value changed")
	} else {
		t.Log("blacklisting enabled, value has not changed")
	}
}

func Test_IniConfig_5(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
blacklistuserconfig = containerusername
blacklistuserconfig = imagename
containerusername = default_contun
imagename = default`), "fred")
	newc, err := loadConfigFromString([]byte(`[dockersh]
imagename = testimage
containerusername = shouldbeblacklisted`), "fred")
	if err != nil {
		t.Error(err)
	}
	c = mergeConfigs(c, newc, true)
	if c.ImageName == "default" {
		t.Log("set ImageName passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName default got %s", c.ImageName))
	}
	if c.ContainerUsername != "default_contun" {
		t.Error("blacklising disabled, value changed")
	} else {
		t.Log("blacklisting enabled, value has not changed")
	}
}

func Test_IniConfig_6(t *testing.T) {
	c, err := loadConfigFromString([]byte(`[dockersh]
blacklistuserconfig = imagename
imagename = default

[user "fred"]
imagename = testimage
`), "fred")
	if err != nil {
		t.Error(err)
	}
	if c.ImageName == "testimage" {
		t.Log("set ImageName in user section when blacklisted in [dockersh] passed.")
	} else {
		t.Error(fmt.Sprintf("Expected ImageName testimage got %s", c.ImageName))
	}
}
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	"github.com/APTrust/exchange/models"
	"github.com/op/go-logging"
	"io/ioutil"
	stdlog "log"
	"os"
	"path"
	"path/filepath"
)

/*
InitLogger creates and returns a logger suitable for logging
human-readable message. Also returns the path to the log file.
*/
func InitLogger(config *models.Config) (*logging.Logger, string) {
	processName := path.Base(os.Args[0])
	logDir, err := config.EnsureLogDirectory()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot create log directory '%s': %v\n", logDir, err)
		os.Exit(1)
	}
	filename := fmt.Sprintf("%s.log", processName)
	filename = filepath.Join(logDir, filename)
	if config.LogDirectory != "" {
		// If this fails, getRotatingFileWriter will panic in just a second
		_ = os.Mkdir(config.LogDirectory, 0755)
	}
	writer, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot open log file '%s': %v\n", filename, err)
		os.Exit(1)
	}

	log := logging.MustGetLogger(processName)
	format := logging.MustStringFormatter("[%{level}] %{message}")
	logging.SetFormatter(format)
	logging.SetLevel(config.LogLevel, processName)

	logBackend := logging.NewLogBackend(writer, "", 0)
	if config.LogToStderr {
		// Log to BOTH file and stderr
		stderrBackend := logging.NewLogBackend(os.Stderr, "", stdlog.LstdFlags|stdlog.Lshortfile)
		stderrBackend.Color = true
		logging.SetBackend(logBackend, stderrBackend)
	} else {
		// Log to file only
		logging.SetBackend(logBackend)
	}

	return log, filename
}

/*
InitJsonLogger creates and returns a logger suitable for logging JSON
data. Bagman JSON logs consist of a single JSON object per line,
with no extraneous data. Because all of the data in the file is
pure JSON, with one record per line, these files are easy to parse.
*/
func InitJsonLogger(config *models.Config) (*stdlog.Logger, string) {
	processName := path.Base(os.Args[0])
	logDir, err := config.EnsureLogDirectory()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot create log directory '%s': %v\n", logDir, err)
		os.Exit(1)
	}
	filename := fmt.Sprintf("%s.json", processName)
	filename = filepath.Join(logDir, filename)
	writer, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot open log file '%s': %v", filename, err)
		os.Exit(1)
	}
	return stdlog.New(writer, "", 0), filename
}

/*
DiscardLogger returns a logger that writes to dev/null.
Suitable for use in testing.
*/
func DiscardLogger(module string) *logging.Logger {
	log := logging.MustGetLogger(module)
	devnull := logging.NewLogBackend(ioutil.Discard, "", 0)
	logging.SetBackend(devnull)
	logging.SetLevel(logging.INFO, "volume_test")
	return log
}
<commit_msg>Include timestamps in log messages<commit_after>package logger

import (
	"fmt"
	"github.com/APTrust/exchange/models"
	"github.com/op/go-logging"
	"io/ioutil"
	stdlog "log"
	"os"
	"path"
	"path/filepath"
)

/*
InitLogger creates and returns a logger suitable for logging
human-readable message. Also returns the path to the log file.
*/
func InitLogger(config *models.Config) (*logging.Logger, string) {
	processName := path.Base(os.Args[0])
	logDir, err := config.EnsureLogDirectory()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot create log directory '%s': %v\n", logDir, err)
		os.Exit(1)
	}
	filename := fmt.Sprintf("%s.log", processName)
	filename = filepath.Join(logDir, filename)
	if config.LogDirectory != "" {
		// If this fails, getRotatingFileWriter will panic in just a second
		_ = os.Mkdir(config.LogDirectory, 0755)
	}
	writer, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot open log file '%s': %v\n", filename, err)
		os.Exit(1)
	}

	log := logging.MustGetLogger(processName)
	format := logging.MustStringFormatter("%{time} [%{level}] %{message}")
	logging.SetFormatter(format)
	logging.SetLevel(config.LogLevel, processName)

	logBackend := logging.NewLogBackend(writer, "", 0)
	if config.LogToStderr {
		// Log to BOTH file and stderr
		stderrBackend := logging.NewLogBackend(os.Stderr, "", stdlog.Lshortfile)
		stderrBackend.Color = true
		logging.SetBackend(logBackend, stderrBackend)
	} else {
		// Log to file only
		logging.SetBackend(logBackend)
	}

	return log, filename
}

/*
InitJsonLogger creates and returns a logger suitable for logging JSON
data. Bagman JSON logs consist of a single JSON object per line,
with no extraneous data. Because all of the data in the file is
pure JSON, with one record per line, these files are easy to parse.
*/
func InitJsonLogger(config *models.Config) (*stdlog.Logger, string) {
	processName := path.Base(os.Args[0])
	logDir, err := config.EnsureLogDirectory()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot create log directory '%s': %v\n", logDir, err)
		os.Exit(1)
	}
	filename := fmt.Sprintf("%s.json", processName)
	filename = filepath.Join(logDir, filename)
	writer, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot open log file '%s': %v", filename, err)
		os.Exit(1)
	}
	return stdlog.New(writer, "", 0), filename
}

/*
DiscardLogger returns a logger that writes to dev/null.
Suitable for use in testing.
*/
func DiscardLogger(module string) *logging.Logger {
	log := logging.MustGetLogger(module)
	devnull := logging.NewLogBackend(ioutil.Discard, "", 0)
	logging.SetBackend(devnull)
	logging.SetLevel(logging.INFO, "volume_test")
	return log
}
<|endoftext|>


<commit_before>package filecache

import (
	"errors"
	"log"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"

	"github.com/anacrolix/missinggo"
	"github.com/anacrolix/missinggo/pproffd"
)

const (
	dirPerm  = 0755
	filePerm = 0644
)

type Cache struct {
	mu       sync.Mutex
	capacity int64
	filled   int64
	items    *lruItems
	paths    map[string]ItemInfo
	root     string
}

type CacheInfo struct {
	Capacity int64
	Filled   int64
	NumItems int
}

type ItemInfo struct {
	Accessed time.Time
	Size     int64
	Path     string
}

// Calls the function for every item known to the cache. The ItemInfo should
// not be modified.
func (me *Cache) WalkItems(cb func(ItemInfo)) {
	me.mu.Lock()
	defer me.mu.Unlock()
	for e := me.items.Front(); e != nil; e = e.Next() {
		cb(e.Value().(ItemInfo))
	}
}

func (me *Cache) Info() (ret CacheInfo) {
	me.mu.Lock()
	defer me.mu.Unlock()
	ret.Capacity = me.capacity
	ret.Filled = me.filled
	ret.NumItems = len(me.paths)
	return
}

// Setting a negative capacity means unlimited.
func (me *Cache) SetCapacity(capacity int64) {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.capacity = capacity
}

func NewCache(root string) (ret *Cache, err error) {
	if !filepath.IsAbs(root) {
		err = errors.New("root is not an absolute filepath")
		return
	}
	ret = &Cache{
		root:     root,
		capacity: -1, // unlimited
	}
	ret.mu.Lock()
	go func() {
		defer ret.mu.Unlock()
		ret.rescan()
	}()
	return
}

// An empty return path is an error.
func sanitizePath(p string) (ret string) {
	if p == "" {
		return
	}
	ret = path.Clean("/" + p)
	if ret[0] == '/' {
		ret = ret[1:]
	}
	return
}

// Leaf is a descendent of root.
func pruneEmptyDirs(root string, leaf string) (err error) {
	rootInfo, err := os.Stat(root)
	if err != nil {
		return
	}
	for {
		var leafInfo os.FileInfo
		leafInfo, err = os.Stat(leaf)
		if os.IsNotExist(err) {
			goto parent
		}
		if err != nil {
			return
		}
		if !leafInfo.IsDir() {
			return
		}
		if os.SameFile(rootInfo, leafInfo) {
			return
		}
		if os.Remove(leaf) != nil {
			return
		}
	parent:
		leaf = filepath.Dir(leaf)
	}
}

func (me *Cache) Remove(path string) (err error) {
	path = sanitizePath(path)
	me.mu.Lock()
	defer me.mu.Unlock()
	err = me.remove(path)
	return
}

var (
	ErrBadPath = errors.New("bad path")
	ErrIsDir   = errors.New("is directory")
)

func (me *Cache) StatFile(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(sanitizePath(path)))
}

func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) {
	path = sanitizePath(path)
	if path == "" {
		err = ErrIsDir
		return
	}
	f, err := os.OpenFile(me.realpath(path), flag, filePerm)
	if flag&os.O_CREATE != 0 && os.IsNotExist(err) {
		os.MkdirAll(me.root, dirPerm)
		os.MkdirAll(filepath.Dir(me.realpath(path)), dirPerm)
		f, err = os.OpenFile(me.realpath(path), flag, filePerm)
		if err != nil {
			me.pruneEmptyDirs(path)
		}
	}
	if err != nil {
		return
	}
	ret = &File{
		c:    me,
		path: path,
		f:    pproffd.WrapOSFile(f),
	}
	me.mu.Lock()
	go func() {
		defer me.mu.Unlock()
		me.statItem(path, time.Now())
	}()
	return
}

func (me *Cache) rescan() {
	me.filled = 0
	me.items = newLRUItems()
	me.paths = make(map[string]ItemInfo)
	err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error {
		if os.IsNotExist(err) {
			return nil
		}
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		path, err = filepath.Rel(me.root, path)
		if err != nil {
			log.Print(err)
			return nil
		}
		me.statItem(path, time.Time{})
		return nil
	})
	if err != nil {
		panic(err)
	}
}

func (me *Cache) insertItem(i ItemInfo) {
	me.items.Insert(i)
}

func (me *Cache) removeInfo(path string) (ret ItemInfo, ok bool) {
	ret, ok = me.paths[path]
	if !ok {
		return
	}
	if !me.items.Remove(ret) {
		panic(ret)
	}
	me.filled -= ret.Size
	delete(me.paths, path)
	return
}

// Triggers the item for path to be updated. If access is non-zero, set the
// item's access time to that value, otherwise deduce it appropriately.
func (me *Cache) statItem(path string, access time.Time) {
	info, ok := me.removeInfo(path)
	fi, err := os.Stat(me.realpath(path))
	if os.IsNotExist(err) {
		return
	}
	if err != nil {
		panic(err)
	}
	if !ok {
		info.Path = path
	}
	if !access.IsZero() {
		info.Accessed = access
	}
	if info.Accessed.IsZero() {
		info.Accessed = missinggo.FileInfoAccessTime(fi)
	}
	info.Size = fi.Size()
	me.filled += info.Size
	me.insertItem(info)
	me.paths[path] = info
}

func (me *Cache) realpath(path string) string {
	return filepath.Join(me.root, filepath.FromSlash(sanitizePath(path)))
}

func (me *Cache) TrimToCapacity() {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.trimToCapacity()
}

func (me *Cache) pruneEmptyDirs(path string) {
	pruneEmptyDirs(me.root, me.realpath(path))
}

func (me *Cache) remove(path string) (err error) {
	err = os.Remove(me.realpath(path))
	if os.IsNotExist(err) {
		err = nil
	}
	me.pruneEmptyDirs(path)
	me.removeInfo(path)
	return
}

func (me *Cache) trimToCapacity() {
	if me.capacity < 0 {
		return
	}
	for me.filled > me.capacity {
		item := me.items.LRU()
		me.remove(item.Path)
	}
}

func (me *Cache) pathInfo(p string) ItemInfo {
	return me.paths[p]
}

func (me *Cache) Rename(from, to string) (err error) {
	_from := sanitizePath(from)
	_to := sanitizePath(to)
	me.mu.Lock()
	defer me.mu.Unlock()
	err = os.MkdirAll(filepath.Dir(me.realpath(_to)), dirPerm)
	if err != nil {
		return
	}
	err = os.Rename(me.realpath(_from), me.realpath(_to))
	if err != nil {
		return
	}
	me.removeInfo(_from)
	me.pruneEmptyDirs(_from)
	me.statItem(_to, time.Now())
	return
}

func (me *Cache) Stat(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(path))
}

func (me *Cache) AsFileStore() missinggo.FileStore {
	return fileStore{me}
}
<commit_msg>filecache: Ensure the cache root is an absolute path<commit_after>package filecache

import (
	"errors"
	"log"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"

	"github.com/anacrolix/missinggo"
	"github.com/anacrolix/missinggo/pproffd"
)

const (
	dirPerm  = 0755
	filePerm = 0644
)

type Cache struct {
	mu       sync.Mutex
	capacity int64
	filled   int64
	items    *lruItems
	paths    map[string]ItemInfo
	root     string
}

type CacheInfo struct {
	Capacity int64
	Filled   int64
	NumItems int
}

type ItemInfo struct {
	Accessed time.Time
	Size     int64
	Path     string
}

// Calls the function for every item known to the cache. The ItemInfo should
// not be modified.
func (me *Cache) WalkItems(cb func(ItemInfo)) {
	me.mu.Lock()
	defer me.mu.Unlock()
	for e := me.items.Front(); e != nil; e = e.Next() {
		cb(e.Value().(ItemInfo))
	}
}

func (me *Cache) Info() (ret CacheInfo) {
	me.mu.Lock()
	defer me.mu.Unlock()
	ret.Capacity = me.capacity
	ret.Filled = me.filled
	ret.NumItems = len(me.paths)
	return
}

// Setting a negative capacity means unlimited.
func (me *Cache) SetCapacity(capacity int64) {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.capacity = capacity
}

func NewCache(root string) (ret *Cache, err error) {
	root, err = filepath.Abs(root)
	ret = &Cache{
		root:     root,
		capacity: -1, // unlimited
	}
	ret.mu.Lock()
	go func() {
		defer ret.mu.Unlock()
		ret.rescan()
	}()
	return
}

// An empty return path is an error.
func sanitizePath(p string) (ret string) {
	if p == "" {
		return
	}
	ret = path.Clean("/" + p)
	if ret[0] == '/' {
		ret = ret[1:]
	}
	return
}

// Leaf is a descendent of root.
func pruneEmptyDirs(root string, leaf string) (err error) {
	rootInfo, err := os.Stat(root)
	if err != nil {
		return
	}
	for {
		var leafInfo os.FileInfo
		leafInfo, err = os.Stat(leaf)
		if os.IsNotExist(err) {
			goto parent
		}
		if err != nil {
			return
		}
		if !leafInfo.IsDir() {
			return
		}
		if os.SameFile(rootInfo, leafInfo) {
			return
		}
		if os.Remove(leaf) != nil {
			return
		}
	parent:
		leaf = filepath.Dir(leaf)
	}
}

func (me *Cache) Remove(path string) (err error) {
	path = sanitizePath(path)
	me.mu.Lock()
	defer me.mu.Unlock()
	err = me.remove(path)
	return
}

var (
	ErrBadPath = errors.New("bad path")
	ErrIsDir   = errors.New("is directory")
)

func (me *Cache) StatFile(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(sanitizePath(path)))
}

func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) {
	path = sanitizePath(path)
	if path == "" {
		err = ErrIsDir
		return
	}
	f, err := os.OpenFile(me.realpath(path), flag, filePerm)
	if flag&os.O_CREATE != 0 && os.IsNotExist(err) {
		os.MkdirAll(me.root, dirPerm)
		os.MkdirAll(filepath.Dir(me.realpath(path)), dirPerm)
		f, err = os.OpenFile(me.realpath(path), flag, filePerm)
		if err != nil {
			me.pruneEmptyDirs(path)
		}
	}
	if err != nil {
		return
	}
	ret = &File{
		c:    me,
		path: path,
		f:    pproffd.WrapOSFile(f),
	}
	me.mu.Lock()
	go func() {
		defer me.mu.Unlock()
		me.statItem(path, time.Now())
	}()
	return
}

func (me *Cache) rescan() {
	me.filled = 0
	me.items = newLRUItems()
	me.paths = make(map[string]ItemInfo)
	err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error {
		if os.IsNotExist(err) {
			return nil
		}
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		path, err = filepath.Rel(me.root, path)
		if err != nil {
			log.Print(err)
			return nil
		}
		me.statItem(path, time.Time{})
		return nil
	})
	if err != nil {
		panic(err)
	}
}

func (me *Cache) insertItem(i ItemInfo) {
	me.items.Insert(i)
}

func (me *Cache) removeInfo(path string) (ret ItemInfo, ok bool) {
	ret, ok = me.paths[path]
	if !ok {
		return
	}
	if !me.items.Remove(ret) {
		panic(ret)
	}
	me.filled -= ret.Size
	delete(me.paths, path)
	return
}

// Triggers the item for path to be updated. If access is non-zero, set the
// item's access time to that value, otherwise deduce it appropriately.
func (me *Cache) statItem(path string, access time.Time) {
	info, ok := me.removeInfo(path)
	fi, err := os.Stat(me.realpath(path))
	if os.IsNotExist(err) {
		return
	}
	if err != nil {
		panic(err)
	}
	if !ok {
		info.Path = path
	}
	if !access.IsZero() {
		info.Accessed = access
	}
	if info.Accessed.IsZero() {
		info.Accessed = missinggo.FileInfoAccessTime(fi)
	}
	info.Size = fi.Size()
	me.filled += info.Size
	me.insertItem(info)
	me.paths[path] = info
}

func (me *Cache) realpath(path string) string {
	return filepath.Join(me.root, filepath.FromSlash(sanitizePath(path)))
}

func (me *Cache) TrimToCapacity() {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.trimToCapacity()
}

func (me *Cache) pruneEmptyDirs(path string) {
	pruneEmptyDirs(me.root, me.realpath(path))
}

func (me *Cache) remove(path string) (err error) {
	err = os.Remove(me.realpath(path))
	if os.IsNotExist(err) {
		err = nil
	}
	me.pruneEmptyDirs(path)
	me.removeInfo(path)
	return
}

func (me *Cache) trimToCapacity() {
	if me.capacity < 0 {
		return
	}
	for me.filled > me.capacity {
		item := me.items.LRU()
		me.remove(item.Path)
	}
}

func (me *Cache) pathInfo(p string) ItemInfo {
	return me.paths[p]
}

func (me *Cache) Rename(from, to string) (err error) {
	_from := sanitizePath(from)
	_to := sanitizePath(to)
	me.mu.Lock()
	defer me.mu.Unlock()
	err = os.MkdirAll(filepath.Dir(me.realpath(_to)), dirPerm)
	if err != nil {
		return
	}
	err = os.Rename(me.realpath(_from), me.realpath(_to))
	if err != nil {
		return
	}
	me.removeInfo(_from)
	me.pruneEmptyDirs(_from)
	me.statItem(_to, time.Now())
	return
}

func (me *Cache) Stat(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(path))
}

func (me *Cache) AsFileStore() missinggo.FileStore {
	return fileStore{me}
}
<|endoftext|>