<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"runtime"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/cloud66/cxlogger"
)

var (
	USER_AGENT string = "gocker/" + VERSION + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"
)

type Notifier struct {
	endpoint string

	client *http.Client
}

type Payload struct {
	CallbackId      string    `json:"callback_id"`
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

type PayloadFull struct {
	CallbackId string      `json:"callback_id"`
	Containers []Container `json:"containers"`
}

type Container struct {
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

func (n *Notifier) notify(status string, process *DockerProcess) (string, error) {
	cxlogger.Log.Infof("Notifying server about %s", process.uid)
	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	runtimeInspect, err := process.Inspect()
	if err != nil {
		return "", err
	}

	payload := Payload{
		CallbackId:      config.CallbackId,
		ContainerId:     process.uid,
		LastObservation: process.lastObservedAt,
		Status:          status,
		Runtime:         runtimeInspect,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) notifyAll(processes []*DockerProcess) (string, error) {
	if processes == nil {
		cxlogger.Log.Infof("Notifying server full - no processes running")
	} else {
		cxlogger.Log.Infof("Notifying server full - %d processes running", len(processes))
	}

	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	containers := make([]Container, 0)
	for _, process := range processes {
		runtimeInspect, err := process.Inspect()
		if err != nil {
			cxlogger.Log.Infof("<<unable to get runtime information>>")
			runtimeInspect = "[{\"error\":\"unable to get runtime information\"}]"
		}
		container := Container{
			ContainerId:     process.uid,
			LastObservation: process.lastObservedAt,
			Status:          "new",
			Runtime:         runtimeInspect,
		}
		containers = append(containers, container)
		cxlogger.Log.Infof("%d containers created", len(containers))
	}

	payload := PayloadFull{
		CallbackId: config.CallbackId,
		Containers: containers,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) PerformPost(payload interface{}) (string, error) {
	var rbody io.Reader

	j, err := json.Marshal(payload)
	if err != nil {
		return "", err
	}
	rbody = bytes.NewReader(j)

	req, err := http.NewRequest("POST", n.endpoint, rbody)
	if err != nil {
		return "", err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Request-Id", uuid.New())
	req.Header.Set("User-Agent", USER_AGENT)
	req.Header.Set("Content-Type", "application/json")

	if cxlogger.Log.Level == cxlogger.LvlDebug {
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			cxlogger.Debug(err)
		} else {
			cxlogger.Debug(string(dump[:]))
		}
	}

	res, err := n.client.Do(req)
	// possible race?
	defer res.Body.Close()

	if err != nil {
		return "", err
	}

	if cxlogger.Log.Level == cxlogger.LvlDebug {
		dump, err := httputil.DumpResponse(res, true)
		if err != nil {
			cxlogger.Debug(err)
		} else {
			cxlogger.Debug(string(dump[:]))
		}
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	return string(body), nil
}
<commit_msg>bug in the stream close<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"runtime"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/cloud66/cxlogger"
)

var (
	USER_AGENT string = "gocker/" + VERSION + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"
)

type Notifier struct {
	endpoint string

	client *http.Client
}

type Payload struct {
	CallbackId      string    `json:"callback_id"`
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

type PayloadFull struct {
	CallbackId string      `json:"callback_id"`
	Containers []Container `json:"containers"`
}

type Container struct {
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

func (n *Notifier) notify(status string, process *DockerProcess) (string, error) {
	cxlogger.Log.Infof("Notifying server about %s", process.uid)
	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	runtimeInspect, err := process.Inspect()
	if err != nil {
		return "", err
	}

	payload := Payload{
		CallbackId:      config.CallbackId,
		ContainerId:     process.uid,
		LastObservation: process.lastObservedAt,
		Status:          status,
		Runtime:         runtimeInspect,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) notifyAll(processes []*DockerProcess) (string, error) {
	if processes == nil {
		cxlogger.Log.Infof("Notifying server full - no processes running")
	} else {
		cxlogger.Log.Infof("Notifying server full - %d processes running", len(processes))
	}

	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	containers := make([]Container, 0)
	for _, process := range processes {
		runtimeInspect, err := process.Inspect()
		if err != nil {
			cxlogger.Log.Infof("<<unable to get runtime information>>")
			runtimeInspect = "[{\"error\":\"unable to get runtime information\"}]"
		}
		container := Container{
			ContainerId:     process.uid,
			LastObservation: process.lastObservedAt,
			Status:          "new",
			Runtime:         runtimeInspect,
		}
		containers = append(containers, container)
		cxlogger.Log.Infof("%d containers created", len(containers))
	}

	payload := PayloadFull{
		CallbackId: config.CallbackId,
		Containers: containers,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) PerformPost(payload interface{}) (string, error) {
	var rbody io.Reader

	j, err := json.Marshal(payload)
	if err != nil {
		return "", err
	}
	rbody = bytes.NewReader(j)

	req, err := http.NewRequest("POST", n.endpoint, rbody)
	if err != nil {
		return "", err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Request-Id", uuid.New())
	req.Header.Set("User-Agent", USER_AGENT)
	req.Header.Set("Content-Type", "application/json")

	if cxlogger.Log.Level == cxlogger.LvlDebug {
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			cxlogger.Debug(err)
		} else {
			cxlogger.Debug(string(dump[:]))
		}
	}

	res, err := n.client.Do(req)
	if err != nil {
		return "", err
	}

	if cxlogger.Log.Level == cxlogger.LvlDebug {
		dump, err := httputil.DumpResponse(res, true)
		if err != nil {
			cxlogger.Debug(err)
		} else {
			cxlogger.Debug(string(dump[:]))
		}
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	the_body := string(body)
	defer res.Body.Close()

	return the_body, nil
}
<|endoftext|>


<commit_before>package connection

import (
	"bufio"
	"encoding/binary"
	"errors"
	"sync/atomic"

	"github.com/VolantMQ/volantmq/packet"
	"github.com/troian/easygo/netpoll"
)

func (s *impl) rxRun(event netpoll.Event) {
	if atomic.CompareAndSwapUint32(&s.rxRunning, 0, 1) {
		mask := netpoll.EventHup | netpoll.EventReadHup | netpoll.EventWriteHup | netpoll.EventErr | netpoll.EventPollClosed
		if (event & mask) != 0 {
			go s.onConnectionClose(nil)
		} else {
			go func() {
				s.rxWg.Wait()
				s.rxWg.Add(1)
				s.rxRoutine()
			}()
		}
	}
}

func (s *impl) rxConnection(event netpoll.Event) {
	mask := netpoll.EventHup | netpoll.EventReadHup | netpoll.EventWriteHup | netpoll.EventErr | netpoll.EventPollClosed
	if (event & mask) != 0 {
		go func() {
			s.connect <- errors.New("disconnected")
		}()
	} else {
		go func() {
			s.connectionRoutine()
		}()
	}
}

func (s *impl) rxRoutine() {
	var err error

	defer func() {
		s.rxWg.Done()
		if err != nil {
			s.onConnectionClose(err)
		}
	}()

	buf := bufio.NewReader(s.conn)

	for atomic.LoadUint32(&s.rxRunning) == 1 {
		s.runKeepAlive()

		var pkt packet.Provider
		if pkt, err = s.readPacket(buf); err == nil {
			s.metric.Received(pkt.Type())
			err = s.processIncoming(pkt)
		}

		if err != nil {
			atomic.StoreUint32(&s.rxRunning, 0)
			break
		}
	}

	if _, ok := err.(packet.ReasonCode); ok {
		return
	}

	err = s.ePoll.Resume(s.desc)
}

func (s *impl) connectionRoutine() {
	buf := bufio.NewReader(s.conn)

	pkt, err := s.readPacket(buf)

	s.keepAliveTimer.Stop()
	if err == nil {
		s.metric.Received(pkt.Type())
		err = s.processIncoming(pkt)
	} else {
		s.connect <- err
	}
}

func (s *impl) readPacket(buf *bufio.Reader) (packet.Provider, error) {
	var err error

	if len(s.rxRecv) == 0 {
		var header []byte
		peekCount := 2
		// Let's read enough bytes to get the fixed header/fh (msg type/flags, remaining length)
		for {
			// max length of fh is 5 bytes
			// if we have read 5 bytes and still not done report protocol error and exit
			if peekCount > 5 {
				return nil, packet.CodeProtocolError
			}

			if header, err = buf.Peek(peekCount); err != nil {
				return nil, err
			}

			// If we got enough bytes, then check the last byte to see if the continuation
			// bit is set. If so, increment cnt and continue peeking
			if header[peekCount-1] >= 0x80 {
				peekCount++
			} else {
				break
			}
		}

		// Get the remaining length of the message
		remLen, m := binary.Uvarint(header[1:])
		// Total message length is remlen + 1 (msg type) + m (remlen bytes)
		s.rxRemaining = int(remLen) + 1 + m
		s.rxRecv = make([]byte, s.rxRemaining)
	}

	if s.rxRemaining > int(s.maxRxPacketSize) {
		return nil, packet.CodePacketTooLarge
	}

	offset := len(s.rxRecv) - s.rxRemaining

	for offset != s.rxRemaining {
		var n int

		n, err = buf.Read(s.rxRecv[offset:])
		offset += n
		if err != nil {
			s.rxRemaining -= offset
			return nil, err
		}
	}

	var pkt packet.Provider
	pkt, _, err = packet.Decode(s.version, s.rxRecv)

	s.rxRecv = []byte{}
	s.rxRemaining = 0

	return pkt, err
}
<commit_msg>Ref #80 check error after packet decode at connect stage<commit_after>package connection

import (
	"bufio"
	"encoding/binary"
	"errors"
	"sync/atomic"

	"github.com/VolantMQ/volantmq/packet"
	"github.com/troian/easygo/netpoll"
)

func (s *impl) rxRun(event netpoll.Event) {
	if atomic.CompareAndSwapUint32(&s.rxRunning, 0, 1) {
		mask := netpoll.EventHup | netpoll.EventReadHup | netpoll.EventWriteHup | netpoll.EventErr | netpoll.EventPollClosed
		if (event & mask) != 0 {
			go s.onConnectionClose(nil)
		} else {
			go func() {
				s.rxWg.Wait()
				s.rxWg.Add(1)
				s.rxRoutine()
			}()
		}
	}
}

func (s *impl) rxConnection(event netpoll.Event) {
	mask := netpoll.EventHup | netpoll.EventReadHup | netpoll.EventWriteHup | netpoll.EventErr | netpoll.EventPollClosed
	if (event & mask) != 0 {
		go func() {
			s.connect <- errors.New("disconnected")
		}()
	} else {
		go func() {
			s.connectionRoutine()
		}()
	}
}

func (s *impl) rxRoutine() {
	var err error

	defer func() {
		s.rxWg.Done()
		if err != nil {
			s.onConnectionClose(err)
		}
	}()

	buf := bufio.NewReader(s.conn)

	for atomic.LoadUint32(&s.rxRunning) == 1 {
		s.runKeepAlive()

		var pkt packet.Provider
		if pkt, err = s.readPacket(buf); err == nil {
			s.metric.Received(pkt.Type())
			err = s.processIncoming(pkt)
		}

		if err != nil {
			atomic.StoreUint32(&s.rxRunning, 0)
			break
		}
	}

	if _, ok := err.(packet.ReasonCode); ok {
		return
	}

	err = s.ePoll.Resume(s.desc)
}

func (s *impl) connectionRoutine() {
	buf := bufio.NewReader(s.conn)

	pkt, err := s.readPacket(buf)

	s.keepAliveTimer.Stop()
	if err == nil {
		s.metric.Received(pkt.Type())
		err = s.processIncoming(pkt)
	}

	s.connect <- err
}

func (s *impl) readPacket(buf *bufio.Reader) (packet.Provider, error) {
	var err error

	if len(s.rxRecv) == 0 {
		var header []byte
		peekCount := 2
		// Let's read enough bytes to get the fixed header/fh (msg type/flags, remaining length)
		for {
			// max length of fh is 5 bytes
			// if we have read 5 bytes and still not done report protocol error and exit
			if peekCount > 5 {
				return nil, packet.CodeProtocolError
			}

			if header, err = buf.Peek(peekCount); err != nil {
				return nil, err
			}

			// If we got enough bytes, then check the last byte to see if the continuation
			// bit is set. If so, increment cnt and continue peeking
			if header[peekCount-1] >= 0x80 {
				peekCount++
			} else {
				break
			}
		}

		// Get the remaining length of the message
		remLen, m := binary.Uvarint(header[1:])
		// Total message length is remlen + 1 (msg type) + m (remlen bytes)
		s.rxRemaining = int(remLen) + 1 + m
		s.rxRecv = make([]byte, s.rxRemaining)
	}

	if s.rxRemaining > int(s.maxRxPacketSize) {
		return nil, packet.CodePacketTooLarge
	}

	offset := len(s.rxRecv) - s.rxRemaining

	for offset != s.rxRemaining {
		var n int

		n, err = buf.Read(s.rxRecv[offset:])
		offset += n
		if err != nil {
			s.rxRemaining -= offset
			return nil, err
		}
	}

	var pkt packet.Provider
	pkt, _, err = packet.Decode(s.version, s.rxRecv)

	s.rxRecv = []byte{}
	s.rxRemaining = 0

	return pkt, err
}
<|endoftext|>


<commit_before>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package blockchain

import (
	"fmt"
	"math/big"
	"time"

	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/log"
	am "github.com/fractalplatform/fractal/accountmanager"
	"github.com/fractalplatform/fractal/asset"
	"github.com/fractalplatform/fractal/common"
	"github.com/fractalplatform/fractal/consensus/dpos"
	"github.com/fractalplatform/fractal/params"
	"github.com/fractalplatform/fractal/rawdb"
	"github.com/fractalplatform/fractal/state"
	"github.com/fractalplatform/fractal/types"
	"github.com/fractalplatform/fractal/utils/fdb"
)

// GenesisAccount is an account in the state of the genesis block.
type GenesisAccount struct {
	Name   common.Name   `json:"name,omitempty"`
	PubKey common.PubKey `json:"pubKey,omitempty"`
}

// Genesis specifies the header fields, state of a genesis block.
type Genesis struct {
	Config        *params.ChainConfig  `json:"config"`
	Dpos          *dpos.Config         `json:"dpos"`
	Timestamp     uint64               `json:"timestamp"`
	ExtraData     []byte               `json:"extraData"`
	GasLimit      uint64               `json:"gasLimit" `
	Difficulty    *big.Int             `json:"difficulty" `
	Coinbase      common.Name          `json:"coinbase"`
	AllocAccounts []*GenesisAccount    `json:"allocAccounts"`
	AllocAssets   []*asset.AssetObject `json:"allocAssets"`
}

// SetupGenesisBlock The returned chain configuration is never nil.
func SetupGenesisBlock(db fdb.Database, genesis *Genesis) (*params.ChainConfig, *dpos.Config, common.Hash, error) {
	if genesis != nil && genesis.Config == nil {
		return params.DefaultChainconfig, dpos.DefaultConfig, common.Hash{}, errGenesisNoConfig
	}
	if genesis != nil && genesis.Dpos == nil {
		return params.DefaultChainconfig, dpos.DefaultConfig, common.Hash{}, errGenesisNoDpos
	}

	// Just commit the new block if there is no stored genesis block.
	stored := rawdb.ReadCanonicalHash(db, 0)
	if (stored == common.Hash{}) {
		if genesis == nil {
			genesis = DefaultGenesis()
		}
		block, err := genesis.Commit(db)
		log.Info("Writing genesis block", "hash", block.Hash().Hex())
		return genesis.Config, genesis.Dpos, block.Hash(), err
	}

	// Check whether the genesis block is already written.
	if genesis != nil {
		hash := genesis.ToBlock(nil).Hash()
		if hash != stored {
			return genesis.Config, genesis.Dpos, hash, &GenesisMismatchError{stored, hash}
		}
	}
	// Get the existing dpos configuration.
	newdpos := genesis.dposOrDefault(stored)

	// Get the existing chain configuration.
	newcfg := genesis.configOrDefault(stored)

	height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db))
	if height == nil {
		return newcfg, newdpos, stored, fmt.Errorf("missing block number for head header hash")
	}
	err := newdpos.Write(db, append([]byte("ft-dpos-"), stored.Bytes()...))
	rawdb.WriteChainConfig(db, stored, newcfg)
	return newcfg, newdpos, stored, err
}

// ToBlock creates the genesis block and writes state of a genesis specification
// to the given database (or discards it if nil).
func (g *Genesis) ToBlock(db fdb.Database) *types.Block {
	if db == nil {
		db = fdb.NewMemDatabase()
	}
	number := big.NewInt(0)
	statedb, err := state.New(common.Hash{}, state.NewDatabase(db))
	if err != nil {
		panic(fmt.Sprintf("genesis statedb new err: %v", err))
	}
	accountManager, err := am.NewAccountManager(statedb)
	if err != nil {
		panic(fmt.Sprintf("genesis accountManager new err: %v", err))
	}

	// dpos
	if !common.IsValidName(g.Dpos.SystemName) {
		panic(fmt.Sprintf("genesis invalid dpos account name %v", g.Dpos.SystemName))
	}
	g.AllocAccounts = append(g.AllocAccounts, &GenesisAccount{
		Name:   common.StrToName(g.Dpos.AccountName),
		PubKey: common.PubKey{},
	})
	if err := dpos.Genesis(g.Dpos, statedb, number.Uint64()); err != nil {
		panic(fmt.Sprintf("genesis dpos err %v", g.Dpos.SystemName))
	}

	for _, account := range g.AllocAccounts {
		if err := accountManager.CreateAccount(account.Name, account.PubKey); err != nil {
			panic(fmt.Sprintf("genesis create account err %v", err))
		}
	}

	for _, asset := range g.AllocAssets {
		if err := accountManager.IssueAsset(asset); err != nil {
			panic(fmt.Sprintf("genesis issue asset err %v", err))
		}
	}

	root := statedb.IntermediateRoot()
	head := &types.Header{
		Number:     number,
		Time:       new(big.Int).SetUint64(g.Timestamp),
		ParentHash: common.Hash{},
		Extra:      g.ExtraData,
		GasLimit:   g.GasLimit,
		GasUsed:    0,
		Difficulty: g.Difficulty,
		Coinbase:   g.Coinbase,
		Root:       root,
	}

	block := types.NewBlock(head, nil, nil)
	batch := db.NewBatch()
	if _, err := statedb.Commit(batch, block.Hash(), block.NumberU64()); err != nil {
		panic(fmt.Sprintf("genesis statedb commit err: %v", err))
	}
	if err := batch.Write(); err != nil {
		panic(fmt.Sprintf("genesis batch write err: %v", err))
	}
	return block
}

// Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
func (g *Genesis) Commit(db fdb.Database) (*types.Block, error) {
	block := g.ToBlock(db)
	if block.Number().Sign() != 0 {
		return nil, fmt.Errorf("can't commit genesis block with number > 0")
	}
	rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty)
	rawdb.WriteBlock(db, block)
	rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
	rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
	rawdb.WriteHeadBlockHash(db, block.Hash())
	rawdb.WriteHeadHeaderHash(db, block.Hash())

	config := g.Config
	if config == nil {
		config = params.DefaultChainconfig
	}
	dposConfig := g.Dpos
	if dposConfig == nil {
		dposConfig = dpos.DefaultConfig
	}

	rawdb.WriteChainConfig(db, block.Hash(), config)
	return block, nil
}

func (g *Genesis) dposOrDefault(ghash common.Hash) *dpos.Config {
	if g != nil {
		return g.Dpos
	}
	return dpos.DefaultConfig
}

func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
	if g != nil {
		return g.Config
	}
	return params.DefaultChainconfig
}

// DefaultGenesis returns the ft net genesis block.
func DefaultGenesis() *Genesis {
	gtime, _ := time.Parse("2006-01-02 15:04:05.999999999", "2019-01-16 00:00:00")
	return &Genesis{
		Config:        params.DefaultChainconfig,
		Dpos:          dpos.DefaultConfig,
		Timestamp:     uint64(gtime.UnixNano()),
		ExtraData:     hexutil.MustDecode(hexutil.Encode([]byte("ft Genesis Block"))),
		GasLimit:      params.GenesisGasLimit,
		Difficulty:    params.GenesisDifficulty,
		Coinbase:      params.DefaultChainconfig.SysName,
		AllocAccounts: DefaultGenesisAccounts(),
		AllocAssets:   DefaultGenesisAssets(),
	}
}

// DefaultGenesisAccounts returns the ft net genesis accounts.
func DefaultGenesisAccounts() []*GenesisAccount {
	pubKey := common.HexToPubKey(params.DefaultPubkeyHex)
	return []*GenesisAccount{
		&GenesisAccount{
			Name:   params.DefaultChainconfig.SysName,
			PubKey: pubKey,
		},
	}
}

// DefaultGenesisAssets returns the ft net genesis assets.
func DefaultGenesisAssets() []*asset.AssetObject {
	supply := new(big.Int)
	supply.SetString("100000000000000000000000000000", 10)
	return []*asset.AssetObject{
		&asset.AssetObject{
			AssetName: params.DefaultChainconfig.SysToken,
			Symbol:    "ft",
			Amount:    supply,
			Decimals:  18,
			Owner:     params.DefaultChainconfig.SysName,
		},
	}
}
<commit_msg>add genesis.json into genesis block extra<commit_after>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package blockchain

import (
	"fmt"
	"math/big"
	"time"

	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/log"
	am "github.com/fractalplatform/fractal/accountmanager"
	"github.com/fractalplatform/fractal/asset"
	"github.com/fractalplatform/fractal/common"
	"github.com/fractalplatform/fractal/consensus/dpos"
	"github.com/fractalplatform/fractal/params"
	"github.com/fractalplatform/fractal/rawdb"
	"github.com/fractalplatform/fractal/state"
	"github.com/fractalplatform/fractal/types"
	"github.com/fractalplatform/fractal/utils/fdb"
)

// GenesisAccount is an account in the state of the genesis block.
type GenesisAccount struct {
	Name   common.Name   `json:"name,omitempty"`
	PubKey common.PubKey `json:"pubKey,omitempty"`
}

// Genesis specifies the header fields, state of a genesis block.
type Genesis struct {
	Config        *params.ChainConfig  `json:"config"`
	Dpos          *dpos.Config         `json:"dpos"`
	Timestamp     uint64               `json:"timestamp"`
	ExtraData     []byte               `json:"extraData"`
	GasLimit      uint64               `json:"gasLimit" `
	Difficulty    *big.Int             `json:"difficulty" `
	Coinbase      common.Name          `json:"coinbase"`
	AllocAccounts []*GenesisAccount    `json:"allocAccounts"`
	AllocAssets   []*asset.AssetObject `json:"allocAssets"`
}

// SetupGenesisBlock The returned chain configuration is never nil.
func SetupGenesisBlock(db fdb.Database, genesis *Genesis) (*params.ChainConfig, *dpos.Config, common.Hash, error) {
	if genesis != nil && genesis.Config == nil {
		return params.DefaultChainconfig, dpos.DefaultConfig, common.Hash{}, errGenesisNoConfig
	}
	if genesis != nil && genesis.Dpos == nil {
		return params.DefaultChainconfig, dpos.DefaultConfig, common.Hash{}, errGenesisNoDpos
	}

	// Just commit the new block if there is no stored genesis block.
	stored := rawdb.ReadCanonicalHash(db, 0)
	if (stored == common.Hash{}) {
		if genesis == nil {
			genesis = DefaultGenesis()
		}
		block, err := genesis.Commit(db)
		log.Info("Writing genesis block", "hash", block.Hash().Hex())
		return genesis.Config, genesis.Dpos, block.Hash(), err
	}

	// Check whether the genesis block is already written.
	if genesis != nil {
		hash := genesis.ToBlock(nil).Hash()
		if hash != stored {
			return genesis.Config, genesis.Dpos, hash, &GenesisMismatchError{stored, hash}
		}
	}
	// Get the existing dpos configuration.
	newdpos := genesis.dposOrDefault(stored)

	// Get the existing chain configuration.
	newcfg := genesis.configOrDefault(stored)

	height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db))
	if height == nil {
		return newcfg, newdpos, stored, fmt.Errorf("missing block number for head header hash")
	}
	err := newdpos.Write(db, append([]byte("ft-dpos-"), stored.Bytes()...))
	rawdb.WriteChainConfig(db, stored, newcfg)
	return newcfg, newdpos, stored, err
}

// ToBlock creates the genesis block and writes state of a genesis specification
// to the given database (or discards it if nil).
func (g *Genesis) ToBlock(db fdb.Database) *types.Block {
	if db == nil {
		db = fdb.NewMemDatabase()
	}
	number := big.NewInt(0)
	statedb, err := state.New(common.Hash{}, state.NewDatabase(db))
	if err != nil {
		panic(fmt.Sprintf("genesis statedb new err: %v", err))
	}
	accountManager, err := am.NewAccountManager(statedb)
	if err != nil {
		panic(fmt.Sprintf("genesis accountManager new err: %v", err))
	}

	// dpos
	if !common.IsValidName(g.Dpos.SystemName) {
		panic(fmt.Sprintf("genesis invalid dpos account name %v", g.Dpos.SystemName))
	}
	g.AllocAccounts = append(g.AllocAccounts, &GenesisAccount{
		Name:   common.StrToName(g.Dpos.AccountName),
		PubKey: common.PubKey{},
	})
	if err := dpos.Genesis(g.Dpos, statedb, number.Uint64()); err != nil {
		panic(fmt.Sprintf("genesis dpos err %v", g.Dpos.SystemName))
	}

	for _, account := range g.AllocAccounts {
		if err := accountManager.CreateAccount(account.Name, account.PubKey); err != nil {
			panic(fmt.Sprintf("genesis create account err %v", err))
		}
	}

	for _, asset := range g.AllocAssets {
		if err := accountManager.IssueAsset(asset); err != nil {
			panic(fmt.Sprintf("genesis issue asset err %v", err))
		}
	}

	root := statedb.IntermediateRoot()
	gjson, _ := g.MarshalJSON()
	head := &types.Header{
		Number:     number,
		Time:       new(big.Int).SetUint64(g.Timestamp),
		ParentHash: common.Hash{},
		Extra:      gjson,
		GasLimit:   g.GasLimit,
		GasUsed:    0,
		Difficulty: g.Difficulty,
		Coinbase:   g.Coinbase,
		Root:       root,
	}

	block := types.NewBlock(head, nil, nil)
	batch := db.NewBatch()
	if _, err := statedb.Commit(batch, block.Hash(), block.NumberU64()); err != nil {
		panic(fmt.Sprintf("genesis statedb commit err: %v", err))
	}
	if err := batch.Write(); err != nil {
		panic(fmt.Sprintf("genesis batch write err: %v", err))
	}
	return block
}

// Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
func (g *Genesis) Commit(db fdb.Database) (*types.Block, error) {
	block := g.ToBlock(db)
	if block.Number().Sign() != 0 {
		return nil, fmt.Errorf("can't commit genesis block with number > 0")
	}
	rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty)
	rawdb.WriteBlock(db, block)
	rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
	rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
	rawdb.WriteHeadBlockHash(db, block.Hash())
	rawdb.WriteHeadHeaderHash(db, block.Hash())

	config := g.Config
	if config == nil {
		config = params.DefaultChainconfig
	}
	dposConfig := g.Dpos
	if dposConfig == nil {
		dposConfig = dpos.DefaultConfig
	}

	rawdb.WriteChainConfig(db, block.Hash(), config)
	return block, nil
}

func (g *Genesis) dposOrDefault(ghash common.Hash) *dpos.Config {
	if g != nil {
		return g.Dpos
	}
	return dpos.DefaultConfig
}

func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
	if g != nil {
		return g.Config
	}
	return params.DefaultChainconfig
}

// DefaultGenesis returns the ft net genesis block.
func DefaultGenesis() *Genesis {
	gtime, _ := time.Parse("2006-01-02 15:04:05.999999999", "2019-01-16 00:00:00")
	return &Genesis{
		Config:        params.DefaultChainconfig,
		Dpos:          dpos.DefaultConfig,
		Timestamp:     uint64(gtime.UnixNano()),
		ExtraData:     hexutil.MustDecode(hexutil.Encode([]byte("ft Genesis Block"))),
		GasLimit:      params.GenesisGasLimit,
		Difficulty:    params.GenesisDifficulty,
		Coinbase:      params.DefaultChainconfig.SysName,
		AllocAccounts: DefaultGenesisAccounts(),
		AllocAssets:   DefaultGenesisAssets(),
	}
}

// DefaultGenesisAccounts returns the ft net genesis accounts.
func DefaultGenesisAccounts() []*GenesisAccount {
	pubKey := common.HexToPubKey(params.DefaultPubkeyHex)
	return []*GenesisAccount{
		&GenesisAccount{
			Name:   params.DefaultChainconfig.SysName,
			PubKey: pubKey,
		},
	}
}

// DefaultGenesisAssets returns the ft net genesis assets.
func DefaultGenesisAssets() []*asset.AssetObject {
	supply := new(big.Int)
	supply.SetString("100000000000000000000000000000", 10)
	return []*asset.AssetObject{
		&asset.AssetObject{
			AssetName: params.DefaultChainconfig.SysToken,
			Symbol:    "ft",
			Amount:    supply,
			Decimals:  18,
			Owner:     params.DefaultChainconfig.SysName,
		},
	}
}
<|endoftext|>


<commit_before>package blockchain

import (
	"bytes"
	"errors"
	"reflect"

	wire "github.com/tendermint/go-wire"
	"github.com/blockchain/p2p"
	"github.com/blockchain/types"
	cmn "github.com/tendermint/tmlibs/common"
    "github.com/blockchain/protocol"
)

const (
	// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
	BlockchainChannel = byte(0x40)

	defaultChannelCapacity = 100
	defaultSleepIntervalMS = 500
	trySyncIntervalMS      = 100
	// stop syncing when last block's time is
	// within this much of the system time.
	// stopSyncingDurationMinutes = 10

	// ask for best height every 10s
	statusUpdateIntervalSeconds = 10
	// check if we should switch to consensus reactor
	switchToConsensusIntervalSeconds = 1
	maxBlockchainResponseSize        = types.MaxBlockSize + 2
)

/*
type consensusReactor interface {
	// for when we switch from blockchain reactor and fast sync to
	// the consensus machine
	SwitchToConsensus(*sm.State)
}
*/

// BlockchainReactor handles long-term catchup syncing.
type BlockchainReactor struct {
	p2p.BaseReactor

//	state        *sm.State
//	proxyAppConn proxy.AppConnConsensus // same as consensus.proxyAppConn
//	store        *BlockStore
//	pool         *BlockPool
//	fastSync     bool
	requestsCh   chan BlockRequest
	timeoutsCh   chan string
//	lastBlock    *types.Block

	evsw types.EventSwitch
}

func NewBlockchainReactor() *BlockchainReactor {
    requestsCh    := make(chan BlockRequest, defaultChannelCapacity)
    timeoutsCh    := make(chan string, defaultChannelCapacity)

    bcR := &BlockchainReactor {
        requestsCh:    requestsCh,
        timeoutsCh:    timeoutsCh,
    }
    bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
    return bcR
}

// OnStart implements BaseService
func (bcR *BlockchainReactor) OnStart() error {
	bcR.BaseReactor.OnStart()
	return nil
}

// OnStop implements BaseService
func (bcR *BlockchainReactor) OnStop() {
	bcR.BaseReactor.OnStop()
}

// GetChannels implements Reactor
func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
	return []*p2p.ChannelDescriptor{
		&p2p.ChannelDescriptor{
			ID:                BlockchainChannel,
			Priority:          5,
			SendQueueCapacity: 100,
		},
	}
}

