<commit_before>package hostdb

import (
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

func calculateWeightFromUInt64Price(price uint64) (weight types.Currency) {
	hdb := bareHostDB()
	hdb.blockHeight = 0
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(price * 1000).Mul(types.SiacoinPrecision)
	return hdb.calculateHostWeight(entry)
}

func TestHostWeightDistinctPrices(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(3)
	weight2 := calculateWeightFromUInt64Price(6)
	expectedWeight := weight1.Div64(16)
	if weight2.Cmp(expectedWeight) != 0 {
		t.Error("Weight of expensive host is not the correct value.")
	}
}

func TestHostWeightIdenticalPrices(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(42)
	weight2 := calculateWeightFromUInt64Price(42)
	if weight1.Cmp(weight2) != 0 {
		t.Error("Weight of identically priced hosts should be equal.")
	}
}

func TestHostWeightWithOnePricedZero(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(5)
	weight2 := calculateWeightFromUInt64Price(0)
	if weight1.Cmp(weight2) >= 0 {
		t.Error("Zero-priced host should have higher weight than nonzero-priced host.")
	}
}

func TestHostWeightWithBothPricesZero(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(0)
	weight2 := calculateWeightFromUInt64Price(0)
	if weight1.Cmp(weight2) != 0 {
		t.Error("Weight of two zero-priced hosts should be equal.")
	}
}

func TestHostWeightCollateralDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)

	entry2 := entry
	entry2.Collateral = types.NewCurrency64(500).Mul(types.SiacoinPrecision)
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Larger collateral should have more weight")
	}
}

func TestHostWeightStorageRemainingDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)

	entry2 := entry
	entry2.RemainingStorage = 50e3
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Larger storage remaining should have more weight")
	}
}

func TestHostWeightVersionDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"

	entry2 := entry
	entry2.Version = "v1.0.3"
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Higher version should have more weight")
	}
}

func TestHostWeightLifetimeDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"

	entry2 := entry
	entry2.FirstSeen = 8100
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences2(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences3(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences4(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}
<commit_msg>adjust weight testing to account for forward looking downtime<commit_after>package hostdb

import (
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

func calculateWeightFromUInt64Price(price uint64) (weight types.Currency) {
	hdb := bareHostDB()
	hdb.blockHeight = 0
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(price * 1000).Mul(types.SiacoinPrecision)
	return hdb.calculateHostWeight(entry)
}

func TestHostWeightDistinctPrices(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(3)
	weight2 := calculateWeightFromUInt64Price(6)
	expectedWeight := weight1.Div64(16)
	if weight2.Cmp(expectedWeight) != 0 {
		t.Error("Weight of expensive host is not the correct value.")
	}
}

func TestHostWeightIdenticalPrices(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(42)
	weight2 := calculateWeightFromUInt64Price(42)
	if weight1.Cmp(weight2) != 0 {
		t.Error("Weight of identically priced hosts should be equal.")
	}
}

func TestHostWeightWithOnePricedZero(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(5)
	weight2 := calculateWeightFromUInt64Price(0)
	if weight1.Cmp(weight2) >= 0 {
		t.Error("Zero-priced host should have higher weight than nonzero-priced host.")
	}
}

func TestHostWeightWithBothPricesZero(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	weight1 := calculateWeightFromUInt64Price(0)
	weight2 := calculateWeightFromUInt64Price(0)
	if weight1.Cmp(weight2) != 0 {
		t.Error("Weight of two zero-priced hosts should be equal.")
	}
}

func TestHostWeightCollateralDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)

	entry2 := entry
	entry2.Collateral = types.NewCurrency64(500).Mul(types.SiacoinPrecision)
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Larger collateral should have more weight")
	}
}

func TestHostWeightStorageRemainingDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)

	entry2 := entry
	entry2.RemainingStorage = 50e3
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Larger storage remaining should have more weight")
	}
}

func TestHostWeightVersionDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"

	entry2 := entry
	entry2.Version = "v1.0.3"
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Higher version should have more weight")
	}
}

func TestHostWeightLifetimeDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"

	entry2 := entry
	entry2.FirstSeen = 8100
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences2(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -1), Success: false},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences3(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: true},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}

func TestHostWeightUptimeDifferences4(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	hdb := bareHostDB()
	hdb.blockHeight = 10000
	var entry modules.HostDBEntry
	entry.RemainingStorage = 250e3
	entry.StoragePrice = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Collateral = types.NewCurrency64(1000).Mul(types.SiacoinPrecision)
	entry.Version = "v1.0.4"
	entry.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
	}

	entry2 := entry
	entry2.ScanHistory = modules.HostDBScans{
		{Timestamp: time.Now().Add(time.Hour * -100), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -80), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -60), Success: true},
		{Timestamp: time.Now().Add(time.Hour * -40), Success: false},
		{Timestamp: time.Now().Add(time.Hour * -20), Success: false},
	}
	w1 := hdb.calculateHostWeight(entry)
	w2 := hdb.calculateHostWeight(entry2)

	if w1.Cmp(w2) < 0 {
		t.Error("Been around longer should have more weight")
	}
}
<|endoftext|>


<commit_before>package raster

import (
	"fmt"
	"image"
	"image/color"
	"image/png"
	"math"
	"os"

	"github.com/krasin/stl"
	"github.com/krasin/voxel/set"
	"github.com/krasin/voxel/timing"
	"github.com/krasin/voxel/triangle"
	"github.com/krasin/voxel/volume"
)

var (
	Black  = color.RGBA{0, 0, 0, 255}
	Yellow = color.RGBA{255, 255, 0, 255}
	Green  = color.RGBA{0, 255, 0, 255}
	Pink   = color.RGBA{255, 20, 147, 255}
	colors = []color.RGBA{
		{0, 0, 0, 255},
		{255, 255, 0, 255},
		{0, 255, 0, 255},
		{255, 0, 0, 255},
		{0, 0, 255, 255},
		{128, 255, 0, 255},
		{0, 255, 128, 255},
		{255, 128, 0, 255},
		{255, 0, 128, 255},
		{0, 128, 255, 255},
		{128, 0, 255, 255},
		{255, 255, 128, 255},
		{128, 255, 128, 255},
		{128, 128, 255, 255},
	}
)

type Uint16Volume interface {
	volume.BoolVoxelVolume
	Set(x, y, z int, v uint16)
	GetV(x, y, z int) uint16
	SetAllFilled(threshold, val uint16)
	MapBoundary(f func(x, y, z int))
	Volume() int64
}

func PinkX(n int) color.RGBA {
	if n < 200 {
		return color.RGBA{Pink.R, Pink.G + uint8(n), Pink.B, 255}
	}
	return Pink
}

type Grid struct {
	P0 [3]float64
	P1 [3]float64
	N  [3]int64
}

type Mesh struct {
	Grid
	Triangle []triangle.Triangle
}

func STLToMesh(n int, triangles []stl.Triangle) (m Mesh) {
	min := []float32{math.MaxFloat32, math.MaxFloat32, math.MaxFloat32}
	max := []float32{-math.MaxFloat32, -math.MaxFloat32, -math.MaxFloat32}
	for _, t := range triangles {
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				cur := t.V[i][j]
				if min[j] > cur {
					min[j] = cur
				}
				if max[j] < cur {
					max[j] = cur
				}
			}
		}
	}
	m.P0 = [3]float64{float64(min[0]) - 1, float64(min[1]) - 1, float64(min[2]) - 1}
	m.P1 = [3]float64{float64(max[0]) + 1, float64(max[1]) + 1, float64(max[2]) + 1}
	m.N = [3]int64{int64(n), int64(n), int64(n)}
	m.Triangle = make([]triangle.Triangle, len(triangles))
	for i, t := range triangles {
		cur := &m.Triangle[i]
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				val := float64(t.V[i][j])
				cur[i][j] = int64((val - m.P0[j]) * float64(m.N[j]) / (m.P1[j] - m.P0[j]))
			}
		}
	}
	return
}

func Rasterize(m Mesh, n int) Uint16Volume {
	scale := m.N[0] / int64(n)
	vol := volume.NewSparseVolume(n)

	timing.StartTiming("Rasterize triangles")
	for index, t := range m.Triangle {
		triangle.AllTriangleDots(t[0], t[1], t[2], scale, vol, uint16(1+(index%10)))
	}
	fmt.Fprintf(os.Stderr, "Triangle rasterization complete\n")
	timing.StopTiming("Rasterize triangles")

	timing.StartTiming("Rasterize cubes")
	ds := set.NewDisjoinSet()
	// Reserve color for outer space
	ds.Make()

	shift := 11

	// Let's color cubes.
	for k, cube := range vol.Cubes {
		// Skip cubes with leaf voxels
		if cube != nil {
			continue
		}
		p := volume.K2cube(k)

		// If this is a cube at the edge of the space, it's a part of outer space.
		if p[0] == 0 || p[1] == 0 || p[2] == 0 ||
			int(p[0]) == (1<<uint(vol.LK))-1 || int(p[1]) == (1<<uint(vol.LK))-1 || int(p[2]) == (1<<uint(vol.LK))-1 {
			vol.Colors[k] = uint16(shift + ds.Find(0))
			continue
		}

		// Look if any neighbour has already color assigned
		for i := 0; i < 3; i++ {
			for j := -1; j <= 1; j += 2 {
				p2 := p
				p2[i] = uint16(int(p2[i]) + j)
				k2 := volume.Cube2k(p2)
				if k2 >= len(vol.Colors) {
					panic(fmt.Sprintf("k2: %d, len(vol.Colors): %d, len(vol.Cubes): %d, p: %v, p2: %v, k: %d", k2, len(vol.Colors), len(vol.Cubes), p, p2, k))
				}
				if vol.Colors[k2] == 0 {
					continue
				}
				if vol.Colors[k] == 0 {
					vol.Colors[k] = vol.Colors[k2]
				} else {
					ds.Join(int(vol.Colors[k])-shift, int(vol.Colors[k2])-shift)
				}
			}
		}

		// If there's no colored neighbour, introduce a new color.
		if vol.Colors[k] == 0 {
			vol.Colors[k] = uint16(shift + ds.Make())
		}
	}
	timing.StopTiming("Rasterize cubes")
	timing.StartTiming("Rasterize leaf voxels")

	// Now, we need to go through cubes which have leaf voxels
	for k, cube := range vol.Cubes {
		if cube == nil {
			continue
		}
		for h, val := range cube {
			if val != 0 {
				continue
			}
			p := volume.Kh2point(k, h)
			color := val
			// Look for neighbours of this leaf voxel
			for i := 0; i < 3; i++ {
				for j := -1; j <= 1; j += 2 {
					p2 := p
					p2[i] = uint16(int(p2[i]) + j)
					color2 := vol.GetV(int(p2[0]), int(p2[1]), int(p2[2]))
					if int(color2) < shift {
						continue
					}
					if color == 0 {
						vol.Set(int(p[0]), int(p[1]), int(p[2]), color2)
						color = color2
					} else {
						ds.Join(int(color)-shift, int(color2)-shift)
					}
				}
			}
			if color == 0 {
				vol.Set(int(p[0]), int(p[1]), int(p[2]), uint16(shift+ds.Make()))
			}
		}
	}
	timing.StopTiming("Rasterize leaf voxels")

	timing.StartTiming("Rasterize.CanonicalizeColors")
	// Canonicalize colors
	canonicalZero := uint16(shift + ds.Find(0))
	for k, cube := range vol.Cubes {
		if cube == nil {
			vol.Colors[k] = uint16(shift + ds.Find(int(vol.Colors[k])-shift))
			if vol.Colors[k] == canonicalZero {
				vol.Colors[k] = 0
			}
			continue
		}
		for h := 0; h < len(cube); h++ {
			if int(cube[h]) < shift {
				continue
			}
			cube[h] = uint16(shift + ds.Find(int(cube[h])-shift))
			if cube[h] == canonicalZero {
				cube[h] = 0
			}
		}
	}
	timing.StopTiming("Rasterize.CanonicalizeColors")

	timing.StartTiming("Rasterize.DrawSlices")
	bmp := image.NewRGBA(image.Rect(0, 0, n, n))
	for z := 1; z < n; z++ {
		if z%10 == 0 {
			for x := 0; x < n; x++ {
				for y := 0; y < n; y++ {
					v := vol.GetV(x, y, z)
					if int(v) < len(colors) {
						bmp.Set(x, y, colors[v])
					} else {
						bmp.Set(x, y, PinkX(int(v)))
					}
				}
			}

			f, _ := os.OpenFile(fmt.Sprintf("zban-%03d.png", z), os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0666)
			png.Encode(f, bmp)
			f.Close()
		}
	}
	timing.StopTiming("Rasterize.DrawSlices")
	fmt.Fprintf(os.Stderr, "Rasterize complete\n")
	return vol
}
<commit_msg>Implement converter from index to point in raster.Grid<commit_after>package raster

import (
	"fmt"
	"image"
	"image/color"
	"image/png"
	"math"
	"os"

	"github.com/krasin/stl"
	"github.com/krasin/voxel/set"
	"github.com/krasin/voxel/timing"
	"github.com/krasin/voxel/triangle"
	"github.com/krasin/voxel/volume"
)

var (
	Black  = color.RGBA{0, 0, 0, 255}
	Yellow = color.RGBA{255, 255, 0, 255}
	Green  = color.RGBA{0, 255, 0, 255}
	Pink   = color.RGBA{255, 20, 147, 255}
	colors = []color.RGBA{
		{0, 0, 0, 255},
		{255, 255, 0, 255},
		{0, 255, 0, 255},
		{255, 0, 0, 255},
		{0, 0, 255, 255},
		{128, 255, 0, 255},
		{0, 255, 128, 255},
		{255, 128, 0, 255},
		{255, 0, 128, 255},
		{0, 128, 255, 255},
		{128, 0, 255, 255},
		{255, 255, 128, 255},
		{128, 255, 128, 255},
		{128, 128, 255, 255},
	}
)

type Uint16Volume interface {
	volume.BoolVoxelVolume
	Set(x, y, z int, v uint16)
	GetV(x, y, z int) uint16
	SetAllFilled(threshold, val uint16)
	MapBoundary(f func(x, y, z int))
	Volume() int64
}

func PinkX(n int) color.RGBA {
	if n < 200 {
		return color.RGBA{Pink.R, Pink.G + uint8(n), Pink.B, 255}
	}
	return Pink
}

type Grid struct {
	P0 [3]float64
	P1 [3]float64
	N  [3]int64
}

func (g Grid) Coord(pos [3]int) [3]float64 {
	d0 := (g.P1[0] - g.P0[0]) / float64(g.N[0])
	d1 := (g.P1[1] - g.P0[1]) / float64(g.N[1])
	d2 := (g.P1[2] - g.P0[2]) / float64(g.N[2])

	return [3]float64{
		g.P0[0] + d0*float64(pos[0]),
		g.P0[1] + d1*float64(pos[1]),
		g.P0[2] + d2*float64(pos[2]),
	}
}

type Mesh struct {
	Grid
	Triangle []triangle.Triangle
}

func STLToMesh(n int, triangles []stl.Triangle) (m Mesh) {
	min := []float32{math.MaxFloat32, math.MaxFloat32, math.MaxFloat32}
	max := []float32{-math.MaxFloat32, -math.MaxFloat32, -math.MaxFloat32}
	for _, t := range triangles {
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				cur := t.V[i][j]
				if min[j] > cur {
					min[j] = cur
				}
				if max[j] < cur {
					max[j] = cur
				}
			}
		}
	}
	m.P0 = [3]float64{float64(min[0]) - 1, float64(min[1]) - 1, float64(min[2]) - 1}
	m.P1 = [3]float64{float64(max[0]) + 1, float64(max[1]) + 1, float64(max[2]) + 1}
	m.N = [3]int64{int64(n), int64(n), int64(n)}
	m.Triangle = make([]triangle.Triangle, len(triangles))
	for i, t := range triangles {
		cur := &m.Triangle[i]
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				val := float64(t.V[i][j])
				cur[i][j] = int64((val - m.P0[j]) * float64(m.N[j]) / (m.P1[j] - m.P0[j]))
			}
		}
	}
	return
}

func Rasterize(m Mesh, n int) Uint16Volume {
	scale := m.N[0] / int64(n)
	vol := volume.NewSparseVolume(n)

	timing.StartTiming("Rasterize triangles")
	for index, t := range m.Triangle {
		triangle.AllTriangleDots(t[0], t[1], t[2], scale, vol, uint16(1+(index%10)))
	}
	fmt.Fprintf(os.Stderr, "Triangle rasterization complete\n")
	timing.StopTiming("Rasterize triangles")

	timing.StartTiming("Rasterize cubes")
	ds := set.NewDisjoinSet()
	// Reserve color for outer space
	ds.Make()

	shift := 11

	// Let's color cubes.
	for k, cube := range vol.Cubes {
		// Skip cubes with leaf voxels
		if cube != nil {
			continue
		}
		p := volume.K2cube(k)

		// If this is a cube at the edge of the space, it's a part of outer space.
		if p[0] == 0 || p[1] == 0 || p[2] == 0 ||
			int(p[0]) == (1<<uint(vol.LK))-1 || int(p[1]) == (1<<uint(vol.LK))-1 || int(p[2]) == (1<<uint(vol.LK))-1 {
			vol.Colors[k] = uint16(shift + ds.Find(0))
			continue
		}

		// Look if any neighbour has already color assigned
		for i := 0; i < 3; i++ {
			for j := -1; j <= 1; j += 2 {
				p2 := p
				p2[i] = uint16(int(p2[i]) + j)
				k2 := volume.Cube2k(p2)
				if k2 >= len(vol.Colors) {
					panic(fmt.Sprintf("k2: %d, len(vol.Colors): %d, len(vol.Cubes): %d, p: %v, p2: %v, k: %d", k2, len(vol.Colors), len(vol.Cubes), p, p2, k))
				}
				if vol.Colors[k2] == 0 {
					continue
				}
				if vol.Colors[k] == 0 {
					vol.Colors[k] = vol.Colors[k2]
				} else {
					ds.Join(int(vol.Colors[k])-shift, int(vol.Colors[k2])-shift)
				}
			}
		}

		// If there's no colored neighbour, introduce a new color.
		if vol.Colors[k] == 0 {
			vol.Colors[k] = uint16(shift + ds.Make())
		}
	}
	timing.StopTiming("Rasterize cubes")
	timing.StartTiming("Rasterize leaf voxels")

	// Now, we need to go through cubes which have leaf voxels
	for k, cube := range vol.Cubes {
		if cube == nil {
			continue
		}
		for h, val := range cube {
			if val != 0 {
				continue
			}
			p := volume.Kh2point(k, h)
			color := val
			// Look for neighbours of this leaf voxel
			for i := 0; i < 3; i++ {
				for j := -1; j <= 1; j += 2 {
					p2 := p
					p2[i] = uint16(int(p2[i]) + j)
					color2 := vol.GetV(int(p2[0]), int(p2[1]), int(p2[2]))
					if int(color2) < shift {
						continue
					}
					if color == 0 {
						vol.Set(int(p[0]), int(p[1]), int(p[2]), color2)
						color = color2
					} else {
						ds.Join(int(color)-shift, int(color2)-shift)
					}
				}
			}
			if color == 0 {
				vol.Set(int(p[0]), int(p[1]), int(p[2]), uint16(shift+ds.Make()))
			}
		}
	}
	timing.StopTiming("Rasterize leaf voxels")

	timing.StartTiming("Rasterize.CanonicalizeColors")
	// Canonicalize colors
	canonicalZero := uint16(shift + ds.Find(0))
	for k, cube := range vol.Cubes {
		if cube == nil {
			vol.Colors[k] = uint16(shift + ds.Find(int(vol.Colors[k])-shift))
			if vol.Colors[k] == canonicalZero {
				vol.Colors[k] = 0
			}
			continue
		}
		for h := 0; h < len(cube); h++ {
			if int(cube[h]) < shift {
				continue
			}
			cube[h] = uint16(shift + ds.Find(int(cube[h])-shift))
			if cube[h] == canonicalZero {
				cube[h] = 0
			}
		}
	}
	timing.StopTiming("Rasterize.CanonicalizeColors")

	timing.StartTiming("Rasterize.DrawSlices")
	bmp := image.NewRGBA(image.Rect(0, 0, n, n))
	for z := 1; z < n; z++ {
		if z%10 == 0 {
			for x := 0; x < n; x++ {
				for y := 0; y < n; y++ {
					v := vol.GetV(x, y, z)
					if int(v) < len(colors) {
						bmp.Set(x, y, colors[v])
					} else {
						bmp.Set(x, y, PinkX(int(v)))
					}
				}
			}

			f, _ := os.OpenFile(fmt.Sprintf("zban-%03d.png", z), os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0666)
			png.Encode(f, bmp)
			f.Close()
		}
	}
	timing.StopTiming("Rasterize.DrawSlices")
	fmt.Fprintf(os.Stderr, "Rasterize complete\n")
	return vol
}
<|endoftext|>


<commit_before>package sshbytestream

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
)

type SSHTransport struct {
	Host         string
	User         string
	Port         uint16
	IdentityFile string
	SSHCommand   string
	Options      []string
}

var SSHCommand string = "ssh"

func Incoming() (wc io.ReadWriteCloser, err error) {
	// derivce ReadWriteCloser from stdin & stdout
	return IncomingReadWriteCloser{}, nil
}

type IncomingReadWriteCloser struct{}

func (f IncomingReadWriteCloser) Read(p []byte) (n int, err error) {
	return os.Stdin.Read(p)
}

func (f IncomingReadWriteCloser) Write(p []byte) (n int, err error) {
	return os.Stdout.Write(p)
}

func (f IncomingReadWriteCloser) Close() (err error) {
	os.Exit(0)
	return nil
}