// AddPeer implements Reactor by sending our state to peer.
func (bcR *BlockchainReactor) AddPeer(peer *p2p.Peer) {
	/*if !peer.Send(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) {
		// doing nothing, will try later in `poolRoutine`
	}*/
}

// RemovePeer implements Reactor by removing peer from the pool.
func (bcR *BlockchainReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
	//bcR.pool.RemovePeer(peer.Key)
}

// Receive implements Reactor by handling 4 types of messages (look below).
func (bcR *BlockchainReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
	_, msg, err := DecodeMessage(msgBytes)
	if err != nil {
		bcR.Logger.Error("Error decoding message", "error", err)
		return
	}

	bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)

	switch msg := msg.(type) {
	case *bcBlockRequestMessage:
		// Got a request for a block. Respond with block if we have it.
		/*block := bcR.store.LoadBlock(msg.Height)
		if block != nil {
			msg := &bcBlockResponseMessage{Block: block}
			queued := src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
			if !queued {
				// queue is full, just ignore.
			}
		} else {
			// TODO peer is asking for things we don't have.
		}*/
	case *bcBlockResponseMessage:
		// Got a block.
		//bcR.pool.AddBlock(src.Key, msg.Block, len(msgBytes))
	case *bcStatusRequestMessage:
		// Send peer our state.
		/*queued := src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}})
		if !queued {
			// sorry
		}*/
	case *bcStatusResponseMessage:
		// Got a peer status. Unverified.
		//bcR.pool.SetPeerHeight(src.Key, msg.Height)
	default:
		bcR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
	}
}