func Outgoing(remote SSHTransport) (conn io.ReadWriteCloser, err error) {

	ctx, cancel := context.WithCancel(context.Background())

	sshArgs := make([]string, 0, 2*len(remote.Options)+4)
	sshArgs = append(sshArgs,
		"-p", fmt.Sprintf("%d", remote.Port),
		"-q",
		"-i", remote.IdentityFile,
		"-o", "BatchMode=yes",
	)
	for _, option := range remote.Options {
		sshArgs = append(sshArgs, "-o", option)
	}
	sshArgs = append(sshArgs, fmt.Sprintf("%s@%s", remote.User, remote.Host))

	var sshCommand = SSHCommand
	if len(remote.SSHCommand) > 0 {
		sshCommand = SSHCommand
	}
	cmd := exec.CommandContext(ctx, sshCommand, sshArgs...)

	// Clear environment of cmd
	cmd.Env = []string{}

	var in io.WriteCloser
	var out io.ReadCloser
	var stderr io.Reader

	if in, err = cmd.StdinPipe(); err != nil {
		return
	}

	if out, err = cmd.StdoutPipe(); err != nil {
		return
	}
	if stderr, err = cmd.StderrPipe(); err != nil {
		return
	}

	f := ForkedSSHReadWriteCloser{
		RemoteStdin:   in,
		RemoteStdout:  out,
		Cancel:        cancel,
		Command:       cmd,
		exitWaitGroup: &sync.WaitGroup{},
	}

	f.exitWaitGroup.Add(1)
	if err = cmd.Start(); err != nil {
		return
	}

	go func() {
		defer f.exitWaitGroup.Done()
		var b bytes.Buffer
		if _, err := io.Copy(&b, stderr); err != nil {
			panic(err)
		}
		if err := cmd.Wait(); err != nil {
			fmt.Fprintf(os.Stderr, "ssh command exited with error: %v. Stderr:\n%s\n", cmd.ProcessState, b)
			//panic(err) TODO
		}
	}()

	return f, nil
}

type ForkedSSHReadWriteCloser struct {
	RemoteStdin   io.Writer
	RemoteStdout  io.Reader
	Command       *exec.Cmd
	Cancel        context.CancelFunc
	exitWaitGroup *sync.WaitGroup
}

func (f ForkedSSHReadWriteCloser) Read(p []byte) (n int, err error) {
	return f.RemoteStdout.Read(p)
}

func (f ForkedSSHReadWriteCloser) Write(p []byte) (n int, err error) {
	return f.RemoteStdin.Write(p)
}

func (f ForkedSSHReadWriteCloser) Close() (err error) {
	f.Cancel()
	f.exitWaitGroup.Wait()
	return nil
}
<commit_msg>sshbytestream: fix semantics when ssh connection dies<commit_after>package sshbytestream

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
)

type Error struct {
	Stderr  []byte
	WaitErr error
}

func (e Error) Error() string {
	return fmt.Sprintf("ssh command failed with error: %v. stderr:\n%s\n", e.WaitErr, e.Stderr)
}

type SSHTransport struct {
	Host         string
	User         string
	Port         uint16
	IdentityFile string
	SSHCommand   string
	Options      []string
}

var SSHCommand string = "ssh"

func Incoming() (wc io.ReadWriteCloser, err error) {
	// derivce ReadWriteCloser from stdin & stdout
	return IncomingReadWriteCloser{}, nil
}

type IncomingReadWriteCloser struct{}

func (f IncomingReadWriteCloser) Read(p []byte) (n int, err error) {
	return os.Stdin.Read(p)
}

func (f IncomingReadWriteCloser) Write(p []byte) (n int, err error) {
	return os.Stdout.Write(p)
}

func (f IncomingReadWriteCloser) Close() (err error) {
	if err = os.Stdin.Close(); err != nil {
		return
	}
	if err = os.Stdout.Close(); err != nil {
		return
	}
	return
}

func Outgoing(remote SSHTransport) (conn io.ReadWriteCloser, err error) {

	ctx, cancel := context.WithCancel(context.Background())

	sshArgs := make([]string, 0, 2*len(remote.Options)+4)
	sshArgs = append(sshArgs,
		"-p", fmt.Sprintf("%d", remote.Port),
		"-q",
		"-i", remote.IdentityFile,
		"-o", "BatchMode=yes",
	)
	for _, option := range remote.Options {
		sshArgs = append(sshArgs, "-o", option)
	}
	sshArgs = append(sshArgs, fmt.Sprintf("%s@%s", remote.User, remote.Host))

	var sshCommand = SSHCommand
	if len(remote.SSHCommand) > 0 {
		sshCommand = SSHCommand
	}
	cmd := exec.CommandContext(ctx, sshCommand, sshArgs...)

	// Clear environment of cmd
	cmd.Env = []string{}

	var in io.WriteCloser
	var out io.ReadCloser

	if in, err = cmd.StdinPipe(); err != nil {
		return
	}
	if out, err = cmd.StdoutPipe(); err != nil {
		return
	}

	stderrBuf := bytes.NewBuffer(make([]byte, 0, 1024))
	cmd.Stderr = stderrBuf

	f := &ForkedSSHReadWriteCloser{
		RemoteStdin:   in,
		RemoteStdout:  out,
		Cancel:        cancel,
		Command:       cmd,
		exitWaitGroup: &sync.WaitGroup{},
	}

	f.exitWaitGroup.Add(1)
	if err = cmd.Start(); err != nil {
		return
	}

	go func() {
		defer f.exitWaitGroup.Done()

		// stderr output is only relevant for errors if the exit code is non-zero
		if err := cmd.Wait(); err != nil {
			f.SSHCommandError = Error{
				Stderr:  stderrBuf.Bytes(),
				WaitErr: err,
			}
			// fmt.Fprintf(os.Stderr, "ssh conn wait err: %#v\n", f.SSHCommandError.(Error))
		} else {
			f.SSHCommandError = nil
		}
	}()

	return f, nil
}

type ForkedSSHReadWriteCloser struct {
	RemoteStdin     io.Writer
	RemoteStdout    io.Reader
	Command         *exec.Cmd
	Cancel          context.CancelFunc
	exitWaitGroup   *sync.WaitGroup
	SSHCommandError error
}

func (f *ForkedSSHReadWriteCloser) Read(p []byte) (n int, err error) {
	if f.SSHCommandError != nil {
		return 0, f.SSHCommandError
	}
	if n, err = f.RemoteStdout.Read(p); err == io.EOF {
		// the ssh command has exited, but we need to wait for post-portem to finish
		f.exitWaitGroup.Wait()
		if f.SSHCommandError != nil {
			err = f.SSHCommandError
		}
	}
	return
}

func (f *ForkedSSHReadWriteCloser) Write(p []byte) (n int, err error) {
	if f.SSHCommandError != nil {
		return 0, f.SSHCommandError
	}
	if n, err = f.RemoteStdin.Write(p); err == io.EOF {
		// the ssh command has exited, but we need to wait for post-portem to finish
		f.exitWaitGroup.Wait()
		if f.SSHCommandError != nil {
			err = f.SSHCommandError
		}
	}
	return
}

func (f *ForkedSSHReadWriteCloser) Close() (err error) {
	// TODO should check SSHCommandError?
	f.Cancel()
	f.exitWaitGroup.Wait()
	return f.SSHCommandError
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2009 Samuel Tesla <samuel.tesla@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package main

import (
	"os";

	. "specify";
	t "../src/testspecify";
)

func init() {
	Describe("After", func() {
		It("should run the block after each test", func(the Example) {
			ch := make(chan bool, 1);
			testRun("", func(r t.Runner) {
				r.After(func(t.Context) { ch <- true });
				r.It("should pass", func(the t.Example) { /* pass */ });
			});
			_, ok := <-ch;
			the.Value(ok).Should(Be(true));
		});

		It("should fail a test if the after has an error", func(the Example) {
			reporter := testRun("", func(r t.Runner) {
				r.After(func(c t.Context) { c.Error(os.NewError("boom")) });
				r.It("should pass", func(the t.Example) { /* pass */ });
			});
			the.Value(reporter).Should(HaveFailureAt("after_spec.go:45"));
		});

		It("should see the fields set in the example", func(the Example) {
			ch := make(chan interface{}, 1);
			testRun("", func(r t.Runner) {
				r.It("should pass", func(e t.Example) { e.SetField("foo", "bar") });
				r.After(func(c t.Context) { ch <- c.GetField("foo") });
			});
			the.Value(<-ch).Should(Be("bar"));
		});
	})
}
<commit_msg>Order after-blocks after the examples<commit_after>/*
Copyright (c) 2009 Samuel Tesla <samuel.tesla@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package main

import (
	"os";

	. "specify";
	t "../src/testspecify";
)

func init() {
	Describe("After", func() {
		It("should run the block after each test", func(the Example) {
			ch := make(chan bool, 1);
			testRun("", func(r t.Runner) {
				r.It("should pass", func(the t.Example) { /* pass */ });
				r.After(func(t.Context) { ch <- true });
			});
			_, ok := <-ch;
			the.Value(ok).Should(Be(true));
		});

		It("should fail a test if the after has an error", func(the Example) {
			reporter := testRun("", func(r t.Runner) {
				r.It("should pass", func(the t.Example) { /* pass */ });
				r.After(func(c t.Context) { c.Error(os.NewError("boom")) });
			});
			the.Value(reporter).Should(HaveFailureAt("after_spec.go:45"));
		});

		It("should see the fields set in the example", func(the Example) {
			ch := make(chan interface{}, 1);
			testRun("", func(r t.Runner) {
				r.It("should pass", func(e t.Example) { e.SetField("foo", "bar") });
				r.After(func(c t.Context) { ch <- c.GetField("foo") });
			});
			the.Value(<-ch).Should(Be("bar"));
		});
	})
}
<|endoftext|>


<commit_before>/*
Package prometheus provides middleware to add Prometheus metrics.

Example:
```
package main
import (
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo-contrib/prometheus"
)
func main() {
    e := echo.New()
    // Enable metrics middleware
    p := prometheus.NewPrometheus("echo", nil)
    p.Use(e)

    e.Logger.Fatal(e.Start(":1323"))
}
```
*/
package prometheus

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"github.com/labstack/gommon/log"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var defaultMetricPath = "/metrics"
var defaultSubsystem = "echo"

// Standard default metrics
//	counter, counter_vec, gauge, gauge_vec,
//	histogram, histogram_vec, summary, summary_vec
var reqCnt = &Metric{
	ID:          "reqCnt",
	Name:        "requests_total",
	Description: "How many HTTP requests processed, partitioned by status code and HTTP method.",
	Type:        "counter_vec",
	Args:        []string{"code", "method", "host", "url"}}

var reqDur = &Metric{
	ID:          "reqDur",
	Name:        "request_duration_seconds",
	Description: "The HTTP request latencies in seconds.",
	Type:        "summary"}

var resSz = &Metric{
	ID:          "resSz",
	Name:        "response_size_bytes",
	Description: "The HTTP response sizes in bytes.",
	Type:        "summary"}

var reqSz = &Metric{
	ID:          "reqSz",
	Name:        "request_size_bytes",
	Description: "The HTTP request sizes in bytes.",
	Type:        "summary"}

var standardMetrics = []*Metric{
	reqCnt,
	reqDur,
	resSz,
	reqSz,
}

/*
RequestCounterURLLabelMappingFunc is a function which can be supplied to the middleware to control
the cardinality of the request counter's "url" label, which might be required in some contexts.
For instance, if for a "/customer/:name" route you don't want to generate a time series for every
possible customer name, you could use this function:

func(c echo.Context) string {
	url := c.Request.URL.Path
	for _, p := range c.Params {
		if p.Key == "name" {
			url = strings.Replace(url, p.Value, ":name", 1)
			break
		}
	}
	return url
}

which would map "/customer/alice" and "/customer/bob" to their template "/customer/:name".
*/
type RequestCounterURLLabelMappingFunc func(c echo.Context) string

// Metric is a definition for the name, description, type, ID, and
// prometheus.Collector type (i.e. CounterVec, Summary, etc) of each metric
type Metric struct {
	MetricCollector prometheus.Collector
	ID              string
	Name            string
	Description     string
	Type            string
	Args            []string
}

// Prometheus contains the metrics gathered by the instance and its path
type Prometheus struct {
	reqCnt               *prometheus.CounterVec
	reqDur, reqSz, resSz prometheus.Summary
	router               *echo.Echo
	listenAddress        string
	Ppg                  PushGateway

	MetricsList []*Metric
	MetricsPath string
	Subsystem   string
	Skipper     middleware.Skipper

	RequestCounterURLLabelMappingFunc RequestCounterURLLabelMappingFunc

	// Context string to use as a prometheus URL label
	URLLabelFromContext string
}

// PushGateway contains the configuration for pushing to a Prometheus pushgateway (optional)
type PushGateway struct {
	// Push interval in seconds
	PushIntervalSeconds time.Duration

	// Push Gateway URL in format http://domain:port
	// where JOBNAME can be any string of your choice
	PushGatewayURL string

	// Local metrics URL where metrics are fetched from, this could be ommited in the future
	// if implemented using prometheus common/expfmt instead
	MetricsURL string

	// pushgateway job name, defaults to "echo"
	Job string
}

// NewPrometheus generates a new set of metrics with a certain subsystem name
func NewPrometheus(subsystem string, skipper middleware.Skipper, customMetricsList ...[]*Metric) *Prometheus {
	var metricsList []*Metric
	if skipper == nil {
		skipper = middleware.DefaultSkipper
	}

	if len(customMetricsList) > 1 {
		panic("Too many args. NewPrometheus( string, <optional []*Metric> ).")
	} else if len(customMetricsList) == 1 {
		metricsList = customMetricsList[0]
	}

	for _, metric := range standardMetrics {
		metricsList = append(metricsList, metric)
	}

	p := &Prometheus{
		MetricsList: metricsList,
		MetricsPath: defaultMetricPath,
		Subsystem:   defaultSubsystem,
		Skipper:     skipper,
		RequestCounterURLLabelMappingFunc: func(c echo.Context) string {
			return c.Path() // i.e. by default do nothing, i.e. return URL as is
		},
	}

	p.registerMetrics(subsystem)

	return p
}

// SetPushGateway sends metrics to a remote pushgateway exposed on pushGatewayURL
// every pushIntervalSeconds. Metrics are fetched from metricsURL
func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushIntervalSeconds time.Duration) {
	p.Ppg.PushGatewayURL = pushGatewayURL
	p.Ppg.MetricsURL = metricsURL
	p.Ppg.PushIntervalSeconds = pushIntervalSeconds
	p.startPushTicker()
}

// SetPushGatewayJob job name, defaults to "echo"
func (p *Prometheus) SetPushGatewayJob(j string) {
	p.Ppg.Job = j
}

// SetListenAddress for exposing metrics on address. If not set, it will be exposed at the
// same address of the echo engine that is being used
// func (p *Prometheus) SetListenAddress(address string) {
// 	p.listenAddress = address
// 	if p.listenAddress != "" {
// 		p.router = echo.Echo().Router()
// 	}
// }

// SetListenAddressWithRouter for using a separate router to expose metrics. (this keeps things like GET /metrics out of
// your content's access log).
// func (p *Prometheus) SetListenAddressWithRouter(listenAddress string, r *echo.Echo) {
// 	p.listenAddress = listenAddress
// 	if len(p.listenAddress) > 0 {
// 		p.router = r
// 	}
// }

// SetMetricsPath set metrics paths
func (p *Prometheus) SetMetricsPath(e *echo.Echo) {
	if p.listenAddress != "" {
		p.router.GET(p.MetricsPath, prometheusHandler())
		p.runServer()
	} else {
		e.GET(p.MetricsPath, prometheusHandler())
	}
}

func (p *Prometheus) runServer() {
	if p.listenAddress != "" {
		go p.router.Start(p.listenAddress)
	}
}

func (p *Prometheus) getMetrics() []byte {
	response, err := http.Get(p.Ppg.MetricsURL)
	if err != nil {
		log.Errorf("Error getting metrics: %v", err)
	}
	defer response.Body.Close()
	body, _ := ioutil.ReadAll(response.Body)

	return body
}

func (p *Prometheus) getPushGatewayURL() string {
	h, _ := os.Hostname()
	if p.Ppg.Job == "" {
		p.Ppg.Job = "echo"
	}
	return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + h
}

func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) {
	req, err := http.NewRequest("POST", p.getPushGatewayURL(), bytes.NewBuffer(metrics))
	client := &http.Client{}
	if _, err = client.Do(req); err != nil {
		log.Errorf("Error sending to push gateway: %v", err)
	}
}

func (p *Prometheus) startPushTicker() {
	ticker := time.NewTicker(time.Second * p.Ppg.PushIntervalSeconds)
	go func() {
		for range ticker.C {
			p.sendMetricsToPushGateway(p.getMetrics())
		}
	}()
}