/*
// Handle messages from the poolReactor telling the reactor what to do.
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
func (bcR *BlockchainReactor) poolRoutine() {

	trySyncTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
	statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
	switchToConsensusTicker := time.NewTicker(switchToConsensusIntervalSeconds * time.Second)

FOR_LOOP:
	for {
		select {
		case request := <-bcR.requestsCh: // chan BlockRequest
			peer := bcR.Switch.Peers().Get(request.PeerID)
			if peer == nil {
				continue FOR_LOOP // Peer has since been disconnected.
			}
			msg := &bcBlockRequestMessage{request.Height}
			queued := peer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
			if !queued {
				// We couldn't make the request, send-queue full.
				// The pool handles timeouts, just let it go.
				continue FOR_LOOP
			}
		case peerID := <-bcR.timeoutsCh: // chan string
			// Peer timed out.
			peer := bcR.Switch.Peers().Get(peerID)
			if peer != nil {
				bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout"))
			}
		case _ = <-statusUpdateTicker.C:
			// ask for status updates
			go bcR.BroadcastStatusRequest()
		case _ = <-switchToConsensusTicker.C:
			height, numPending, _ := bcR.pool.GetStatus()
			outbound, inbound, _ := bcR.Switch.NumPeers()
			bcR.Logger.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters),
				"outbound", outbound, "inbound", inbound)
			if bcR.pool.IsCaughtUp() {
				bcR.Logger.Info("Time to switch to consensus reactor!", "height", height)
				bcR.pool.Stop()

				conR := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
				conR.SwitchToConsensus(bcR.state)

				break FOR_LOOP
			}
		case _ = <-trySyncTicker.C: // chan time
			// This loop can be slow as long as it's doing syncing work.
		SYNC_LOOP:
			for i := 0; i < 10; i++ {
				// See if there are any blocks to sync.
				first, second := bcR.pool.PeekTwoBlocks()
				//bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
				if first == nil || second == nil {
					// We need both to sync the first block.
					break SYNC_LOOP
				}
				firstParts := first.MakePartSet(types.DefaultBlockPartSize)
				firstPartsHeader := firstParts.Header()
				// Finally, verify the first block using the second's commit
				// NOTE: we can probably make this more efficient, but note that calling
				// first.Hash() doesn't verify the tx contents, so MakePartSet() is
				// currently necessary.
				err := bcR.state.Validators.VerifyCommit(
					bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit)
				if err != nil {
					bcR.Logger.Info("error in validation", "error", err)
					bcR.pool.RedoRequest(first.Height)
					break SYNC_LOOP
				} else {
					bcR.pool.PopRequest()

					bcR.store.SaveBlock(first, firstParts, second.LastCommit)

					// TODO: should we be firing events? need to fire NewBlock events manually ...
					// NOTE: we could improve performance if we
					// didn't make the app commit to disk every block
					// ... but we would need a way to get the hash without it persisting
					err := bcR.state.ApplyBlock(bcR.evsw, bcR.proxyAppConn, first, firstPartsHeader, types.MockMempool{})
					if err != nil {
						// TODO This is bad, are we zombie?
						cmn.PanicQ(cmn.Fmt("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
					}
				}
			}
			continue FOR_LOOP
		case <-bcR.Quit:
			break FOR_LOOP
		}
	}
}

// BroadcastStatusRequest broadcasts `BlockStore` height.
func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
	bcR.Switch.Broadcast(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusRequestMessage{bcR.store.Height()}})
	return nil
}

*/