// NewMetric associates prometheus.Collector based on Metric.Type
func NewMetric(m *Metric, subsystem string) prometheus.Collector {
	var metric prometheus.Collector
	switch m.Type {
	case "counter_vec":
		metric = prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "counter":
		metric = prometheus.NewCounter(
			prometheus.CounterOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	case "gauge_vec":
		metric = prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "gauge":
		metric = prometheus.NewGauge(
			prometheus.GaugeOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	case "histogram_vec":
		metric = prometheus.NewHistogramVec(
			prometheus.HistogramOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "histogram":
		metric = prometheus.NewHistogram(
			prometheus.HistogramOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	case "summary_vec":
		metric = prometheus.NewSummaryVec(
			prometheus.SummaryOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "summary":
		metric = prometheus.NewSummary(
			prometheus.SummaryOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	}
	return metric
}

func (p *Prometheus) registerMetrics(subsystem string) {

	for _, metricDef := range p.MetricsList {
		metric := NewMetric(metricDef, subsystem)
		if err := prometheus.Register(metric); err != nil {
			log.Errorf("%s could not be registered in Prometheus: %v", metricDef.Name, err)
		}
		switch metricDef {
		case reqCnt:
			p.reqCnt = metric.(*prometheus.CounterVec)
		case reqDur:
			p.reqDur = metric.(prometheus.Summary)
		case resSz:
			p.resSz = metric.(prometheus.Summary)
		case reqSz:
			p.reqSz = metric.(prometheus.Summary)
		}
		metricDef.MetricCollector = metric
	}
}

// Use adds the middleware to the Echo engine.
func (p *Prometheus) Use(e *echo.Echo) {
	e.Use(p.HandlerFunc)
	p.SetMetricsPath(e)
}

// HandlerFunc defines handler function for middleware
func (p *Prometheus) HandlerFunc(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		if c.Path() == p.MetricsPath {
			return next(c)
		}
		if p.Skipper(c) {
			return next(c)
		}

		start := time.Now()
		reqSz := computeApproximateRequestSize(c.Request())

		status := strconv.Itoa(c.Response().Status)

		elapsed := float64(time.Since(start)) / float64(time.Second)
		resSz := float64(c.Response().Size)

		p.reqDur.Observe(elapsed)
		url := p.RequestCounterURLLabelMappingFunc(c)

		if len(p.URLLabelFromContext) > 0 {
			u := c.Get(p.URLLabelFromContext)
			if u == nil {
				u = "unknown"
			}
			url = u.(string)
		}

		p.reqCnt.WithLabelValues(status, c.Request().Method, c.Request().Host, url).Inc()
		p.reqSz.Observe(float64(reqSz))
		p.resSz.Observe(resSz)
		return next(c)
	}
}

func prometheusHandler() echo.HandlerFunc {
	h := promhttp.Handler()
	return func(c echo.Context) error {
		h.ServeHTTP(c.Response(), c.Request())
		return nil
	}
}

func computeApproximateRequestSize(r *http.Request) int {
	s := 0
	if r.URL != nil {
		s = len(r.URL.Path)
	}

	s += len(r.Method)
	s += len(r.Proto)
	for name, values := range r.Header {
		s += len(name)
		for _, value := range values {
			s += len(value)
		}
	}
	s += len(r.Host)

	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.

	if r.ContentLength != -1 {
		s += int(r.ContentLength)
	}
	return s
}
<commit_msg>handled response status correctly (#21)<commit_after>/*
Package prometheus provides middleware to add Prometheus metrics.

Example:
```
package main
import (
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo-contrib/prometheus"
)
func main() {
    e := echo.New()
    // Enable metrics middleware
    p := prometheus.NewPrometheus("echo", nil)
    p.Use(e)

    e.Logger.Fatal(e.Start(":1323"))
}
```
*/
package prometheus

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"github.com/labstack/gommon/log"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var defaultMetricPath = "/metrics"
var defaultSubsystem = "echo"

// Standard default metrics
//	counter, counter_vec, gauge, gauge_vec,
//	histogram, histogram_vec, summary, summary_vec
var reqCnt = &Metric{
	ID:          "reqCnt",
	Name:        "requests_total",
	Description: "How many HTTP requests processed, partitioned by status code and HTTP method.",
	Type:        "counter_vec",
	Args:        []string{"code", "method", "host", "url"}}

var reqDur = &Metric{
	ID:          "reqDur",
	Name:        "request_duration_seconds",
	Description: "The HTTP request latencies in seconds.",
	Type:        "summary"}

var resSz = &Metric{
	ID:          "resSz",
	Name:        "response_size_bytes",
	Description: "The HTTP response sizes in bytes.",
	Type:        "summary"}

var reqSz = &Metric{
	ID:          "reqSz",
	Name:        "request_size_bytes",
	Description: "The HTTP request sizes in bytes.",
	Type:        "summary"}

var standardMetrics = []*Metric{
	reqCnt,
	reqDur,
	resSz,
	reqSz,
}

/*
RequestCounterURLLabelMappingFunc is a function which can be supplied to the middleware to control
the cardinality of the request counter's "url" label, which might be required in some contexts.
For instance, if for a "/customer/:name" route you don't want to generate a time series for every
possible customer name, you could use this function:

func(c echo.Context) string {
	url := c.Request.URL.Path
	for _, p := range c.Params {
		if p.Key == "name" {
			url = strings.Replace(url, p.Value, ":name", 1)
			break
		}
	}
	return url
}

which would map "/customer/alice" and "/customer/bob" to their template "/customer/:name".
*/
type RequestCounterURLLabelMappingFunc func(c echo.Context) string

// Metric is a definition for the name, description, type, ID, and
// prometheus.Collector type (i.e. CounterVec, Summary, etc) of each metric
type Metric struct {
	MetricCollector prometheus.Collector
	ID              string
	Name            string
	Description     string
	Type            string
	Args            []string
}

// Prometheus contains the metrics gathered by the instance and its path
type Prometheus struct {
	reqCnt               *prometheus.CounterVec
	reqDur, reqSz, resSz prometheus.Summary
	router               *echo.Echo
	listenAddress        string
	Ppg                  PushGateway

	MetricsList []*Metric
	MetricsPath string
	Subsystem   string
	Skipper     middleware.Skipper

	RequestCounterURLLabelMappingFunc RequestCounterURLLabelMappingFunc

	// Context string to use as a prometheus URL label
	URLLabelFromContext string
}

// PushGateway contains the configuration for pushing to a Prometheus pushgateway (optional)
type PushGateway struct {
	// Push interval in seconds
	PushIntervalSeconds time.Duration

	// Push Gateway URL in format http://domain:port
	// where JOBNAME can be any string of your choice
	PushGatewayURL string

	// Local metrics URL where metrics are fetched from, this could be ommited in the future
	// if implemented using prometheus common/expfmt instead
	MetricsURL string

	// pushgateway job name, defaults to "echo"
	Job string
}

// NewPrometheus generates a new set of metrics with a certain subsystem name
func NewPrometheus(subsystem string, skipper middleware.Skipper, customMetricsList ...[]*Metric) *Prometheus {
	var metricsList []*Metric
	if skipper == nil {
		skipper = middleware.DefaultSkipper
	}

	if len(customMetricsList) > 1 {
		panic("Too many args. NewPrometheus( string, <optional []*Metric> ).")
	} else if len(customMetricsList) == 1 {
		metricsList = customMetricsList[0]
	}

	for _, metric := range standardMetrics {
		metricsList = append(metricsList, metric)
	}

	p := &Prometheus{
		MetricsList: metricsList,
		MetricsPath: defaultMetricPath,
		Subsystem:   defaultSubsystem,
		Skipper:     skipper,
		RequestCounterURLLabelMappingFunc: func(c echo.Context) string {
			return c.Path() // i.e. by default do nothing, i.e. return URL as is
		},
	}

	p.registerMetrics(subsystem)

	return p
}

// SetPushGateway sends metrics to a remote pushgateway exposed on pushGatewayURL
// every pushIntervalSeconds. Metrics are fetched from metricsURL
func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushIntervalSeconds time.Duration) {
	p.Ppg.PushGatewayURL = pushGatewayURL
	p.Ppg.MetricsURL = metricsURL
	p.Ppg.PushIntervalSeconds = pushIntervalSeconds
	p.startPushTicker()
}

// SetPushGatewayJob job name, defaults to "echo"
func (p *Prometheus) SetPushGatewayJob(j string) {
	p.Ppg.Job = j
}

// SetListenAddress for exposing metrics on address. If not set, it will be exposed at the
// same address of the echo engine that is being used
// func (p *Prometheus) SetListenAddress(address string) {
// 	p.listenAddress = address
// 	if p.listenAddress != "" {
// 		p.router = echo.Echo().Router()
// 	}
// }

// SetListenAddressWithRouter for using a separate router to expose metrics. (this keeps things like GET /metrics out of
// your content's access log).
// func (p *Prometheus) SetListenAddressWithRouter(listenAddress string, r *echo.Echo) {
// 	p.listenAddress = listenAddress
// 	if len(p.listenAddress) > 0 {
// 		p.router = r
// 	}
// }

// SetMetricsPath set metrics paths
func (p *Prometheus) SetMetricsPath(e *echo.Echo) {
	if p.listenAddress != "" {
		p.router.GET(p.MetricsPath, prometheusHandler())
		p.runServer()
	} else {
		e.GET(p.MetricsPath, prometheusHandler())
	}
}

func (p *Prometheus) runServer() {
	if p.listenAddress != "" {
		go p.router.Start(p.listenAddress)
	}
}

func (p *Prometheus) getMetrics() []byte {
	response, err := http.Get(p.Ppg.MetricsURL)
	if err != nil {
		log.Errorf("Error getting metrics: %v", err)
	}
	defer response.Body.Close()
	body, _ := ioutil.ReadAll(response.Body)

	return body
}

func (p *Prometheus) getPushGatewayURL() string {
	h, _ := os.Hostname()
	if p.Ppg.Job == "" {
		p.Ppg.Job = "echo"
	}
	return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + h
}

func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) {
	req, err := http.NewRequest("POST", p.getPushGatewayURL(), bytes.NewBuffer(metrics))
	client := &http.Client{}
	if _, err = client.Do(req); err != nil {
		log.Errorf("Error sending to push gateway: %v", err)
	}
}

func (p *Prometheus) startPushTicker() {
	ticker := time.NewTicker(time.Second * p.Ppg.PushIntervalSeconds)
	go func() {
		for range ticker.C {
			p.sendMetricsToPushGateway(p.getMetrics())
		}
	}()
}

// NewMetric associates prometheus.Collector based on Metric.Type
func NewMetric(m *Metric, subsystem string) prometheus.Collector {
	var metric prometheus.Collector
	switch m.Type {
	case "counter_vec":
		metric = prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "counter":
		metric = prometheus.NewCounter(
			prometheus.CounterOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	case "gauge_vec":
		metric = prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "gauge":
		metric = prometheus.NewGauge(
			prometheus.GaugeOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	case "histogram_vec":
		metric = prometheus.NewHistogramVec(
			prometheus.HistogramOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "histogram":
		metric = prometheus.NewHistogram(
			prometheus.HistogramOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	case "summary_vec":
		metric = prometheus.NewSummaryVec(
			prometheus.SummaryOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
			m.Args,
		)
	case "summary":
		metric = prometheus.NewSummary(
			prometheus.SummaryOpts{
				Subsystem: subsystem,
				Name:      m.Name,
				Help:      m.Description,
			},
		)
	}
	return metric
}

func (p *Prometheus) registerMetrics(subsystem string) {

	for _, metricDef := range p.MetricsList {
		metric := NewMetric(metricDef, subsystem)
		if err := prometheus.Register(metric); err != nil {
			log.Errorf("%s could not be registered in Prometheus: %v", metricDef.Name, err)
		}
		switch metricDef {
		case reqCnt:
			p.reqCnt = metric.(*prometheus.CounterVec)
		case reqDur:
			p.reqDur = metric.(prometheus.Summary)
		case resSz:
			p.resSz = metric.(prometheus.Summary)
		case reqSz:
			p.reqSz = metric.(prometheus.Summary)
		}
		metricDef.MetricCollector = metric
	}
}

// Use adds the middleware to the Echo engine.
func (p *Prometheus) Use(e *echo.Echo) {
	e.Use(p.HandlerFunc)
	p.SetMetricsPath(e)
}

// HandlerFunc defines handler function for middleware
func (p *Prometheus) HandlerFunc(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) (err error) {
		if c.Path() == p.MetricsPath {
			return next(c)
		}
		if p.Skipper(c) {
			return next(c)
		}

		start := time.Now()
		reqSz := computeApproximateRequestSize(c.Request())

		if err = next(c); err != nil {
			c.Error(err)
		}

		status := strconv.Itoa(c.Response().Status)

		elapsed := float64(time.Since(start)) / float64(time.Second)
		resSz := float64(c.Response().Size)

		p.reqDur.Observe(elapsed)
		url := p.RequestCounterURLLabelMappingFunc(c)

		if len(p.URLLabelFromContext) > 0 {
			u := c.Get(p.URLLabelFromContext)
			if u == nil {
				u = "unknown"
			}
			url = u.(string)
		}

		p.reqCnt.WithLabelValues(status, c.Request().Method, c.Request().Host, url).Inc()
		p.reqSz.Observe(float64(reqSz))
		p.resSz.Observe(resSz)

		return
	}
}

func prometheusHandler() echo.HandlerFunc {
	h := promhttp.Handler()
	return func(c echo.Context) error {
		h.ServeHTTP(c.Response(), c.Request())
		return nil
	}
}

func computeApproximateRequestSize(r *http.Request) int {
	s := 0
	if r.URL != nil {
		s = len(r.URL.Path)
	}

	s += len(r.Method)
	s += len(r.Proto)
	for name, values := range r.Header {
		s += len(name)
		for _, value := range values {
			s += len(value)
		}
	}
	s += len(r.Host)

	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.

	if r.ContentLength != -1 {
		s += int(r.ContentLength)
	}
	return s
}
<|endoftext|>


<commit_before>package zhihu

import (
	"fmt"
	"net/url"

	"github.com/zquestz/s/providers"
)

func init() {
	providers.AddProvider("zhihu", &Provider{})
}

// Provider merely implements the Provider interface.
type Provider struct{}

// BuildURI generates a search URL for zhihu.
func (p *Provider) BuildURI(q string) string {
	return fmt.Sprintf("https://www.zhihu.com/search?type=content&q=%s", url.QueryEscape(q))
}

// Tags returns the tags relevant to this provider.
func (p *Provider) Tags() []string {
	return []string{"forums"}
}
<commit_msg>Fix zhihu tags<commit_after>package zhihu

import (
	"fmt"
	"net/url"

	"github.com/zquestz/s/providers"
)

func init() {
	providers.AddProvider("zhihu", &Provider{})
}

// Provider merely implements the Provider interface.
type Provider struct{}

// BuildURI generates a search URL for zhihu.
func (p *Provider) BuildURI(q string) string {
	return fmt.Sprintf("https://www.zhihu.com/search?type=content&q=%s", url.QueryEscape(q))
}

// Tags returns the tags relevant to this provider.
func (p *Provider) Tags() []string {
	switch providers.Language() {
	case "zh":
		return []string{"forums"}
	default:
		return []string{}
	}
}
<|endoftext|>


<commit_before>package dnsfallback

import (
	"errors"
	"net"

	"github.com/eycorsican/go-tun2socks/common/dns"
	"github.com/eycorsican/go-tun2socks/core"
)

// UDP handler that intercepts DNS queries and replies with a truncated response (TC bit)
// in order for the client to retry over TCP. This DNS/TCP fallback mechanism is
// useful for proxy servers that do not support UDP.
// Note that non-DNS UDP traffic is dropped.
type udpHandler struct{}

const (
	dnsHeaderLength = 12
	dnsMaskQr       = uint8(0x80)
	dnsMaskTc       = uint8(0x02)
	dnsMaskRcode    = uint8(0x0F)
)

func NewUDPHandler() core.UDPConnHandler {
	return &udpHandler{}
}

func (h *udpHandler) Connect(conn core.UDPConn, target net.Addr) error {
	udpAddr, ok := target.(*net.UDPAddr)
	if !ok {
		return errors.New("Unsupported address type")
	}
	if udpAddr.Port != dns.COMMON_DNS_PORT {
		return errors.New("Cannot handle non-DNS packet")
	}
	return nil
}

func (h *udpHandler) DidReceiveTo(conn core.UDPConn, data []byte, addr net.Addr) error {
	if len(data) < dnsHeaderLength {
		return errors.New("Received malformed DNS query")
	}
	//  DNS Header
	//  0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                      ID                       |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    QDCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    ANCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    NSCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    ARCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	// Set response and truncated bits
	data[2] |= dnsMaskQr | dnsMaskTc
	// Set response code to 'no error'.
	data[3] &= ^dnsMaskRcode
	_, err := conn.WriteFrom(data, addr)
	return err
}
<commit_msg>DNS fallback: set ANCOUNT to QDCOUNT<commit_after>package dnsfallback

import (
	"encoding/binary"
	"errors"
	"net"

	"github.com/eycorsican/go-tun2socks/common/dns"
	"github.com/eycorsican/go-tun2socks/core"
)

// UDP handler that intercepts DNS queries and replies with a truncated response (TC bit)
// in order for the client to retry over TCP. This DNS/TCP fallback mechanism is
// useful for proxy servers that do not support UDP.
// Note that non-DNS UDP traffic is dropped.
type udpHandler struct{}

const (
	dnsHeaderLength = 12
	dnsMaskQr       = uint8(0x80)
	dnsMaskTc       = uint8(0x02)
	dnsMaskRcode    = uint8(0x0F)
)

func NewUDPHandler() core.UDPConnHandler {
	return &udpHandler{}
}

func (h *udpHandler) Connect(conn core.UDPConn, target net.Addr) error {
	udpAddr, ok := target.(*net.UDPAddr)
	if !ok {
		return errors.New("Unsupported address type")
	}
	if udpAddr.Port != dns.COMMON_DNS_PORT {
		return errors.New("Cannot handle non-DNS packet")
	}
	return nil
}

func (h *udpHandler) DidReceiveTo(conn core.UDPConn, data []byte, addr net.Addr) error {
	if len(data) < dnsHeaderLength {
		return errors.New("Received malformed DNS query")
	}
	//  DNS Header
	//  0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                      ID                       |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    QDCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    ANCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    NSCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	//  |                    ARCOUNT                    |
	//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	// Set response and truncated bits
	data[2] |= dnsMaskQr | dnsMaskTc
	// Set response code to 'no error'.
	data[3] &= ^dnsMaskRcode
	// Set ANCOUNT to QDCOUNT. This is technically incorrect, since the response does not
	// include an answer. However, without it some DNS clients (i.e. Windows 7) do not retry
	// over TCP.
	var qdcount = binary.BigEndian.Uint16(data[4:6])
	binary.BigEndian.PutUint16(data[6:], qdcount)
	_, err := conn.WriteFrom(data, addr)
	return err
}
<|endoftext|>


<commit_before>package proxyserver

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestHealthCheck(t *testing.T) {
	expectedBody := "OK"
	conf := "/etc/swift/proxy-server.conf"
	ip, port, handler, _, _ := GetServer(conf)
	recorder := httptest.NewRecorder()
	url := fmt.Sprintf("http://%s:%d/healthcheck", ip, port)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		t.Error("Unable to create new Request")
	}
	handler.ServeHTTP(recorder, req)

	if recorder.Body.String() != expectedBody {
		t.Error("Excepting ", expectedBody, " got ", recorder.Body.String())
	}
}

func TestGetServer(t *testing.T) {
	tests := []struct {
		conf    string
		err_msg string
	}{
		{"/etc/swift/proxy-server.conf", ""},
		{"/tmp/asdf", "Unable to load /tmp/asdf"},
	}
	for _, test := range tests {
		if test.err_msg != "" {
			_, _, _, _, err := GetServer(test.conf)
			assert.Equal(t, test.err_msg, err.Error())
			continue
		}
		ip, port, handler, _, _ := GetServer(test.conf)
		if proxy_handler, ok := handler.(ProxyHandler); ok {
			assert.Equal(t, ip, "0.0.0.0")
			assert.Equal(t, port, 8080)
			assert.NotNil(t, proxy_handler.mc)
			assert.NotNil(t, proxy_handler.client)
			assert.NotNil(t, proxy_handler.logger)
			assert.NotNil(t, proxy_handler.objectRing)
			assert.NotNil(t, proxy_handler.accountRing)
			assert.NotNil(t, proxy_handler.containerRing)
		}
	}

}
<commit_msg>test fix<commit_after>package proxyserver

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestHealthCheck(t *testing.T) {
	expectedBody := "OK"
	conf := "/etc/swift/proxy-server.conf"
	ip, port, handler, _, _ := GetServer(conf)
	recorder := httptest.NewRecorder()
	url := fmt.Sprintf("http://%s:%d/healthcheck", ip, port)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		t.Error("Unable to create new Request")
	}
	handler.ServeHTTP(recorder, req)

	if recorder.Body.String() != expectedBody {
		t.Error("Excepting ", expectedBody, " got ", recorder.Body.String())
	}
}

func TestGetServer(t *testing.T) {
	tests := []struct {
		conf    string
		err_msg string
	}{
		{"/etc/swift/proxy-server.conf", ""},
		{"/tmp/asdf", "Unable to load /tmp/asdf"},
	}
	for _, test := range tests {
		if test.err_msg != "" {
			_, _, _, _, err := GetServer(test.conf)
			assert.Equal(t, test.err_msg, err.Error())
			continue
		}
		ip, port, handler, _, _ := GetServer(test.conf)
		if proxy_handler, ok := handler.(ProxyHandler); ok {
			assert.NotNil(t, net.ParseIP(ip))
			assert.Equal(t, port, 8080)
			assert.NotNil(t, proxy_handler.mc)
			assert.NotNil(t, proxy_handler.client)
			assert.NotNil(t, proxy_handler.logger)
			assert.NotNil(t, proxy_handler.objectRing)
			assert.NotNil(t, proxy_handler.accountRing)
			assert.NotNil(t, proxy_handler.containerRing)
		}
	}
}
<|endoftext|>


<commit_before>package tme

import (
	"sync"
	"testing"
	"time"
)

func TestRealTickerSynchronousTicks(t *testing.T) {
	const dur = 1 * time.Millisecond
	const numChecks = 5
	ticker := NewRealTicker(dur, func() {})
	defer ticker.Stop()
	ch := ticker.Chan()
	for i := 0; i < numChecks; i++ {
		select {
		case <-ch:
		case <-time.After(dur * 2):
			t.Fatalf("ticker didn't tick within %s", dur*2)
		}
	}
}

func TestRealTickerBroadcastTicker(t *testing.T) {
	const dur = 10 * time.Millisecond
	const numChecks = 5
	const numThreads = 10
	ticker := NewRealTicker(dur, func() {})
	ch := ticker.Chan()
	defer ticker.Stop()
	var wg sync.WaitGroup
	for i := 0; i < numThreads; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			select {
			case <-ch:
			case <-time.After(dur * 10):
				t.Errorf("ticker didn't tick within %s (thread %d)", dur*10, i)
			}
		}(i)
	}
	wg.Wait()
}

func TestRealTickerStop(t *testing.T) {
	const dur = 10 * time.Millisecond
	ticker := NewRealTicker(dur, func() {})
	bch := ticker.Chan()
	select {
	case <-bch:
	case <-time.After(dur * 2):
		t.Errorf("ticker didn't tick within %s", dur*2)
	}
	ticker.Stop()
	select {
	case <-bch:
		t.Errorf("ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
	select {
	case <-ticker.Chan():
		t.Errorf("new channel from ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
}
<commit_msg>additional logging & checks<commit_after>package tme

import (
	"sync"
	"testing"
	"time"
)

func TestRealTickerSynchronousTicks(t *testing.T) {
	const dur = 1 * time.Millisecond
	const numChecks = 5
	ticker := NewRealTicker(dur, func() {})
	defer ticker.Stop()
	ch := ticker.Chan()
	for i := 0; i < numChecks; i++ {
		select {
		case <-ch:
		case <-time.After(dur * 2):
			t.Fatalf("ticker didn't tick within %s (thread %d)", dur*2, i)
		}
	}
}

func TestRealTickerBroadcastTicker(t *testing.T) {
	const dur = 10 * time.Millisecond
	const numChecks = 5
	const numThreads = 10
	ticker := NewRealTicker(dur, func() {})
	defer ticker.Stop()
	ch := ticker.Chan()
	var wg sync.WaitGroup
	for i := 0; i < numThreads; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			select {
			case <-ch:
			case <-time.After(dur * 10):
				t.Errorf("ticker didn't tick within %s (thread %d)", dur*10, i)
			}
		}(i)
	}
	wg.Wait()
}

func TestRealTickerStop(t *testing.T) {
	const dur = 10 * time.Millisecond
	ticker := NewRealTicker(dur, func() {})
	bch := ticker.Chan()
	select {
	case <-bch:
	case <-time.After(dur * 2):
		t.Errorf("ticker didn't tick within %s", dur*2)
	}
	if ticker.Stop() != true {
		t.Errorf("ticker was already marked stopped")
	}
	select {
	case <-bch:
		t.Errorf("ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
	select {
	case <-ticker.Chan():
		t.Errorf("new channel from ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
}
<|endoftext|>


<commit_before>package frame

import (
	"image"
	"image/draw"
)

func (f *Frame) Mark() {
	f.modified = true
}

// boxalign collects a list of pts of each box
// on the frame before and after an insertion occurs
func (f *Frame) boxalign(cb0 int64, b0 int, pt0, pt1 image.Point) (int64, int, image.Point, image.Point) {
	type Pts [2]image.Point
	f.pts = f.pts[:0]

	// collect the start pts for each box on the plane
	// before and after the insertion
	for {
		if pt0.X == pt1.X || pt1.Y == f.r.Max.Y || b0 == f.Nbox {
			break
		}
		b := &f.Box[b0]
		pt0 = f.wrapMax(pt0, b)
		pt1 = f.wrapMin(pt1, b)
		if b.Nrune > 0 {
			if n := f.fits(pt1, b); n != b.Nrune {
				f.Split(b0, n)
				b = &f.Box[b0]
			}
		}

		// early exit - point went off the frame
		if pt1.Y == f.r.Max.Y {
			break
		}

		f.pts = append(f.pts, Pts{pt0, pt1})

		pt0 = f.advance(pt0, b)
		pt1.X += f.plot(pt1, b)

		cb0 += int64(b.Len())
		b0++
	}
	return cb0, b0, pt0, pt1
}

// boxpush moves boxes down the frame to make room for an insertion
// from pt0 to pt1
func (f *Frame) boxpush(p0 int64, b0, b1 int, pt0, pt1, ppt1 image.Point) {

	// delete boxes that ran off the frame
	// and update the char count
	if pt1.Y == f.r.Max.Y && b0 < f.Nbox {
		f.Nchars -= f.Count(b0)
		f.Run.Delete(b0, f.Nbox-1)
	}

	// update the line count
	if b0 == f.Nbox {
		f.Nlines = (pt1.Y - f.r.Min.Y) / f.Font.Dy()
		if pt1.X > f.r.Min.X {
			f.Nlines++
		}
		return
	}

	if pt1.Y == pt0.Y {
		// insertion won't propagate down
		return
	}

	qt0 := pt0.Y + f.Font.Dy()
	qt1 := pt1.Y + f.Font.Dy()
	f.Nlines += (qt1 - qt0) / f.Font.Dy()
	if f.Nlines > f.maxlines {
		f.trim(ppt1, p0, b1)
	}

	// shift down the existing boxes
	// on the bitmap
	if r := f.r; pt1.Y < r.Max.Y {
		r.Min.Y = qt1

		// rectangular group of boxes
		if qt1 < f.r.Max.Y {
			f.Draw(f.b, r, f.b, image.Pt(f.r.Min.X, qt0), f.op)
		}

		// partial line
		r.Min = pt1
		r.Max.X = pt1.X + (f.r.Max.X - pt0.X)
		r.Max.Y = qt1
		f.Draw(f.b, r, f.b, pt0, f.op)
	}
}

func (f *Frame) boxmove(cb0 int64, b0 int, pt0, pt1, opt0 image.Point) {
	h := f.Font.Dy()
	y := 0
	if pt1.Y == f.r.Max.Y {
		y = pt1.Y
	}
	x := len(f.pts)
	run := f.Box[b0-x:]
	x--
	_, back := f.pick(cb0, f.p0, f.p1)
	for ; x >= 0; x-- {
		b := &run[x]
		br := image.Rect(0, 0, b.Width, h)
		pt := f.pts[x]
		if b.Nrune > 0 {
			f.Draw(f.b, br.Add(pt[1]), f.b, pt[0], f.op)
			// clear bit hanging off right
			if x == 0 && pt[1].Y > pt0.Y {
				_, back = f.pick(cb0, f.p0, f.p1)
				// line wrap - new char bigger than first char displaced
				r := br.Add(opt0)
				r.Max.X = f.r.Max.X
				f.Draw(f.b, r, back, r.Min, f.op)
			} else if pt[1].Y < y {
				// copy from left to right
				_, back = f.pick(cb0, f.p0, f.p1)

				r := image.ZR.Add(pt[1])
				r.Min.X += b.Width
				r.Max.X += f.r.Max.X
				r.Max.Y += h
				f.Draw(f.b, r, back, r.Min, f.op)
			}
			y = pt[1].Y
			cb0 -= int64(b.Nrune)
		} else {
			r := br.Add(pt[1])
			if r.Max.X >= f.r.Max.X {
				r.Max.X = f.r.Max.X
			}
			cb0--
			_, back = f.pick(cb0, f.p0, f.p1)
			f.Draw(f.b, r, back, r.Min, f.op)
			y = 0
			if pt[1].X == f.r.Min.X {
				y = pt[1].Y
			}
		}
	}
}

type offset struct {
	p0   int64
	b0   int
	cb0  int64
	b1   int64
	pt0  image.Point
	pt1  image.Point
	opt0 image.Point
}

func (f *Frame) Insert(s []byte, p0 int64) (wrote int) {
	type Pts [2]image.Point
	if p0 > f.Nchars || len(s) == 0 || f.b == nil {
		return
	}

	// find p0, it's box, and its point in the box its in
	b0 := f.Find(0, 0, p0)
	//ob0 := b0
	cb0 := p0
	b1 := b0
//	ebn := f.StartCell(b0)
//	ept := f.pointOfBox(p0, ebn)
	pt0 := f.pointOfBox(p0, b0)
	opt0 := pt0

	// find p1
	ppt0, pt1 := f.boxscan(s, pt0)
	ppt1 := pt1

	if ForceElasticTabstopExperiment {
		// With elastic tabstops, the scanned boxes need to be
		// connected to the frame's run to determine whether
		// anything above the scan point was affected by the
		// insertion.
		//
		// The old method is simple:
		//	1.) Scan text to be inserted, into boxes
		//	2.) Align the scanned boxes on the frame without drawing them
		//	2.) Push existing boxes down the bitmap
		//	3.) Bitblt the new boxes onto the bitmap
		//  ... now the elastic part
		//	4.) Realize the scanned boxes may affect the boxes above and below
		//  5.) Redraw the entire frame

//		f.ir.Box = append(f.Box[ebn:b0], f.ir.Box)
//		f.ir.Nbox += len(f.Box[ebn:b0])

		bn := f.Nbox
		for bn > 0 {
			bn = f.Stretch(bn)
		}
		f.Stretch(bn)
	}

	// Line wrap
	if b0 < f.Nbox {
		b := &f.Box[b0]
		pt0 = f.wrapMax(pt0, b)
		ppt1 = f.wrapMin(ppt1, b)
	}
	f.modified = true

	if f.p0 == f.p1 {
		f.tickat(f.PointOf(int64(f.p0)), false)
	}

	cb0, b0, pt0, pt1 = f.boxalign(cb0, b0, pt0, pt1)
	f.boxpush(p0, b0, b1, pt0, pt1, ppt1)
	f.boxmove(cb0, b0, pt0, pt1, opt0)
	text, back := f.pick(p0, f.p0+1, f.p1+1)
	f.Paint(ppt0, ppt1, back)
	f.redrawRun0(f.ir, ppt0, text, back)
	f.Run.Combine(f.ir, b1)
	if b1 > 0 && f.Box[b1-1].Nrune >= 0 && ppt0.X-f.Box[b1-1].Width >= f.r.Min.X {
		b1--
		ppt0.X -= f.Box[b1].Width
	}
	b0 += f.ir.Nbox
	if b0 < f.Nbox-1 {
		b0++
	}
	f.clean(ppt0, b1, b0)
	f.Nchars += f.ir.Nchars

	f.p0, f.p1 = coInsert(p0, p0+f.Nchars, f.p0, f.p1)
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(f.p0), true)
	}
	if ForceElasticTabstopExperiment {
		// Just to see if the algorithm works not ideal to sift through all of
		// the boxes per insertion, although surprisingly faster than expected
		// to the point of where its almost unnoticable without the print
		// statements
		bn := f.Nbox
		for bn > 0 {
			bn = f.Stretch(bn)
		}
		f.Stretch(bn)
		f.Refresh() // must do this until line mapper is fixed
	}
	return int(f.ir.Nchars)
}

func (f *Frame) pick(c, p0, p1 int64) (text, back image.Image) {
	if p0 <= c && c < p1 {
		return f.Color.Hi.Text, f.Color.Hi.Back
	}
	return f.Color.Text, f.Color.Back
}

func region(c, p0, p1 int64) int {
	if c < p0 {
		return -1
	}
	if c >= p1 {
		return 1
	}
	return 0
}

func drawBorder(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point, thick int) {
	draw.Draw(dst, image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+thick), src, sp, draw.Src)
	draw.Draw(dst, image.Rect(r.Min.X, r.Max.Y-thick, r.Max.X, r.Max.Y), src, sp, draw.Src)
	draw.Draw(dst, image.Rect(r.Min.X, r.Min.Y, r.Min.X+thick, r.Max.Y), src, sp, draw.Src)
	draw.Draw(dst, image.Rect(r.Max.X-thick, r.Min.Y, r.Max.X, r.Max.Y), src, sp, draw.Src)
}
<commit_msg>elastic tabstop integration<commit_after>package frame

import (
	"image"
	"image/draw"
)

func (f *Frame) Mark() {
	f.modified = true
}

// boxalign collects a list of pts of each box
// on the frame before and after an insertion occurs
func (f *Frame) boxalign(cb0 int64, b0 int, pt0, pt1 image.Point) (int64, int, image.Point, image.Point) {
	type Pts [2]image.Point
	f.pts = f.pts[:0]

	// collect the start pts for each box on the plane
	// before and after the insertion
	for {
		if pt0.X == pt1.X || pt1.Y == f.r.Max.Y || b0 == f.Nbox {
			break
		}
		b := &f.Box[b0]
		pt0 = f.wrapMax(pt0, b)
		pt1 = f.wrapMin(pt1, b)
		if b.Nrune > 0 {
			if n := f.fits(pt1, b); n != b.Nrune {
				f.Split(b0, n)
				b = &f.Box[b0]
			}
		}

		// early exit - point went off the frame
		if pt1.Y == f.r.Max.Y {
			break
		}

		f.pts = append(f.pts, Pts{pt0, pt1})

		pt0 = f.advance(pt0, b)
		pt1.X += f.plot(pt1, b)

		cb0 += int64(b.Len())
		b0++
	}
	return cb0, b0, pt0, pt1
}

// boxpush moves boxes down the frame to make room for an insertion
// from pt0 to pt1
func (f *Frame) boxpush(p0 int64, b0, b1 int, pt0, pt1, ppt1 image.Point) {

	// delete boxes that ran off the frame
	// and update the char count
	if pt1.Y == f.r.Max.Y && b0 < f.Nbox {
		f.Nchars -= f.Count(b0)
		f.Run.Delete(b0, f.Nbox-1)
	}

	// update the line count
	if b0 == f.Nbox {
		f.Nlines = (pt1.Y - f.r.Min.Y) / f.Font.Dy()
		if pt1.X > f.r.Min.X {
			f.Nlines++
		}
		return
	}

	if pt1.Y == pt0.Y {
		// insertion won't propagate down
		return
	}

	qt0 := pt0.Y + f.Font.Dy()
	qt1 := pt1.Y + f.Font.Dy()
	f.Nlines += (qt1 - qt0) / f.Font.Dy()
	if f.Nlines > f.maxlines {
		f.trim(ppt1, p0, b1)
	}

	// shift down the existing boxes
	// on the bitmap
	if r := f.r; pt1.Y < r.Max.Y {
		r.Min.Y = qt1

		// rectangular group of boxes
		if qt1 < f.r.Max.Y {
			f.Draw(f.b, r, f.b, image.Pt(f.r.Min.X, qt0), f.op)
		}

		// partial line
		r.Min = pt1
		r.Max.X = pt1.X + (f.r.Max.X - pt0.X)
		r.Max.Y = qt1
		f.Draw(f.b, r, f.b, pt0, f.op)
	}
}

func (f *Frame) boxmove(cb0 int64, b0 int, pt0, pt1, opt0 image.Point) {
	h := f.Font.Dy()
	y := 0
	if pt1.Y == f.r.Max.Y {
		y = pt1.Y
	}
	x := len(f.pts)
	run := f.Box[b0-x:]
	x--
	_, back := f.pick(cb0, f.p0, f.p1)
	for ; x >= 0; x-- {
		b := &run[x]
		br := image.Rect(0, 0, b.Width, h)
		pt := f.pts[x]
		if b.Nrune > 0 {
			f.Draw(f.b, br.Add(pt[1]), f.b, pt[0], f.op)
			// clear bit hanging off right
			if x == 0 && pt[1].Y > pt0.Y {
				_, back = f.pick(cb0, f.p0, f.p1)
				// line wrap - new char bigger than first char displaced
				r := br.Add(opt0)
				r.Max.X = f.r.Max.X
				f.Draw(f.b, r, back, r.Min, f.op)
			} else if pt[1].Y < y {
				// copy from left to right
				_, back = f.pick(cb0, f.p0, f.p1)

				r := image.ZR.Add(pt[1])
				r.Min.X += b.Width
				r.Max.X += f.r.Max.X
				r.Max.Y += h
				f.Draw(f.b, r, back, r.Min, f.op)
			}
			y = pt[1].Y
			cb0 -= int64(b.Nrune)
		} else {
			r := br.Add(pt[1])
			if r.Max.X >= f.r.Max.X {
				r.Max.X = f.r.Max.X
			}
			cb0--
			_, back = f.pick(cb0, f.p0, f.p1)
			f.Draw(f.b, r, back, r.Min, f.op)
			y = 0
			if pt[1].X == f.r.Min.X {
				y = pt[1].Y
			}
		}
	}
}

type offset struct {
	p0   int64
	b0   int
	cb0  int64
	b1   int64
	pt0  image.Point
	pt1  image.Point
	opt0 image.Point
}

func (f *Frame) Insert(s []byte, p0 int64) (wrote int) {
	type Pts [2]image.Point
	if p0 > f.Nchars || len(s) == 0 || f.b == nil {
		return
	}

	// find p0, it's box, and its point in the box its in
	b0 := f.Find(0, 0, p0)
	//ob0 := b0
	cb0 := p0
	b1 := b0
	//	ebn := f.StartCell(b0)
	//	ept := f.pointOfBox(p0, ebn)
	pt0 := f.pointOfBox(p0, b0)
	opt0 := pt0

	// find p1
	ppt0, pt1 := f.boxscan(s, pt0)
	ppt1 := pt1

	if ForceElasticTabstopExperiment {
		// With elastic tabstops, the scanned boxes need to be
		// connected to the frame's run to determine whether
		// anything above the scan point was affected by the
		// insertion.
		//
		// The old method is simple:
		//	1.) Scan text to be inserted, into boxes
		//	2.) Align the scanned boxes on the frame without drawing them
		//	2.) Push existing boxes down the bitmap
		//	3.) Bitblt the new boxes onto the bitmap
		//  ... now the elastic part
		//	4.) Realize the scanned boxes may affect the boxes above and below
		//  5.) Redraw the entire frame

		//		f.ir.Box = append(f.Box[ebn:b0], f.ir.Box)
		//		f.ir.Nbox += len(f.Box[ebn:b0])

		bn := f.Nbox
		for bn > 0 {
			bn = f.Stretch(bn)
		}
		f.Stretch(bn)
	}

	// Line wrap
	if b0 < f.Nbox {
		b := &f.Box[b0]
		pt0 = f.wrapMax(pt0, b)
		ppt1 = f.wrapMin(ppt1, b)
	}
	f.modified = true

	if f.p0 == f.p1 {
		f.tickat(f.PointOf(int64(f.p0)), false)
	}

	cb0, b0, pt0, pt1 = f.boxalign(cb0, b0, pt0, pt1)
	f.boxpush(p0, b0, b1, pt0, pt1, ppt1)
	f.boxmove(cb0, b0, pt0, pt1, opt0)
	text, back := f.pick(p0, f.p0+1, f.p1+1)
	f.Paint(ppt0, ppt1, back)
	f.redrawRun0(f.ir, ppt0, text, back)
	f.Run.Combine(f.ir, b1)
	if b1 > 0 && f.Box[b1-1].Nrune >= 0 && ppt0.X-f.Box[b1-1].Width >= f.r.Min.X {
		b1--
		ppt0.X -= f.Box[b1].Width
	}
	b0 += f.ir.Nbox
	if b0 < f.Nbox-1 {
		b0++
	}
	f.clean(ppt0, b1, b0)
	f.Nchars += f.ir.Nchars

	f.p0, f.p1 = coInsert(p0, p0+f.Nchars, f.p0, f.p1)
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(f.p0), true)
	}
	if ForceElasticTabstopExperiment {
		// Just to see if the algorithm works not ideal to sift through all of
		// the boxes per insertion, although surprisingly faster than expected
		// to the point of where its almost unnoticable without the print
		// statements
		bn := f.Nbox
		for bn > 0 {
			bn = f.Stretch(bn)
		}
		f.Stretch(bn)
		f.Refresh() // must do this until line mapper is fixed
	}
	return int(f.ir.Nchars)
}

func (f *Frame) pick(c, p0, p1 int64) (text, back image.Image) {
	if p0 <= c && c < p1 {
		return f.Color.Hi.Text, f.Color.Hi.Back
	}
	return f.Color.Text, f.Color.Back
}

func region(c, p0, p1 int64) int {
	if c < p0 {
		return -1
	}
	if c >= p1 {
		return 1
	}
	return 0
}

func drawBorder(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point, thick int) {
	draw.Draw(dst, image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+thick), src, sp, draw.Src)
	draw.Draw(dst, image.Rect(r.Min.X, r.Max.Y-thick, r.Max.X, r.Max.Y), src, sp, draw.Src)
	draw.Draw(dst, image.Rect(r.Min.X, r.Min.Y, r.Min.X+thick, r.Max.Y), src, sp, draw.Src)
	draw.Draw(dst, image.Rect(r.Max.X-thick, r.Min.Y, r.Max.X, r.Max.Y), src, sp, draw.Src)
}
<|endoftext|>


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

import (
	"reflect"
	"testing"
)

func TestMergeLocations(t *testing.T) {
	flm1 := FieldTermLocationMap{
		"marty": TermLocationMap{
			"name": {
				&Location{
					Pos:   1,
					Start: 0,
					End:   5,
				},
			},
		},
	}

	flm2 := FieldTermLocationMap{
		"marty": TermLocationMap{
			"description": {
				&Location{
					Pos:   5,
					Start: 20,
					End:   25,
				},
			},
		},
	}

	expectedMerge := FieldTermLocationMap{
		"marty": TermLocationMap{
			"description": {
				&Location{
					Pos:   5,
					Start: 20,
					End:   25,
				},
			},
			"name": {
				&Location{
					Pos:   1,
					Start: 0,
					End:   5,
				},
			},
		},
	}

	mergedLocations := mergeLocations([]FieldTermLocationMap{flm1, flm2})
	if !reflect.DeepEqual(expectedMerge, mergedLocations) {
		t.Errorf("expected %v, got %v", expectedMerge, mergedLocations)
	}
}
<commit_msg>cover another case in the merge<commit_after>//  Copyright (c) 2013 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.
package search

import (
	"reflect"
	"testing"
)

func TestMergeLocations(t *testing.T) {
	flm1 := FieldTermLocationMap{
		"marty": TermLocationMap{
			"name": {
				&Location{
					Pos:   1,
					Start: 0,
					End:   5,
				},
			},
		},
	}

	flm2 := FieldTermLocationMap{
		"marty": TermLocationMap{
			"description": {
				&Location{
					Pos:   5,
					Start: 20,
					End:   25,
				},
			},
		},
	}

	flm3 := FieldTermLocationMap{
		"josh": TermLocationMap{
			"description": {
				&Location{
					Pos:   5,
					Start: 20,
					End:   25,
				},
			},
		},
	}

	expectedMerge := FieldTermLocationMap{
		"marty": TermLocationMap{
			"description": {
				&Location{
					Pos:   5,
					Start: 20,
					End:   25,
				},
			},
			"name": {
				&Location{
					Pos:   1,
					Start: 0,
					End:   5,
				},
			},
		},
		"josh": TermLocationMap{
			"description": {
				&Location{
					Pos:   5,
					Start: 20,
					End:   25,
				},
			},
		},
	}

	mergedLocations := mergeLocations([]FieldTermLocationMap{flm1, flm2, flm3})
	if !reflect.DeepEqual(expectedMerge, mergedLocations) {
		t.Errorf("expected %v, got %v", expectedMerge, mergedLocations)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/rbkreisberg/CloudForest"
	"log"
	"os"
	"fmt"
)

func main() {
	fm := flag.String("fm", "featurematrix.afm", "AFM formated feature matrix to use.")
	rf := flag.String("rfpred", "rface.sf", "A predictor forest as outputed by rf-ace")
	outf := flag.String("splits", "splits.tsv", "a case by case sparse matrix of leaf cooccurance in tsv format")
	boutf := flag.String("branches", "branches.tsv", "a case by feature sparse matrix of case/splitter cooccurance in tsv format")
	rboutf := flag.String("branches", "relativeBranches.tsv", "a case by feature sparse matrix of split direction for each case/feature in tsv format")

	flag.Parse()

	datafile, err := os.Open(*fm) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer datafile.Close()
	data := CloudForest.ParseAFM(datafile)
	nfeatures := len(data.Data)
	ncases := len(data.Data[0].Data)
	log.Print("Data file ", nfeatures, " by ", ncases)

	forestfile, err := os.Open(*rf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer forestfile.Close()
	forest := CloudForest.ParseRfAcePredictor(forestfile)
	log.Print("Forest has ", len(forest.Trees), " trees ")

	featureCounts := make([]int, nfeatures) // a total count of the number of times each feature was used to split
	caseFeatureCounts := new(CloudForest.SparseCounter)
	relativeSplitCount := new(CloudForest.SparseCounter)

	for i := 0; i < len(forest.Trees); i++ {
		splits := forest.Trees[i].GetSplits(data, caseFeatureCounts, relativeSplitCount)

		for _, split := range splits {
			featureCounts[data.Map[split.Feature]]++ //increment the count for the total # of times the feature was a splitter
		}
	}

	log.Print("Outputing Split Feature/Case Cooccurance Counts")
	outfile, err := os.Create(*outf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer outfile.Close()
	for feature, count := range featureCounts {
		if _, err := fmt.Fprintf(outfile, "%v\t%v\n", feature, count); err != nil {
			log.Fatal(err)
		}
	}

	log.Print("Outputing Case Feature Cooccurance Counts")
	boutfile, err := os.Create(*boutf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer boutfile.Close()
	caseFeatureCounts.WriteTsv(boutfile)

	log.Print("Outputing Case Feature Splitter Direction")
	rboutfile, err := os.Create(*rboutf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer rboutfile.Close()
	relativeSplitCount.WriteTsv(boutfile)
}
<commit_msg>fix parameter name<commit_after>package main

import (
	"flag"
	"github.com/rbkreisberg/CloudForest"
	"log"
	"os"
	"fmt"
)

func main() {
	fm := flag.String("fm", "featurematrix.afm", "AFM formated feature matrix to use.")
	rf := flag.String("rfpred", "rface.sf", "A predictor forest as outputed by rf-ace")
	outf := flag.String("splits", "splits.tsv", "a case by case sparse matrix of leaf cooccurance in tsv format")
	boutf := flag.String("branches", "branches.tsv", "a case by feature sparse matrix of case/splitter cooccurance in tsv format")
	rboutf := flag.String("relbranches", "relativeBranches.tsv", "a case by feature sparse matrix of split direction for each case/feature in tsv format")

	flag.Parse()

	datafile, err := os.Open(*fm) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer datafile.Close()
	data := CloudForest.ParseAFM(datafile)
	nfeatures := len(data.Data)
	ncases := len(data.Data[0].Data)
	log.Print("Data file ", nfeatures, " by ", ncases)

	forestfile, err := os.Open(*rf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer forestfile.Close()
	forest := CloudForest.ParseRfAcePredictor(forestfile)
	log.Print("Forest has ", len(forest.Trees), " trees ")

	featureCounts := make([]int, nfeatures) // a total count of the number of times each feature was used to split
	caseFeatureCounts := new(CloudForest.SparseCounter)
	relativeSplitCount := new(CloudForest.SparseCounter)

	for i := 0; i < len(forest.Trees); i++ {
		splits := forest.Trees[i].GetSplits(data, caseFeatureCounts, relativeSplitCount)

		for _, split := range splits {
			featureCounts[data.Map[split.Feature]]++ //increment the count for the total # of times the feature was a splitter
		}
	}

	log.Print("Outputing Split Feature/Case Cooccurance Counts")
	outfile, err := os.Create(*outf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer outfile.Close()
	for feature, count := range featureCounts {
		if _, err := fmt.Fprintf(outfile, "%v\t%v\n", feature, count); err != nil {
			log.Fatal(err)
		}
	}

	log.Print("Outputing Case Feature Cooccurance Counts")
	boutfile, err := os.Create(*boutf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer boutfile.Close()
	caseFeatureCounts.WriteTsv(boutfile)

	log.Print("Outputing Case Feature Splitter Direction")
	rboutfile, err := os.Create(*rboutf) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer rboutfile.Close()
	relativeSplitCount.WriteTsv(boutfile)
}
<|endoftext|>