// SetEventSwitch implements events.Eventable
func (bcR *BlockchainReactor) SetEventSwitch(evsw types.EventSwitch) {
	bcR.evsw = evsw
}

//-----------------------------------------------------------------------------
// Messages

const (
	msgTypeBlockRequest   = byte(0x10)
	msgTypeBlockResponse  = byte(0x11)
	msgTypeStatusResponse = byte(0x20)
	msgTypeStatusRequest  = byte(0x21)
)

// BlockchainMessage is a generic message for this reactor.
type BlockchainMessage interface{}

var _ = wire.RegisterInterface(
	struct{ BlockchainMessage }{},
	wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest},
	wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse},
	wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse},
	wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
)

// DecodeMessage decodes BlockchainMessage.
// TODO: ensure that bz is completely read.
func DecodeMessage(bz []byte) (msgType byte, msg BlockchainMessage, err error) {
	msgType = bz[0]
	n := int(0)
	r := bytes.NewReader(bz)
	msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxBlockchainResponseSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage
	if err != nil && n != len(bz) {
		err = errors.New("DecodeMessage() had bytes left over")
	}
	return
}

//-------------------------------------

type bcBlockRequestMessage struct {
	Height int
}

func (m *bcBlockRequestMessage) String() string {
	return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height)
}

//-------------------------------------

// NOTE: keep up-to-date with maxBlockchainResponseSize
type bcBlockResponseMessage struct {
	Block *types.Block
}

func (m *bcBlockResponseMessage) String() string {
	return cmn.Fmt("[bcBlockResponseMessage %v]", m.Block.Height)
}

//-------------------------------------

type bcStatusRequestMessage struct {
	Height int
}

func (m *bcStatusRequestMessage) String() string {
	return cmn.Fmt("[bcStatusRequestMessage %v]", m.Height)
}

//-------------------------------------

type bcStatusResponseMessage struct {
	Height int
}

func (m *bcStatusResponseMessage) String() string {
	return cmn.Fmt("[bcStatusResponseMessage %v]", m.Height)
}
<commit_msg>Added block pool .<commit_after>package blockchain

import (
	"bytes"
	"errors"
	"reflect"
    "time"

	wire "github.com/tendermint/go-wire"
	"github.com/blockchain/p2p"
	"github.com/blockchain/types"
	cmn "github.com/tendermint/tmlibs/common"
 //   "github.com/blockchain/protocol"
)

const (
	// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
	BlockchainChannel = byte(0x40)

	defaultChannelCapacity = 100
	defaultSleepIntervalMS = 500
	trySyncIntervalMS      = 100
	// stop syncing when last block's time is
	// within this much of the system time.
	// stopSyncingDurationMinutes = 10

	// ask for best height every 10s
	statusUpdateIntervalSeconds = 10
	// check if we should switch to consensus reactor
	switchToConsensusIntervalSeconds = 1
	maxBlockchainResponseSize        = types.MaxBlockSize + 2
)

/*
type consensusReactor interface {
	// for when we switch from blockchain reactor and fast sync to
	// the consensus machine
	SwitchToConsensus(*sm.State)
}
*/

// BlockchainReactor handles long-term catchup syncing.
type BlockchainReactor struct {
	p2p.BaseReactor

//	state        *sm.State
//	proxyAppConn proxy.AppConnConsensus // same as consensus.proxyAppConn
//	store        *BlockStore
	pool         *BlockPool
//	fastSync     bool
	requestsCh   chan BlockRequest
	timeoutsCh   chan string
//	lastBlock    *types.Block

	evsw types.EventSwitch
}

func NewBlockchainReactor() *BlockchainReactor {
    requestsCh    := make(chan BlockRequest, defaultChannelCapacity)
    timeoutsCh    := make(chan string, defaultChannelCapacity)

    bcR := &BlockchainReactor {
        requestsCh:    requestsCh,
        timeoutsCh:    timeoutsCh,
    }
    bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
    return bcR
}

// OnStart implements BaseService
func (bcR *BlockchainReactor) OnStart() error {
	bcR.BaseReactor.OnStart()
	return nil
}

// OnStop implements BaseService
func (bcR *BlockchainReactor) OnStop() {
	bcR.BaseReactor.OnStop()
}

// GetChannels implements Reactor
func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
	return []*p2p.ChannelDescriptor{
		&p2p.ChannelDescriptor{
			ID:                BlockchainChannel,
			Priority:          5,
			SendQueueCapacity: 100,
		},
	}
}

// AddPeer implements Reactor by sending our state to peer.
func (bcR *BlockchainReactor) AddPeer(peer *p2p.Peer) {
	//if !peer.Send(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) {
		// doing nothing, will try later in `poolRoutine`
	//}
}

// RemovePeer implements Reactor by removing peer from the pool.
func (bcR *BlockchainReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
	bcR.pool.RemovePeer(peer.Key)
}

// Receive implements Reactor by handling 4 types of messages (look below).
func (bcR *BlockchainReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
	_, msg, err := DecodeMessage(msgBytes)
	if err != nil {
		bcR.Logger.Error("Error decoding message", "error", err)
		return
	}

	bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)

	switch msg := msg.(type) {
	case *bcBlockRequestMessage:
		// Got a request for a block. Respond with block if we have it.
		/*block := bcR.store.LoadBlock(msg.Height)
		if block != nil {
			msg := &bcBlockResponseMessage{Block: block}
			queued := src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
			if !queued {
				// queue is full, just ignore.
			}
		} else {
			// TODO peer is asking for things we don't have.
		}*/
	case *bcBlockResponseMessage:
		// Got a block.
		bcR.pool.AddBlock(src.Key, msg.Block, len(msgBytes))
	case *bcStatusRequestMessage:
		// Send peer our state.
		/*queued := src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}})
		if !queued {
			// sorry
		}*/
	case *bcStatusResponseMessage:
		// Got a peer status. Unverified.
		bcR.pool.SetPeerHeight(src.Key, msg.Height)
	default:
		bcR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
	}
}

// Handle messages from the poolReactor telling the reactor what to do.
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
func (bcR *BlockchainReactor) poolRoutine() {

	trySyncTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
	statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
	//switchToConsensusTicker := time.NewTicker(switchToConsensusIntervalSeconds * time.Second)

FOR_LOOP:
	for {
		select {
		case request := <-bcR.requestsCh: // chan BlockRequest
			peer := bcR.Switch.Peers().Get(request.PeerID)
			if peer == nil {
				continue FOR_LOOP // Peer has since been disconnected.
			}
			msg := &bcBlockRequestMessage{request.Height}
			queued := peer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
			if !queued {
				// We couldn't make the request, send-queue full.
				// The pool handles timeouts, just let it go.
				continue FOR_LOOP
			}
		case peerID := <-bcR.timeoutsCh: // chan string
			// Peer timed out.
			peer := bcR.Switch.Peers().Get(peerID)
			if peer != nil {
				bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout"))
			}
		case _ = <-statusUpdateTicker.C:
			// ask for status updates
			go bcR.BroadcastStatusRequest()
		/*case _ = <-switchToConsensusTicker.C:
			height, numPending, _ := bcR.pool.GetStatus()
			outbound, inbound, _ := bcR.Switch.NumPeers()
			bcR.Logger.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters),
				"outbound", outbound, "inbound", inbound)
			if bcR.pool.IsCaughtUp() {
				bcR.Logger.Info("Time to switch to consensus reactor!", "height", height)
				bcR.pool.Stop()

				conR := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
				conR.SwitchToConsensus(bcR.state)

				break FOR_LOOP
			}*/
		case _ = <-trySyncTicker.C: // chan time
			// This loop can be slow as long as it's doing syncing work.
		SYNC_LOOP:
			for i := 0; i < 10; i++ {
				// See if there are any blocks to sync.
				first, second := bcR.pool.PeekTwoBlocks()
				//bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
				if first == nil || second == nil {
					// We need both to sync the first block.
					break SYNC_LOOP
				}
				//firstParts := first.MakePartSet(types.DefaultBlockPartSize)
				//firstPartsHeader := firstParts.Header()
				// Finally, verify the first block using the second's commit
				// NOTE: we can probably make this more efficient, but note that calling
				// first.Hash() doesn't verify the tx contents, so MakePartSet() is
				// currently necessary.
				/*err := bcR.state.Validators.VerifyCommit(
					bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit)
				if err != nil {
					bcR.Logger.Info("error in validation", "error", err)
					bcR.pool.RedoRequest(first.Height)
					break SYNC_LOOP
				} else {
					bcR.pool.PopRequest()

					bcR.store.SaveBlock(first, firstParts, second.LastCommit)

					// TODO: should we be firing events? need to fire NewBlock events manually ...
					// NOTE: we could improve performance if we
					// didn't make the app commit to disk every block
					// ... but we would need a way to get the hash without it persisting
					err := bcR.state.ApplyBlock(bcR.evsw, bcR.proxyAppConn, first, firstPartsHeader, types.MockMempool{})
					if err != nil {
						// TODO This is bad, are we zombie?
						cmn.PanicQ(cmn.Fmt("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
					}
				}*/
			}
			continue FOR_LOOP
		case <-bcR.Quit:
			break FOR_LOOP
		}
	}
}

// BroadcastStatusRequest broadcasts `BlockStore` height.
func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
	//bcR.Switch.Broadcast(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusRequestMessage{bcR.store.Height()}})
	return nil
}

// SetEventSwitch implements events.Eventable
func (bcR *BlockchainReactor) SetEventSwitch(evsw types.EventSwitch) {
	bcR.evsw = evsw
}

//-----------------------------------------------------------------------------
// Messages

const (
	msgTypeBlockRequest   = byte(0x10)
	msgTypeBlockResponse  = byte(0x11)
	msgTypeStatusResponse = byte(0x20)
	msgTypeStatusRequest  = byte(0x21)
)

// BlockchainMessage is a generic message for this reactor.
type BlockchainMessage interface{}

var _ = wire.RegisterInterface(
	struct{ BlockchainMessage }{},
	wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest},
	wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse},
	wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse},
	wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
)

// DecodeMessage decodes BlockchainMessage.
// TODO: ensure that bz is completely read.
func DecodeMessage(bz []byte) (msgType byte, msg BlockchainMessage, err error) {
	msgType = bz[0]
	n := int(0)
	r := bytes.NewReader(bz)
	msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxBlockchainResponseSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage
	if err != nil && n != len(bz) {
		err = errors.New("DecodeMessage() had bytes left over")
	}
	return
}

//-------------------------------------

type bcBlockRequestMessage struct {
	Height int
}

func (m *bcBlockRequestMessage) String() string {
	return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height)
}

//-------------------------------------

// NOTE: keep up-to-date with maxBlockchainResponseSize
type bcBlockResponseMessage struct {
	Block *types.Block
}

func (m *bcBlockResponseMessage) String() string {
	return cmn.Fmt("[bcBlockResponseMessage %v]", m.Block.Height)
}

//-------------------------------------

type bcStatusRequestMessage struct {
	Height int
}

func (m *bcStatusRequestMessage) String() string {
	return cmn.Fmt("[bcStatusRequestMessage %v]", m.Height)
}

//-------------------------------------

type bcStatusResponseMessage struct {
	Height int
}

func (m *bcStatusResponseMessage) String() string {
	return cmn.Fmt("[bcStatusResponseMessage %v]", m.Height)
}
<|endoftext|>


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

// +build appengine

package build

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"runtime"
	"sort"
	"text/template"

	"appengine"
	"appengine/datastore"
	"appengine/delay"
	"appengine/mail"
	"appengine/urlfetch"
)

const (
	mailFrom   = "builder@golang.org" // use this for sending any mail
	failMailTo = "golang-dev@googlegroups.com"
	domain     = "build.golang.org"
	gobotBase  = "http://research.swtch.com/gobot_codereview"
)

// ignoreFailure is a set of builders that we don't email about because
// they are not yet production-ready.
var ignoreFailure = map[string]bool{
	"dragonfly-386":         true,
	"dragonfly-amd64":       true,
	"netbsd-amd64-bsiegert": true,
	"netbsd-arm-rpi":        true,
	"plan9-amd64-aram":      true,
}

// notifyOnFailure checks whether the supplied Commit or the subsequent
// Commit (if present) breaks the build for this builder.
// If either of those commits break the build an email notification is sent
// from a delayed task. (We use a task because this way the mail won't be
// sent if the enclosing datastore transaction fails.)
//
// This must be run in a datastore transaction, and the provided *Commit must
// have been retrieved from the datastore within that transaction.
func notifyOnFailure(c appengine.Context, com *Commit, builder string) error {
	if ignoreFailure[builder] {
		return nil
	}

	// TODO(adg): implement notifications for packages
	if com.PackagePath != "" {
		return nil
	}

	p := &Package{Path: com.PackagePath}
	var broken *Commit
	cr := com.Result(builder, "")
	if cr == nil {
		return fmt.Errorf("no result for %s/%s", com.Hash, builder)
	}
	q := datastore.NewQuery("Commit").Ancestor(p.Key(c))
	if cr.OK {
		// This commit is OK. Notify if next Commit is broken.
		next := new(Commit)
		q = q.Filter("ParentHash=", com.Hash)
		if err := firstMatch(c, q, next); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// OK at tip, no notification necessary.
				return nil
			}
			return err
		}
		if nr := next.Result(builder, ""); nr != nil && !nr.OK {
			c.Debugf("commit ok: %#v\nresult: %#v", com, cr)
			c.Debugf("next commit broken: %#v\nnext result:%#v", next, nr)
			broken = next
		}
	} else {
		// This commit is broken. Notify if the previous Commit is OK.
		prev := new(Commit)
		q = q.Filter("Hash=", com.ParentHash)
		if err := firstMatch(c, q, prev); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// No previous result, let the backfill of
				// this result trigger the notification.
				return nil
			}
			return err
		}
		if pr := prev.Result(builder, ""); pr != nil && pr.OK {
			c.Debugf("commit broken: %#v\nresult: %#v", com, cr)
			c.Debugf("previous commit ok: %#v\nprevious result:%#v", prev, pr)
			broken = com
		}
	}
	if broken == nil {
		return nil
	}
	r := broken.Result(builder, "")
	if r == nil {
		return fmt.Errorf("finding result for %q: %+v", builder, com)
	}
	return commonNotify(c, broken, builder, r.LogHash)
}

// firstMatch executes the query q and loads the first entity into v.
func firstMatch(c appengine.Context, q *datastore.Query, v interface{}) error {
	t := q.Limit(1).Run(c)
	_, err := t.Next(v)
	if err == datastore.Done {
		err = datastore.ErrNoSuchEntity
	}
	return err
}

var notifyLater = delay.Func("notify", notify)

// notify tries to update the CL for the given Commit with a failure message.
// If it doesn't succeed, it sends a failure email to golang-dev.
func notify(c appengine.Context, com *Commit, builder, logHash string) {
	v := url.Values{"brokebuild": {builder}, "log": {logHash}}
	if !updateCL(c, com, v) {
		// Send a mail notification if the CL can't be found.
		sendFailMail(c, com, builder, logHash)
	}
}

// updateCL tells gobot to update the CL for the given Commit with
// the provided query values.
func updateCL(c appengine.Context, com *Commit, v url.Values) bool {
	cl, err := lookupCL(c, com)
	if err != nil {
		c.Errorf("could not find CL for %v: %v", com.Hash, err)
		return false
	}
	u := fmt.Sprintf("%v?cl=%v&%s", gobotBase, cl, v.Encode())
	r, err := urlfetch.Client(c).Post(u, "text/plain", nil)
	if err != nil {
		c.Errorf("could not update CL %v: %v", cl, err)
		return false
	}
	r.Body.Close()
	if r.StatusCode != http.StatusOK {
		c.Errorf("could not update CL %v: %v", cl, r.Status)
		return false
	}
	return true
}

var clURL = regexp.MustCompile(`https://codereview.appspot.com/([0-9]+)`)

// lookupCL consults code.google.com for the full change description for the
// provided Commit, and returns the relevant CL number.
func lookupCL(c appengine.Context, com *Commit) (string, error) {
	url := "https://code.google.com/p/go/source/detail?r=" + com.Hash
	r, err := urlfetch.Client(c).Get(url)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return "", fmt.Errorf("retrieving %v: %v", url, r.Status)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	m := clURL.FindAllSubmatch(b, -1)
	if m == nil {
		return "", errors.New("no CL URL found on changeset page")
	}
	// Return the last visible codereview URL on the page,
	// in case the change description refers to another CL.
	return string(m[len(m)-1][1]), nil
}

var sendFailMailTmpl = template.Must(template.New("notify.txt").
	Funcs(template.FuncMap(tmplFuncs)).
	ParseFiles("build/notify.txt"))

func init() {
	gob.Register(&Commit{}) // for delay
}

var (
	sendPerfMailLater = delay.Func("sendPerfMail", sendPerfMailFunc)
	sendPerfMailTmpl  = template.Must(
		template.New("perf_notify.txt").
			Funcs(template.FuncMap(tmplFuncs)).
			ParseFiles("build/perf_notify.txt"),
	)
)

func sendPerfFailMail(c appengine.Context, builder string, res *PerfResult) error {
	return datastore.RunInTransaction(c, func(c appengine.Context) error {
		com := &Commit{Hash: res.CommitHash}
		if err := datastore.Get(c, com.Key(c), com); err != nil {
			return err
		}
		logHash := ""
		parsed := res.ParseData()
		for _, data := range parsed[builder] {
			if !data.OK {
				logHash = data.Artifacts["log"]
				break
			}
		}
		if logHash == "" {
			return fmt.Errorf("can not find failed result for commit %v on builder %v", com.Hash, builder)
		}
		return commonNotify(c, com, builder, logHash)
	}, nil)
}

// commonNotify MUST!!! be called from within a transaction inside which
// the provided Commit entity was retrieved from the datastore.
func commonNotify(c appengine.Context, com *Commit, builder, logHash string) error {
	if com.Num == 0 || com.Desc == "" {
		stk := make([]byte, 10000)
		n := runtime.Stack(stk, false)
		stk = stk[:n]
		c.Errorf("refusing to notify with com=%+v\n%s", *com, string(stk))
		return fmt.Errorf("misuse of commonNotify")
	}
	if com.FailNotificationSent {
		return nil
	}
	c.Infof("%s is broken commit; notifying", com.Hash)
	notifyLater.Call(c, com, builder, logHash) // add task to queue
	com.FailNotificationSent = true
	return putCommit(c, com)
}

// sendFailMail sends a mail notification that the build failed on the
// provided commit and builder.
func sendFailMail(c appengine.Context, com *Commit, builder, logHash string) {
	// get Log
	k := datastore.NewKey(c, "Log", logHash, 0, nil)
	l := new(Log)
	if err := datastore.Get(c, k, l); err != nil {
		c.Errorf("finding Log record %v: %v", logHash, err)
		return
	}
	logText, err := l.Text()
	if err != nil {
		c.Errorf("unpacking Log record %v: %v", logHash, err)
		return
	}

	// prepare mail message
	var body bytes.Buffer
	err = sendFailMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "LogHash": logHash, "LogText": logText,
		"Hostname": domain,
	})
	if err != nil {
		c.Errorf("rendering mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("%s broken by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}

type PerfChangeBenchmark struct {
	Name    string
	Metrics []*PerfChangeMetric
}

type PerfChangeMetric struct {
	Name  string
	Old   uint64
	New   uint64
	Delta float64
}

type PerfChangeBenchmarkSlice []*PerfChangeBenchmark

func (l PerfChangeBenchmarkSlice) Len() int      { return len(l) }
func (l PerfChangeBenchmarkSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l PerfChangeBenchmarkSlice) Less(i, j int) bool {
	b1, p1 := splitBench(l[i].Name)
	b2, p2 := splitBench(l[j].Name)
	if b1 != b2 {
		return b1 < b2
	}
	return p1 < p2
}

type PerfChangeMetricSlice []*PerfChangeMetric

func (l PerfChangeMetricSlice) Len() int           { return len(l) }
func (l PerfChangeMetricSlice) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
func (l PerfChangeMetricSlice) Less(i, j int) bool { return l[i].Name < l[j].Name }

func sendPerfMailFunc(c appengine.Context, com *Commit, prevCommitHash, builder string, changes []*PerfChange) {
	// Sort the changes into the right order.
	var benchmarks []*PerfChangeBenchmark
	for _, ch := range changes {
		// Find the benchmark.
		var b *PerfChangeBenchmark
		for _, b1 := range benchmarks {
			if b1.Name == ch.Bench {
				b = b1
				break
			}
		}
		if b == nil {
			b = &PerfChangeBenchmark{Name: ch.Bench}
			benchmarks = append(benchmarks, b)
		}
		b.Metrics = append(b.Metrics, &PerfChangeMetric{Name: ch.Metric, Old: ch.Old, New: ch.New, Delta: ch.Diff})
	}
	for _, b := range benchmarks {
		sort.Sort(PerfChangeMetricSlice(b.Metrics))
	}
	sort.Sort(PerfChangeBenchmarkSlice(benchmarks))

	u := fmt.Sprintf("http://%v/perfdetail?commit=%v&commit0=%v&kind=builder&builder=%v", domain, com.Hash, prevCommitHash, builder)

	// Prepare mail message (without Commit, for updateCL).
	var body bytes.Buffer
	err := sendPerfMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Hostname": domain, "Url": u, "Benchmarks": benchmarks,
	})
	if err != nil {
		c.Errorf("rendering perf mail template: %v", err)
		return
	}

	// First, try to update the CL.
	v := url.Values{"textmsg": {body.String()}}
	if updateCL(c, com, v) {
		return
	}

	// Otherwise, send mail (with Commit, for independent mail message).
	body.Reset()
	err = sendPerfMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "Hostname": domain, "Url": u, "Benchmarks": benchmarks,
	})
	if err != nil {
		c.Errorf("rendering perf mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("Perf changes on %s by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}
<commit_msg>go.tools/dashboard/app: remove redundant transaction<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package build

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"runtime"
	"sort"
	"text/template"

	"appengine"
	"appengine/datastore"
	"appengine/delay"
	"appengine/mail"
	"appengine/urlfetch"
)

const (
	mailFrom   = "builder@golang.org" // use this for sending any mail
	failMailTo = "golang-dev@googlegroups.com"
	domain     = "build.golang.org"
	gobotBase  = "http://research.swtch.com/gobot_codereview"
)

// ignoreFailure is a set of builders that we don't email about because
// they are not yet production-ready.
var ignoreFailure = map[string]bool{
	"dragonfly-386":         true,
	"dragonfly-amd64":       true,
	"netbsd-amd64-bsiegert": true,
	"netbsd-arm-rpi":        true,
	"plan9-amd64-aram":      true,
}

// notifyOnFailure checks whether the supplied Commit or the subsequent
// Commit (if present) breaks the build for this builder.
// If either of those commits break the build an email notification is sent
// from a delayed task. (We use a task because this way the mail won't be
// sent if the enclosing datastore transaction fails.)
//
// This must be run in a datastore transaction, and the provided *Commit must
// have been retrieved from the datastore within that transaction.
func notifyOnFailure(c appengine.Context, com *Commit, builder string) error {
	if ignoreFailure[builder] {
		return nil
	}

	// TODO(adg): implement notifications for packages
	if com.PackagePath != "" {
		return nil
	}

	p := &Package{Path: com.PackagePath}
	var broken *Commit
	cr := com.Result(builder, "")
	if cr == nil {
		return fmt.Errorf("no result for %s/%s", com.Hash, builder)
	}
	q := datastore.NewQuery("Commit").Ancestor(p.Key(c))
	if cr.OK {
		// This commit is OK. Notify if next Commit is broken.
		next := new(Commit)
		q = q.Filter("ParentHash=", com.Hash)
		if err := firstMatch(c, q, next); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// OK at tip, no notification necessary.
				return nil
			}
			return err
		}
		if nr := next.Result(builder, ""); nr != nil && !nr.OK {
			c.Debugf("commit ok: %#v\nresult: %#v", com, cr)
			c.Debugf("next commit broken: %#v\nnext result:%#v", next, nr)
			broken = next
		}
	} else {
		// This commit is broken. Notify if the previous Commit is OK.
		prev := new(Commit)
		q = q.Filter("Hash=", com.ParentHash)
		if err := firstMatch(c, q, prev); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// No previous result, let the backfill of
				// this result trigger the notification.
				return nil
			}
			return err
		}
		if pr := prev.Result(builder, ""); pr != nil && pr.OK {
			c.Debugf("commit broken: %#v\nresult: %#v", com, cr)
			c.Debugf("previous commit ok: %#v\nprevious result:%#v", prev, pr)
			broken = com
		}
	}
	if broken == nil {
		return nil
	}
	r := broken.Result(builder, "")
	if r == nil {
		return fmt.Errorf("finding result for %q: %+v", builder, com)
	}
	return commonNotify(c, broken, builder, r.LogHash)
}

// firstMatch executes the query q and loads the first entity into v.
func firstMatch(c appengine.Context, q *datastore.Query, v interface{}) error {
	t := q.Limit(1).Run(c)
	_, err := t.Next(v)
	if err == datastore.Done {
		err = datastore.ErrNoSuchEntity
	}
	return err
}

var notifyLater = delay.Func("notify", notify)

// notify tries to update the CL for the given Commit with a failure message.
// If it doesn't succeed, it sends a failure email to golang-dev.
func notify(c appengine.Context, com *Commit, builder, logHash string) {
	v := url.Values{"brokebuild": {builder}, "log": {logHash}}
	if !updateCL(c, com, v) {
		// Send a mail notification if the CL can't be found.
		sendFailMail(c, com, builder, logHash)
	}
}

// updateCL tells gobot to update the CL for the given Commit with
// the provided query values.
func updateCL(c appengine.Context, com *Commit, v url.Values) bool {
	cl, err := lookupCL(c, com)
	if err != nil {
		c.Errorf("could not find CL for %v: %v", com.Hash, err)
		return false
	}
	u := fmt.Sprintf("%v?cl=%v&%s", gobotBase, cl, v.Encode())
	r, err := urlfetch.Client(c).Post(u, "text/plain", nil)
	if err != nil {
		c.Errorf("could not update CL %v: %v", cl, err)
		return false
	}
	r.Body.Close()
	if r.StatusCode != http.StatusOK {
		c.Errorf("could not update CL %v: %v", cl, r.Status)
		return false
	}
	return true
}

var clURL = regexp.MustCompile(`https://codereview.appspot.com/([0-9]+)`)

// lookupCL consults code.google.com for the full change description for the
// provided Commit, and returns the relevant CL number.
func lookupCL(c appengine.Context, com *Commit) (string, error) {
	url := "https://code.google.com/p/go/source/detail?r=" + com.Hash
	r, err := urlfetch.Client(c).Get(url)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return "", fmt.Errorf("retrieving %v: %v", url, r.Status)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	m := clURL.FindAllSubmatch(b, -1)
	if m == nil {
		return "", errors.New("no CL URL found on changeset page")
	}
	// Return the last visible codereview URL on the page,
	// in case the change description refers to another CL.
	return string(m[len(m)-1][1]), nil
}

var sendFailMailTmpl = template.Must(template.New("notify.txt").
	Funcs(template.FuncMap(tmplFuncs)).
	ParseFiles("build/notify.txt"))

func init() {
	gob.Register(&Commit{}) // for delay
}

var (
	sendPerfMailLater = delay.Func("sendPerfMail", sendPerfMailFunc)
	sendPerfMailTmpl  = template.Must(
		template.New("perf_notify.txt").
			Funcs(template.FuncMap(tmplFuncs)).
			ParseFiles("build/perf_notify.txt"),
	)
)

// MUST be called from inside a transaction.
func sendPerfFailMail(c appengine.Context, builder string, res *PerfResult) error {
	com := &Commit{Hash: res.CommitHash}
	if err := datastore.Get(c, com.Key(c), com); err != nil {
		return err
	}
	logHash := ""
	parsed := res.ParseData()
	for _, data := range parsed[builder] {
		if !data.OK {
			logHash = data.Artifacts["log"]
			break
		}
	}
	if logHash == "" {
		return fmt.Errorf("can not find failed result for commit %v on builder %v", com.Hash, builder)
	}
	return commonNotify(c, com, builder, logHash)
}

// commonNotify MUST!!! be called from within a transaction inside which
// the provided Commit entity was retrieved from the datastore.
func commonNotify(c appengine.Context, com *Commit, builder, logHash string) error {
	if com.Num == 0 || com.Desc == "" {
		stk := make([]byte, 10000)
		n := runtime.Stack(stk, false)
		stk = stk[:n]
		c.Errorf("refusing to notify with com=%+v\n%s", *com, string(stk))
		return fmt.Errorf("misuse of commonNotify")
	}
	if com.FailNotificationSent {
		return nil
	}
	c.Infof("%s is broken commit; notifying", com.Hash)
	notifyLater.Call(c, com, builder, logHash) // add task to queue
	com.FailNotificationSent = true
	return putCommit(c, com)
}

// sendFailMail sends a mail notification that the build failed on the
// provided commit and builder.
func sendFailMail(c appengine.Context, com *Commit, builder, logHash string) {
	// get Log
	k := datastore.NewKey(c, "Log", logHash, 0, nil)
	l := new(Log)
	if err := datastore.Get(c, k, l); err != nil {
		c.Errorf("finding Log record %v: %v", logHash, err)
		return
	}
	logText, err := l.Text()
	if err != nil {
		c.Errorf("unpacking Log record %v: %v", logHash, err)
		return
	}

	// prepare mail message
	var body bytes.Buffer
	err = sendFailMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "LogHash": logHash, "LogText": logText,
		"Hostname": domain,
	})
	if err != nil {
		c.Errorf("rendering mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("%s broken by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}

type PerfChangeBenchmark struct {
	Name    string
	Metrics []*PerfChangeMetric
}

type PerfChangeMetric struct {
	Name  string
	Old   uint64
	New   uint64
	Delta float64
}

type PerfChangeBenchmarkSlice []*PerfChangeBenchmark

func (l PerfChangeBenchmarkSlice) Len() int      { return len(l) }
func (l PerfChangeBenchmarkSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l PerfChangeBenchmarkSlice) Less(i, j int) bool {
	b1, p1 := splitBench(l[i].Name)
	b2, p2 := splitBench(l[j].Name)
	if b1 != b2 {
		return b1 < b2
	}
	return p1 < p2
}

type PerfChangeMetricSlice []*PerfChangeMetric

func (l PerfChangeMetricSlice) Len() int           { return len(l) }
func (l PerfChangeMetricSlice) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
func (l PerfChangeMetricSlice) Less(i, j int) bool { return l[i].Name < l[j].Name }

func sendPerfMailFunc(c appengine.Context, com *Commit, prevCommitHash, builder string, changes []*PerfChange) {
	// Sort the changes into the right order.
	var benchmarks []*PerfChangeBenchmark
	for _, ch := range changes {
		// Find the benchmark.
		var b *PerfChangeBenchmark
		for _, b1 := range benchmarks {
			if b1.Name == ch.Bench {
				b = b1
				break
			}
		}
		if b == nil {
			b = &PerfChangeBenchmark{Name: ch.Bench}
			benchmarks = append(benchmarks, b)
		}
		b.Metrics = append(b.Metrics, &PerfChangeMetric{Name: ch.Metric, Old: ch.Old, New: ch.New, Delta: ch.Diff})
	}
	for _, b := range benchmarks {
		sort.Sort(PerfChangeMetricSlice(b.Metrics))
	}
	sort.Sort(PerfChangeBenchmarkSlice(benchmarks))

	u := fmt.Sprintf("http://%v/perfdetail?commit=%v&commit0=%v&kind=builder&builder=%v", domain, com.Hash, prevCommitHash, builder)

	// Prepare mail message (without Commit, for updateCL).
	var body bytes.Buffer
	err := sendPerfMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Hostname": domain, "Url": u, "Benchmarks": benchmarks,
	})
	if err != nil {
		c.Errorf("rendering perf mail template: %v", err)
		return
	}

	// First, try to update the CL.
	v := url.Values{"textmsg": {body.String()}}
	if updateCL(c, com, v) {
		return
	}

	// Otherwise, send mail (with Commit, for independent mail message).
	body.Reset()
	err = sendPerfMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "Hostname": domain, "Url": u, "Benchmarks": benchmarks,
	})
	if err != nil {
		c.Errorf("rendering perf mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("Perf changes on %s by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}
<|endoftext|>


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

// +build appengine

package build

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"text/template"

	"appengine"
	"appengine/datastore"
	"appengine/delay"
	"appengine/mail"
	"appengine/urlfetch"
)

const (
	mailFrom   = "builder@golang.org" // use this for sending any mail
	failMailTo = "golang-dev@googlegroups.com"
	domain     = "build.golang.org"
	gobotBase  = "http://research.swtch.com/gobot_codereview"
)

// ignoreFailure is a set of builders that we don't email about because
// they are not yet production-ready.
var ignoreFailure = map[string]bool{
	"dragonfly-386":           true,
	"dragonfly-amd64":         true,
	"netbsd-arm-rpi":          true,
	"solaris-amd64-smartos":   true,
	"solaris-amd64-solaris11": true,
}

// notifyOnFailure checks whether the supplied Commit or the subsequent
// Commit (if present) breaks the build for this builder.
// If either of those commits break the build an email notification is sent
// from a delayed task. (We use a task because this way the mail won't be
// sent if the enclosing datastore transaction fails.)
//
// This must be run in a datastore transaction, and the provided *Commit must
// have been retrieved from the datastore within that transaction.
func notifyOnFailure(c appengine.Context, com *Commit, builder string) error {
	if ignoreFailure[builder] {
		return nil
	}

	// TODO(adg): implement notifications for packages
	if com.PackagePath != "" {
		return nil
	}

	p := &Package{Path: com.PackagePath}
	var broken *Commit
	cr := com.Result(builder, "")
	if cr == nil {
		return fmt.Errorf("no result for %s/%s", com.Hash, builder)
	}
	q := datastore.NewQuery("Commit").Ancestor(p.Key(c))
	if cr.OK {
		// This commit is OK. Notify if next Commit is broken.
		next := new(Commit)
		q = q.Filter("ParentHash=", com.Hash)
		if err := firstMatch(c, q, next); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// OK at tip, no notification necessary.
				return nil
			}
			return err
		}
		if nr := next.Result(builder, ""); nr != nil && !nr.OK {
			c.Debugf("commit ok: %#v\nresult: %#v", com, cr)
			c.Debugf("next commit broken: %#v\nnext result:%#v", next, nr)
			broken = next
		}
	} else {
		// This commit is broken. Notify if the previous Commit is OK.
		prev := new(Commit)
		q = q.Filter("Hash=", com.ParentHash)
		if err := firstMatch(c, q, prev); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// No previous result, let the backfill of
				// this result trigger the notification.
				return nil
			}
			return err
		}
		if pr := prev.Result(builder, ""); pr != nil && pr.OK {
			c.Debugf("commit broken: %#v\nresult: %#v", com, cr)
			c.Debugf("previous commit ok: %#v\nprevious result:%#v", prev, pr)
			broken = com
		}
	}
	var err error
	if broken != nil && !broken.FailNotificationSent {
		c.Infof("%s is broken commit; notifying", broken.Hash)
		notifyLater.Call(c, broken, builder) // add task to queue
		broken.FailNotificationSent = true
		_, err = datastore.Put(c, broken.Key(c), broken)
	}
	return err
}

// firstMatch executes the query q and loads the first entity into v.
func firstMatch(c appengine.Context, q *datastore.Query, v interface{}) error {
	t := q.Limit(1).Run(c)
	_, err := t.Next(v)
	if err == datastore.Done {
		err = datastore.ErrNoSuchEntity
	}
	return err
}

var notifyLater = delay.Func("notify", notify)

// notify tries to update the CL for the given Commit with a failure message.
// If it doesn't succeed, it sends a failure email to golang-dev.
func notify(c appengine.Context, com *Commit, builder string) {
	if !updateCL(c, com, builder) {
		// Send a mail notification if the CL can't be found.
		sendFailMail(c, com, builder)
	}
}

// updateCL updates the CL for the given Commit with a failure message
// for the given builder.
func updateCL(c appengine.Context, com *Commit, builder string) bool {
	cl, err := lookupCL(c, com)
	if err != nil {
		c.Errorf("could not find CL for %v: %v", com.Hash, err)
		return false
	}
	url := fmt.Sprintf("%v?cl=%v&brokebuild=%v", gobotBase, cl, builder)
	r, err := urlfetch.Client(c).Post(url, "text/plain", nil)
	if err != nil {
		c.Errorf("could not update CL %v: %v", cl, err)
		return false
	}
	r.Body.Close()
	if r.StatusCode != http.StatusOK {
		c.Errorf("could not update CL %v: %v", cl, r.Status)
		return false
	}
	return true
}

var clURL = regexp.MustCompile(`https://codereview.appspot.com/([0-9]+)`)

// lookupCL consults code.google.com for the full change description for the
// provided Commit, and returns the relevant CL number.
func lookupCL(c appengine.Context, com *Commit) (string, error) {
	url := "https://code.google.com/p/go/source/detail?r=" + com.Hash
	r, err := urlfetch.Client(c).Get(url)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return "", fmt.Errorf("retrieving %v: %v", url, r.Status)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	m := clURL.FindAllSubmatch(b, -1)
	if m == nil {
		return "", errors.New("no CL URL found on changeset page")
	}
	// Return the last visible codereview URL on the page,
	// in case the change description refers to another CL.
	return string(m[len(m)-1][1]), nil
}

var sendFailMailTmpl = template.Must(template.New("notify.txt").
	Funcs(template.FuncMap(tmplFuncs)).
	ParseFiles("build/notify.txt"))

func init() {
	gob.Register(&Commit{}) // for delay
}

// sendFailMail sends a mail notification that the build failed on the
// provided commit and builder.
func sendFailMail(c appengine.Context, com *Commit, builder string) {
	// TODO(adg): handle packages

	// get Result
	r := com.Result(builder, "")
	if r == nil {
		c.Errorf("finding result for %q: %+v", builder, com)
		return
	}

	// get Log
	k := datastore.NewKey(c, "Log", r.LogHash, 0, nil)
	l := new(Log)
	if err := datastore.Get(c, k, l); err != nil {
		c.Errorf("finding Log record %v: %v", r.LogHash, err)
		return
	}

	// prepare mail message
	var body bytes.Buffer
	err := sendFailMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "Result": r, "Log": l,
		"Hostname": domain,
	})
	if err != nil {
		c.Errorf("rendering mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("%s broken by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}
<commit_msg>go.tools/dashboard/app: send log hash to gobot on build failure<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package build

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"text/template"

	"appengine"
	"appengine/datastore"
	"appengine/delay"
	"appengine/mail"
	"appengine/urlfetch"
)

const (
	mailFrom   = "builder@golang.org" // use this for sending any mail
	failMailTo = "golang-dev@googlegroups.com"
	domain     = "build.golang.org"
	gobotBase  = "http://research.swtch.com/gobot_codereview"
)

// ignoreFailure is a set of builders that we don't email about because
// they are not yet production-ready.
var ignoreFailure = map[string]bool{
	"dragonfly-386":           true,
	"dragonfly-amd64":         true,
	"netbsd-arm-rpi":          true,
	"solaris-amd64-smartos":   true,
	"solaris-amd64-solaris11": true,
}

// notifyOnFailure checks whether the supplied Commit or the subsequent
// Commit (if present) breaks the build for this builder.
// If either of those commits break the build an email notification is sent
// from a delayed task. (We use a task because this way the mail won't be
// sent if the enclosing datastore transaction fails.)
//
// This must be run in a datastore transaction, and the provided *Commit must
// have been retrieved from the datastore within that transaction.
func notifyOnFailure(c appengine.Context, com *Commit, builder string) error {
	if ignoreFailure[builder] {
		return nil
	}

	// TODO(adg): implement notifications for packages
	if com.PackagePath != "" {
		return nil
	}

	p := &Package{Path: com.PackagePath}
	var broken *Commit
	cr := com.Result(builder, "")
	if cr == nil {
		return fmt.Errorf("no result for %s/%s", com.Hash, builder)
	}
	q := datastore.NewQuery("Commit").Ancestor(p.Key(c))
	if cr.OK {
		// This commit is OK. Notify if next Commit is broken.
		next := new(Commit)
		q = q.Filter("ParentHash=", com.Hash)
		if err := firstMatch(c, q, next); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// OK at tip, no notification necessary.
				return nil
			}
			return err
		}
		if nr := next.Result(builder, ""); nr != nil && !nr.OK {
			c.Debugf("commit ok: %#v\nresult: %#v", com, cr)
			c.Debugf("next commit broken: %#v\nnext result:%#v", next, nr)
			broken = next
		}
	} else {
		// This commit is broken. Notify if the previous Commit is OK.
		prev := new(Commit)
		q = q.Filter("Hash=", com.ParentHash)
		if err := firstMatch(c, q, prev); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// No previous result, let the backfill of
				// this result trigger the notification.
				return nil
			}
			return err
		}
		if pr := prev.Result(builder, ""); pr != nil && pr.OK {
			c.Debugf("commit broken: %#v\nresult: %#v", com, cr)
			c.Debugf("previous commit ok: %#v\nprevious result:%#v", prev, pr)
			broken = com
		}
	}
	var err error
	if broken != nil && !broken.FailNotificationSent {
		c.Infof("%s is broken commit; notifying", broken.Hash)
		notifyLater.Call(c, broken, builder) // add task to queue
		broken.FailNotificationSent = true
		_, err = datastore.Put(c, broken.Key(c), broken)
	}
	return err
}

// firstMatch executes the query q and loads the first entity into v.
func firstMatch(c appengine.Context, q *datastore.Query, v interface{}) error {
	t := q.Limit(1).Run(c)
	_, err := t.Next(v)
	if err == datastore.Done {
		err = datastore.ErrNoSuchEntity
	}
	return err
}

var notifyLater = delay.Func("notify", notify)

// notify tries to update the CL for the given Commit with a failure message.
// If it doesn't succeed, it sends a failure email to golang-dev.
func notify(c appengine.Context, com *Commit, builder string) {
	if !updateCL(c, com, builder) {
		// Send a mail notification if the CL can't be found.
		sendFailMail(c, com, builder)
	}
}

// updateCL updates the CL for the given Commit with a failure message
// for the given builder.
func updateCL(c appengine.Context, com *Commit, builder string) bool {
	cl, err := lookupCL(c, com)
	if err != nil {
		c.Errorf("could not find CL for %v: %v", com.Hash, err)
		return false
	}
	res := com.Result(builder, "")
	if res == nil {
		c.Errorf("finding result for %q: %+v", builder, com)
		return false
	}
	url := fmt.Sprintf("%v?cl=%v&brokebuild=%v&log=%v", gobotBase, cl, builder, res.LogHash)
	r, err := urlfetch.Client(c).Post(url, "text/plain", nil)
	if err != nil {
		c.Errorf("could not update CL %v: %v", cl, err)
		return false
	}
	r.Body.Close()
	if r.StatusCode != http.StatusOK {
		c.Errorf("could not update CL %v: %v", cl, r.Status)
		return false
	}
	return true
}

var clURL = regexp.MustCompile(`https://codereview.appspot.com/([0-9]+)`)

// lookupCL consults code.google.com for the full change description for the
// provided Commit, and returns the relevant CL number.
func lookupCL(c appengine.Context, com *Commit) (string, error) {
	url := "https://code.google.com/p/go/source/detail?r=" + com.Hash
	r, err := urlfetch.Client(c).Get(url)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return "", fmt.Errorf("retrieving %v: %v", url, r.Status)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	m := clURL.FindAllSubmatch(b, -1)
	if m == nil {
		return "", errors.New("no CL URL found on changeset page")
	}
	// Return the last visible codereview URL on the page,
	// in case the change description refers to another CL.
	return string(m[len(m)-1][1]), nil
}

var sendFailMailTmpl = template.Must(template.New("notify.txt").
	Funcs(template.FuncMap(tmplFuncs)).
	ParseFiles("build/notify.txt"))

func init() {
	gob.Register(&Commit{}) // for delay
}

// sendFailMail sends a mail notification that the build failed on the
// provided commit and builder.
func sendFailMail(c appengine.Context, com *Commit, builder string) {
	// TODO(adg): handle packages

	// get Result
	r := com.Result(builder, "")
	if r == nil {
		c.Errorf("finding result for %q: %+v", builder, com)
		return
	}

	// get Log
	k := datastore.NewKey(c, "Log", r.LogHash, 0, nil)
	l := new(Log)
	if err := datastore.Get(c, k, l); err != nil {
		c.Errorf("finding Log record %v: %v", r.LogHash, err)
		return
	}

	// prepare mail message
	var body bytes.Buffer
	err := sendFailMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "Result": r, "Log": l,
		"Hostname": domain,
	})
	if err != nil {
		c.Errorf("rendering mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("%s broken by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}
<|endoftext|>


<commit_before>package golog

import (
	"gomock.googlecode.com/hg/gomock"
	"testing"
)

func TestOutput(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	message := &LogMessage{}

	mockLogOuter := NewMockLogOuter(mockCtrl)
	mockLogOuter.EXPECT().Output(message)

	logger := NewLogger(mockLogOuter, 0, nil)
	logger.Log(0, func() *LogMessage { return message })
}

func TestNoOutput(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockLogOuter := NewMockLogOuter(mockCtrl)

	var called bool = false

	logger := NewLogger(mockLogOuter, 1, nil)
	// The message is logged below the filter level.
	logger.Log(0, func() *LogMessage { called = true; return nil })

	if called {
		t.Error("Closure evaluated even though no output produced")
	}
}

func TestFailFunc(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockLogOuter := NewMockLogOuter(mockCtrl)

	var called bool = false

	// TODO(awreece) Our testing fail function violates spec by returning to
	// normal control flow?
	logger := NewLogger(mockLogOuter, 1, func() { called = true })

	logger.FailNow()
	if !called {
		t.Error("Fail function not called!")
	}
}
<commit_msg>TestSetMinLogLevel<commit_after>package golog

import (
	"gomock.googlecode.com/hg/gomock"
	"testing"
)

func TestOutput(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	message := &LogMessage{}

	mockLogOuter := NewMockLogOuter(mockCtrl)
	mockLogOuter.EXPECT().Output(message)

	logger := NewLogger(mockLogOuter, 0, nil)
	logger.Log(0, func() *LogMessage { return message })
}

func TestNoOutput(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockLogOuter := NewMockLogOuter(mockCtrl)

	var called bool = false

	logger := NewLogger(mockLogOuter, 1, nil)
	// The message is logged below the filter level.
	logger.Log(0, func() *LogMessage { called = true; return nil })

	if called {
		t.Error("Closure evaluated even though no output produced")
	}
}

func TestFailFunc(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockLogOuter := NewMockLogOuter(mockCtrl)

	var called bool = false

	// TODO(awreece) Our testing fail function violates spec by returning to
	// normal control flow?
	logger := NewLogger(mockLogOuter, 1, func() { called = true })

	logger.FailNow()
	if !called {
		t.Error("Fail function not called!")
	}
}

func TestSetMinLogLevel(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	message := &LogMessage{}

	mockLogOuter := NewMockLogOuter(mockCtrl)
	mockLogOuter.EXPECT().Output(message)

	logger := NewLogger(mockLogOuter, 0, nil)
	logger.Log(0, func() *LogMessage { return message })

	logger.SetMinLogLevel(1)

	var called bool = false
	logger.Log(0, func() *LogMessage { called = true; return nil })

	if called {
		t.Error("Message logged when log level wrong")
	}
}
<|endoftext|>


<commit_before>package flect

import "strings"

// ToUpper is a convience wrapper for strings.ToUpper
var ToUpper = strings.ToUpper

// ToLower is a convience wrapper for strings.ToLower
var ToLower = strings.ToLower

// ToUpper is a convience wrapper for strings.ToUpper
func (i Ident) ToUpper() Ident {
	return New(ToUpper(i.Original))
}

// ToLower is a convience wrapper for strings.ToLower
func (i Ident) ToLower() Ident {
	return New(ToLower(i.Original))
}
<commit_msg>removed the basic ToLower and ToUpper mappings<commit_after>package flect

import "strings"

// ToUpper is a convience wrapper for strings.ToUpper
func (i Ident) ToUpper() Ident {
	return New(strings.ToUpper(i.Original))
}

// ToLower is a convience wrapper for strings.ToLower
func (i Ident) ToLower() Ident {
	return New(strings.ToLower(i.Original))
}
<|endoftext|>