<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//     https://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 server

import (
	"context"
	"encoding/json"
	"sort"

	"cloud.google.com/go/bigtable"
	pb "github.com/datacommonsorg/mixer/internal/proto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// GetStatSeries implements API for Mixer.GetStatSeries.
// Endpoint: /stat/series
// TODO(shifucun): consilidate and dedup the logic among these similar APIs.
func (s *Server) GetStatSeries(
	ctx context.Context, in *pb.GetStatSeriesRequest) (
	*pb.GetStatSeriesResponse, error) {
	place := in.GetPlace()
	statVar := in.GetStatVar()
	if place == "" {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: place")
	}
	if statVar == "" {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: stat_var")
	}
	filterProp := &ObsProp{
		Mmethod: in.GetMeasurementMethod(),
		Operiod: in.GetObservationPeriod(),
		Unit:    in.GetUnit(),
		Sfactor: in.GetScalingFactor(),
	}

	rowList, keyTokens := buildStatsKey([]string{place}, []string{statVar})
	btData, err := readStats(ctx, s.store, rowList, keyTokens)
	if err != nil {
		return nil, err
	}
	obsTimeSeries := btData[place][statVar]
	if obsTimeSeries == nil {
		return nil, status.Errorf(codes.NotFound,
			"No data for %s, %s", place, statVar)
	}
	series := obsTimeSeries.SourceSeries
	series = filterSeries(series, filterProp)
	sort.Sort(byRank(series))
	resp := pb.GetStatSeriesResponse{Series: map[string]float64{}}
	if len(series) > 0 {
		resp.Series = series[0].Val
	}
	return &resp, nil
}

// GetStatAll implements API for Mixer.GetStatAll.
// Endpoint: /stat/set/series/all
// Endpoint: /stat/all
func (s *Server) GetStatAll(ctx context.Context, in *pb.GetStatAllRequest) (
	*pb.GetStatAllResponse, error) {

	places := in.GetPlaces()
	statVars := in.GetStatVars()
	if len(places) == 0 {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: place")
	}
	if len(statVars) == 0 {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: stat_var")
	}

	// Initialize result with place and stat var dcids.
	result := &pb.GetStatAllResponse{
		PlaceData: make(map[string]*pb.PlaceStat),
	}
	for _, place := range places {
		result.PlaceData[place] = &pb.PlaceStat{
			StatVarData: make(map[string]*pb.ObsTimeSeries),
		}
		for _, statVar := range statVars {
			result.PlaceData[place].StatVarData[statVar] = nil
		}
	}

	rowList, keyTokens := buildStatsKey(places, statVars)
	cacheData, err := readStatsPb(ctx, s.store, rowList, keyTokens)
	if err != nil {
		return nil, err
	}
	for place, placeData := range cacheData {
		for statVar, data := range placeData {
			result.PlaceData[place].StatVarData[statVar] = data
		}
	}
	return result, nil
}

// GetStats implements API for Mixer.GetStats.
// Endpoint: /stat/set/series
// Endpoint: /bulk/stats
func (s *Server) GetStats(ctx context.Context, in *pb.GetStatsRequest) (
	*pb.GetStatsResponse, error) {

	placeDcids := in.GetPlace()
	statsVarDcid := in.GetStatsVar()

	if len(placeDcids) == 0 {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: place")
	}
	if statsVarDcid == "" {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: stat_var")
	}
	filterProp := &ObsProp{
		Mmethod: in.GetMeasurementMethod(),
		Operiod: in.GetObservationPeriod(),
		Unit:    in.GetUnit(),
	}
	var rowList bigtable.RowList
	var keyTokens map[string]*placeStatVar
	rowList, keyTokens = buildStatsKey(placeDcids, []string{statsVarDcid})

	result := map[string]*ObsTimeSeries{}
	cacheData, err := readStats(ctx, s.store, rowList, keyTokens)
	if err != nil {
		return nil, err
	}
	for place := range cacheData {
		result[place] = cacheData[place][statsVarDcid]
	}

	// Fill missing place data and result result
	for _, dcid := range placeDcids {
		if _, ok := result[dcid]; !ok {
			result[dcid] = nil
		}
	}
	for _, obsSeries := range result {
		obsSeries.filterAndRank(filterProp)
	}
	jsonRaw, err := json.Marshal(result)
	if err != nil {
		return nil, err
	}
	return &pb.GetStatsResponse{Payload: string(jsonRaw)}, nil
}

// GetStatSetSeries implements API for Mixer.GetStatSetSeries.
// Endpoint: /v1/stat/set/series
func (s *Server) GetStatSetSeries(ctx context.Context, in *pb.GetStatSetSeriesRequest) (
	*pb.GetStatSetSeriesResponse, error) {
	places := in.GetPlaces()
	statVars := in.GetStatVars()
	if len(places) == 0 {
		return nil, status.Errorf(
			codes.InvalidArgument, "Missing required argument: places")
	}
	if len(statVars) == 0 {
		return nil, status.Errorf(
			codes.InvalidArgument, "Missing required argument: stat_vars")
	}

	// Initialize result with place and stat var dcids.
	result := &pb.GetStatSetSeriesResponse{
		Data: make(map[string]*pb.SeriesMap),
	}
	for _, place := range places {
		result.Data[place] = &pb.SeriesMap{
			Data: make(map[string]*pb.Series),
		}
		for _, statVar := range statVars {
			result.Data[place].Data[statVar] = nil
		}
	}
	// Read data from Cloud Bigtable.
	if s.store.BaseBt() != nil {
		rowList, keyTokens := buildStatsKey(places, statVars)

		// Read data from BigTable.
		cacheData, err := readStatsPb(ctx, s.store, rowList, keyTokens)
		if err != nil {
			return nil, err
		}
		for place, placeData := range cacheData {
			for statVar, data := range placeData {
				if data != nil {
					series, _ := getBestSeries(data, false /* useLatest */)
					result.Data[place].Data[statVar] = series
				}
			}
		}
	}
	// Read data from in-memory cache (private data).
	// When there is data in both BigTable and private data. Prefer private data
	// as this instance is for a private DC.
	if !s.store.MemDb.IsEmpty() {
		for _, place := range places {
			for _, statVar := range statVars {
				series := s.store.MemDb.ReadSeries(statVar, place)
				if len(series) > 0 {
					// TODO: add ranking function for *pb.Series. Now only pick one series
					// from the private import.
					result.Data[place].Data[statVar] = series[0]
				}
			}
		}
	}

	return result, nil
}
<commit_msg>Add logging to GetStats() API (#628)<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//     https://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 server

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"sort"
	"time"

	"cloud.google.com/go/bigtable"
	pb "github.com/datacommonsorg/mixer/internal/proto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func logIfDurationTooLong(t time.Time, thresholdSec float32, msg string) {
	duration := time.Since(t).Seconds()
	if duration > float64(thresholdSec) {
		log.Printf("%f seconds:\n%s", duration, msg)
	}
}

// GetStatSeries implements API for Mixer.GetStatSeries.
// Endpoint: /stat/series
// TODO(shifucun): consilidate and dedup the logic among these similar APIs.
func (s *Server) GetStatSeries(
	ctx context.Context, in *pb.GetStatSeriesRequest) (
	*pb.GetStatSeriesResponse, error) {
	place := in.GetPlace()
	statVar := in.GetStatVar()
	if place == "" {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: place")
	}
	if statVar == "" {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: stat_var")
	}
	filterProp := &ObsProp{
		Mmethod: in.GetMeasurementMethod(),
		Operiod: in.GetObservationPeriod(),
		Unit:    in.GetUnit(),
		Sfactor: in.GetScalingFactor(),
	}

	rowList, keyTokens := buildStatsKey([]string{place}, []string{statVar})
	btData, err := readStats(ctx, s.store, rowList, keyTokens)
	if err != nil {
		return nil, err
	}
	obsTimeSeries := btData[place][statVar]
	if obsTimeSeries == nil {
		return nil, status.Errorf(codes.NotFound,
			"No data for %s, %s", place, statVar)
	}
	series := obsTimeSeries.SourceSeries
	series = filterSeries(series, filterProp)
	sort.Sort(byRank(series))
	resp := pb.GetStatSeriesResponse{Series: map[string]float64{}}
	if len(series) > 0 {
		resp.Series = series[0].Val
	}
	return &resp, nil
}

// GetStatAll implements API for Mixer.GetStatAll.
// Endpoint: /stat/set/series/all
// Endpoint: /stat/all
func (s *Server) GetStatAll(ctx context.Context, in *pb.GetStatAllRequest) (
	*pb.GetStatAllResponse, error) {

	places := in.GetPlaces()
	statVars := in.GetStatVars()
	if len(places) == 0 {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: place")
	}
	if len(statVars) == 0 {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: stat_var")
	}

	// Initialize result with place and stat var dcids.
	result := &pb.GetStatAllResponse{
		PlaceData: make(map[string]*pb.PlaceStat),
	}
	for _, place := range places {
		result.PlaceData[place] = &pb.PlaceStat{
			StatVarData: make(map[string]*pb.ObsTimeSeries),
		}
		for _, statVar := range statVars {
			result.PlaceData[place].StatVarData[statVar] = nil
		}
	}

	rowList, keyTokens := buildStatsKey(places, statVars)
	cacheData, err := readStatsPb(ctx, s.store, rowList, keyTokens)
	if err != nil {
		return nil, err
	}
	for place, placeData := range cacheData {
		for statVar, data := range placeData {
			result.PlaceData[place].StatVarData[statVar] = data
		}
	}
	return result, nil
}

// GetStats implements API for Mixer.GetStats.
// Endpoint: /stat/set/series
// Endpoint: /bulk/stats
func (s *Server) GetStats(ctx context.Context, in *pb.GetStatsRequest) (
	*pb.GetStatsResponse, error) {
	ts := time.Now()
	placeDcids := in.GetPlace()
	statsVarDcid := in.GetStatsVar()
	defer logIfDurationTooLong(
		ts,
		30,
		fmt.Sprintf(
			"GetStats(): placeDcids: %s, statsVarDcid: %v", placeDcids, statsVarDcid),
	)

	if len(placeDcids) == 0 {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: place")
	}
	if statsVarDcid == "" {
		return nil, status.Errorf(codes.InvalidArgument,
			"Missing required argument: stat_var")
	}
	filterProp := &ObsProp{
		Mmethod: in.GetMeasurementMethod(),
		Operiod: in.GetObservationPeriod(),
		Unit:    in.GetUnit(),
	}
	var rowList bigtable.RowList
	var keyTokens map[string]*placeStatVar
	rowList, keyTokens = buildStatsKey(placeDcids, []string{statsVarDcid})

	result := map[string]*ObsTimeSeries{}
	cacheData, err := readStats(ctx, s.store, rowList, keyTokens)
	if err != nil {
		return nil, err
	}
	for place := range cacheData {
		result[place] = cacheData[place][statsVarDcid]
	}

	// Fill missing place data and result result
	for _, dcid := range placeDcids {
		if _, ok := result[dcid]; !ok {
			result[dcid] = nil
		}
	}
	for _, obsSeries := range result {
		obsSeries.filterAndRank(filterProp)
	}
	jsonRaw, err := json.Marshal(result)
	if err != nil {
		return nil, err
	}
	return &pb.GetStatsResponse{Payload: string(jsonRaw)}, nil
}

// GetStatSetSeries implements API for Mixer.GetStatSetSeries.
// Endpoint: /v1/stat/set/series
func (s *Server) GetStatSetSeries(ctx context.Context, in *pb.GetStatSetSeriesRequest) (
	*pb.GetStatSetSeriesResponse, error) {
	places := in.GetPlaces()
	statVars := in.GetStatVars()
	if len(places) == 0 {
		return nil, status.Errorf(
			codes.InvalidArgument, "Missing required argument: places")
	}
	if len(statVars) == 0 {
		return nil, status.Errorf(
			codes.InvalidArgument, "Missing required argument: stat_vars")
	}

	// Initialize result with place and stat var dcids.
	result := &pb.GetStatSetSeriesResponse{
		Data: make(map[string]*pb.SeriesMap),
	}
	for _, place := range places {
		result.Data[place] = &pb.SeriesMap{
			Data: make(map[string]*pb.Series),
		}
		for _, statVar := range statVars {
			result.Data[place].Data[statVar] = nil
		}
	}
	// Read data from Cloud Bigtable.
	if s.store.BaseBt() != nil {
		rowList, keyTokens := buildStatsKey(places, statVars)

		// Read data from BigTable.
		cacheData, err := readStatsPb(ctx, s.store, rowList, keyTokens)
		if err != nil {
			return nil, err
		}
		for place, placeData := range cacheData {
			for statVar, data := range placeData {
				if data != nil {
					series, _ := getBestSeries(data, false /* useLatest */)
					result.Data[place].Data[statVar] = series
				}
			}
		}
	}
	// Read data from in-memory cache (private data).
	// When there is data in both BigTable and private data. Prefer private data
	// as this instance is for a private DC.
	if !s.store.MemDb.IsEmpty() {
		for _, place := range places {
			for _, statVar := range statVars {
				series := s.store.MemDb.ReadSeries(statVar, place)
				if len(series) > 0 {
					// TODO: add ranking function for *pb.Series. Now only pick one series
					// from the private import.
					result.Data[place].Data[statVar] = series[0]
				}
			}
		}
	}

	return result, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 处理路由字符串语法。
package syntax

import (
	"errors"
	"fmt"
	"strings"
)

// Type 表示路由项的类型
type Type int8

// 表示路由项的类型。
// 同时也会被用于表示节点的匹配优先级，值越小优先级越高。
const (
	TypeBasic Type = iota + 10
	TypeRegexp
	TypeNamed
)

// 路由项字符串中的几个特殊字符定义
const (
	NameStart       byte = '{' // 包含命名参数的起始字符
	NameEnd         byte = '}' // 包含命名参数的结束字符
	RegexpSeparator byte = ':' // 名称和正则的分隔符
)

// Parse 将字符串解析成 Segment 对象数组
func Parse(str string) ([]*Segment, error) {
	ss := make([]*Segment, 0, strings.Count(str, string(NameStart)))

	startIndex := 0
	endIndex := -1
	separatorIndex := -1

	nType := TypeBasic
	state := NameEnd // 表示当前的状态

	for i := 0; i < len(str); i++ {
		switch str[i] {
		case NameStart:
			if state != NameEnd {
				return nil, fmt.Errorf("不能嵌套 %s", string(NameStart))
			}
			if endIndex+1 == i {
				return nil, errors.New("两个命名参数不能相邻")
			}

			ss = append(ss, &Segment{
				Value: str[startIndex:i],
				Type:  nType,
			})

			startIndex = i
			state = NameStart
			nType = TypeBasic // 记录了数据之后，重置为 TypeBasic
		case RegexpSeparator:
			if state != NameStart {
				return nil, fmt.Errorf("字符(:)只能出现在 %v %v 中间", string(NameStart), string(NameEnd))
			}

			if i == startIndex+1 {
				return nil, errors.New("未指定参数名称")
			}

			state = RegexpSeparator
			separatorIndex = i
		case NameEnd:
			if state == NameEnd {
				return nil, fmt.Errorf("%v %v 必须成对出现", string(NameStart), string(NameEnd))
			}

			if i == startIndex+1 {
				return nil, errors.New("未指定参数名称")
			}

			if i == separatorIndex+1 {
				return nil, errors.New("未指定的正则表达式")
			}

			if state == NameStart {
				nType = TypeNamed
			} else {
				nType = TypeRegexp
			}

			state = NameEnd
			endIndex = i
		}
	}

	if startIndex < len(str) {
		if state != NameEnd {
			return nil, fmt.Errorf("缺少 %s 字符", string(NameEnd))
		}

		// 最后一个节点是命名节点，则转换成通配符模式
		ss = append(ss, &Segment{
			Value:    str[startIndex:],
			Type:     nType,
			Endpoint: str[len(str)-1] == NameEnd,
		})
	}

	return ss, nil
}

var repl = strings.NewReplacer(string(NameStart), "(?P<",
	string(RegexpSeparator), ">",
	string(NameEnd), ")")

// Regexp 将路由语法转换成正则表达式语法，比如：
//  {id:\\d+}/author => (?P<id>\\d+)
// 需要保证 pattern 的语法正确，此处不再做检测。
func Regexp(pattern string) string {
	return repl.Replace(pattern)
}

// PrefixLen 判断两个字符串之间共同的开始内容的长度，
// 不会从{} 中间被分开，正则表达式与之后的内容也不再分隔。
func PrefixLen(s1, s2 string) int {
	l := len(s1)
	if len(s2) < l {
		l = len(s2)
	}

	startIndex := -10
	endIndex := -10
	state := NameEnd
	for i := 0; i < l; i++ {
		switch s1[i] {
		case NameStart:
			startIndex = i
			state = NameStart
		case NameEnd:
			state = NameEnd
			endIndex = i
		}

		if s1[i] != s2[i] {
			if state != NameEnd { // 不从命名参数中间分隔
				return startIndex
			}
			if endIndex == i { // 命名参数之后必须要有一个或以上的普通字符
				return startIndex
			}
			return i
		}
	} // end for

	if endIndex == l-1 {
		return startIndex
	}

	return l
}
<commit_msg>[internal/tree/syntax] 加大 Type 值之间的间距<commit_after>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 处理路由字符串语法。
package syntax

import (
	"errors"
	"fmt"
	"strings"
)

// Type 表示路由项的类型
type Type int8

// 表示路由项的类型。
// 同时也会被用于表示节点的匹配优先级，值越小优先级越高。
const (
	TypeBasic Type = iota * 10
	TypeRegexp
	TypeNamed
)

// 路由项字符串中的几个特殊字符定义
const (
	NameStart       byte = '{' // 包含命名参数的起始字符
	NameEnd         byte = '}' // 包含命名参数的结束字符
	RegexpSeparator byte = ':' // 名称和正则的分隔符
)

// Parse 将字符串解析成 Segment 对象数组
func Parse(str string) ([]*Segment, error) {
	ss := make([]*Segment, 0, strings.Count(str, string(NameStart)))

	startIndex := 0
	endIndex := -1
	separatorIndex := -1

	nType := TypeBasic
	state := NameEnd // 表示当前的状态

	for i := 0; i < len(str); i++ {
		switch str[i] {
		case NameStart:
			if state != NameEnd {
				return nil, fmt.Errorf("不能嵌套 %s", string(NameStart))
			}
			if endIndex+1 == i {
				return nil, errors.New("两个命名参数不能相邻")
			}

			ss = append(ss, &Segment{
				Value: str[startIndex:i],
				Type:  nType,
			})

			startIndex = i
			state = NameStart
			nType = TypeBasic // 记录了数据之后，重置为 TypeBasic
		case RegexpSeparator:
			if state != NameStart {
				return nil, fmt.Errorf("字符(:)只能出现在 %v %v 中间", string(NameStart), string(NameEnd))
			}

			if i == startIndex+1 {
				return nil, errors.New("未指定参数名称")
			}

			state = RegexpSeparator
			separatorIndex = i
		case NameEnd:
			if state == NameEnd {
				return nil, fmt.Errorf("%v %v 必须成对出现", string(NameStart), string(NameEnd))
			}

			if i == startIndex+1 {
				return nil, errors.New("未指定参数名称")
			}

			if i == separatorIndex+1 {
				return nil, errors.New("未指定的正则表达式")
			}

			if state == NameStart {
				nType = TypeNamed
			} else {
				nType = TypeRegexp
			}

			state = NameEnd
			endIndex = i
		}
	}

	if startIndex < len(str) {
		if state != NameEnd {
			return nil, fmt.Errorf("缺少 %s 字符", string(NameEnd))
		}

		// 最后一个节点是命名节点，则转换成通配符模式
		ss = append(ss, &Segment{
			Value:    str[startIndex:],
			Type:     nType,
			Endpoint: str[len(str)-1] == NameEnd,
		})
	}

	return ss, nil
}

var repl = strings.NewReplacer(string(NameStart), "(?P<",
	string(RegexpSeparator), ">",
	string(NameEnd), ")")

// Regexp 将路由语法转换成正则表达式语法，比如：
//  {id:\\d+}/author => (?P<id>\\d+)
// 需要保证 pattern 的语法正确，此处不再做检测。
func Regexp(pattern string) string {
	return repl.Replace(pattern)
}

// PrefixLen 判断两个字符串之间共同的开始内容的长度，
// 不会从{} 中间被分开，正则表达式与之后的内容也不再分隔。
func PrefixLen(s1, s2 string) int {
	l := len(s1)
	if len(s2) < l {
		l = len(s2)
	}

	startIndex := -10
	endIndex := -10
	state := NameEnd
	for i := 0; i < l; i++ {
		switch s1[i] {
		case NameStart:
			startIndex = i
			state = NameStart
		case NameEnd:
			state = NameEnd
			endIndex = i
		}

		if s1[i] != s2[i] {
			if state != NameEnd { // 不从命名参数中间分隔
				return startIndex
			}
			if endIndex == i { // 命名参数之后必须要有一个或以上的普通字符
				return startIndex
			}
			return i
		}
	} // end for

	if endIndex == l-1 {
		return startIndex
	}

	return l
}
<|endoftext|>


<commit_before><commit_msg>symbol: add Unmarshal function<commit_after><|endoftext|>


<commit_before>package topfeeds

import (
	"csdn"
	"fmt"
	"net/http"
	"oschina"
	"strconv"
)

import "appengine"

type Error string

func (e Error) Error() string {
	return string(e)
}

func init() {
	http.HandleFunc("/topfeeds", handleTopFeeds)
}

func handleTopFeeds(w http.ResponseWriter, r *http.Request) {
	cxt := appengine.NewContext(r)

	//Error-handling anyway.
	defer func() {
		if err := recover(); err != nil {
			cxt.Errorf("handleTopFeeds: %v", err)
			fmt.Fprintf(w, `{"status":%d}`, 300)
		}
	}()

	args := r.URL.Query()
	typ, _ := strconv.Atoi(args["type"][0])  //Which type, 0: oschina, 1: csdn
	page, _ := strconv.Atoi(args["page"][0]) //Which page, if typ is csdn(1), then ignore this.

	site := ""
	siteMobile := ""
	res := ""
	switch typ {
	case 1:
		//Ask csdn:
		chCsdn := make(chan *string)
		go csdn.NewNewsList().Create(cxt, chCsdn)
		res = *(<-chCsdn)

		site = "http://www.csdn.net"
		siteMobile = "http://m.csdn.net"
	default:
		//Ask news-list of www.oschina.net
		chOsc := make(chan *string)
		go oschina.NewNewsList().Create(cxt, page, chOsc)
		res = *(<-chOsc)

		site = "http://www.oschina.net"
		siteMobile = "http://m.oschina.net"
	}

	//Output result of all news-list of all sites.
	s := fmt.Sprintf(`{"status":%d, "site" : "%s", "site_mobile":"%s", "result":%s}`, 200, site, siteMobile, res)
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, s)
}
<commit_msg>Add param test, type and page.<commit_after>package topfeeds

import (
	"csdn"
	"fmt"
	"net/http"
	"oschina"
	"strconv"
)

import "appengine"

type Error string

func (e Error) Error() string {
	return string(e)
}

func init() {
	http.HandleFunc("/topfeeds", handleTopFeeds)
}

func handleTopFeeds(w http.ResponseWriter, r *http.Request) {
	cxt := appengine.NewContext(r)

	//Error-handling anyway.
	defer func() {
		if err := recover(); err != nil {
			cxt.Errorf("handleTopFeeds: %v", err)
			w.Header().Set("Content-Type", "application/json")
			fmt.Fprintf(w, `{"status":%d}`, 300)
		}
	}()

	args := r.URL.Query()

	typ := 0 //Which type, 0: oschina, 1: csdn
	if len(args["type"]) > 0 {
		t, _ := strconv.Atoi(args["type"][0])
		typ = t
	}

	page := 0
	if len(args["page"]) > 0 {
		p, _ := strconv.Atoi(args["page"][0]) //Which page, if typ is csdn(1), then ignore this.
		page = p
	}

	site := ""
	siteMobile := ""
	res := ""
	switch typ {
	case 1:
		//Ask csdn:
		chCsdn := make(chan *string)
		go csdn.NewNewsList().Create(cxt, chCsdn)
		res = *(<-chCsdn)

		site = "http://www.csdn.net"
		siteMobile = "http://m.csdn.net"
	default:
		//Ask news-list of www.oschina.net
		chOsc := make(chan *string)
		go oschina.NewNewsList().Create(cxt, page, chOsc)
		res = *(<-chOsc)

		site = "http://www.oschina.net"
		siteMobile = "http://m.oschina.net"
	}

	//Output result of all news-list of all sites.
	s := fmt.Sprintf(`{"status":%d, "site" : "%s", "site_mobile":"%s", "result":%s}`, 200, site, siteMobile, res)
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, s)
}
<|endoftext|>


<commit_before>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gcron_test

import (
	"testing"
	"time"

	"github.com/gogf/gf/container/garray"
	"github.com/gogf/gf/os/gcron"
	"github.com/gogf/gf/test/gtest"
)

func TestCron_Add_Close(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		_, err1 := cron.Add("* * * * * *", func() {
			//glog.Println("cron1")
			array.Append(1)
		})
		_, err2 := cron.Add("* * * * * *", func() {
			//glog.Println("cron2")
			array.Append(1)
		}, "test")
		_, err3 := cron.Add("* * * * * *", func() {
			array.Append(1)
		}, "test")
		_, err4 := cron.Add("@every 2s", func() {
			//glog.Println("cron3")
			array.Append(1)
		})
		t.Assert(err1, nil)
		t.Assert(err2, nil)
		t.AssertNE(err3, nil)
		t.Assert(err4, nil)
		t.Assert(cron.Size(), 3)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), 2)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), 5)
		cron.Close()
		time.Sleep(1200 * time.Millisecond)
		fixedLength := array.Len()
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), fixedLength)
	})
}

func TestCron_Basic(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		cron.Add("* * * * * *", func() {}, "add")
		//fmt.Println("start", time.Now())
		cron.DelayAdd(time.Second, "* * * * * *", func() {}, "delay_add")
		t.Assert(cron.Size(), 1)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(cron.Size(), 2)

		cron.Remove("delay_add")
		t.Assert(cron.Size(), 1)

		entry1 := cron.Search("add")
		entry2 := cron.Search("test-none")
		t.AssertNE(entry1, nil)
		t.Assert(entry2, nil)
	})
}

func TestCron_Remove(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.Add("* * * * * *", func() {
			array.Append(1)
		}, "add")
		t.Assert(array.Len(), 0)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), 1)

		cron.Remove("add")
		t.Assert(array.Len(), 1)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestCron_AddSingleton(t *testing.T) {
	// un used, can be removed
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		cron.Add("* * * * * *", func() {}, "add")
		cron.DelayAdd(time.Second, "* * * * * *", func() {}, "delay_add")
		t.Assert(cron.Size(), 1)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(cron.Size(), 2)

		cron.Remove("delay_add")
		t.Assert(cron.Size(), 1)

		entry1 := cron.Search("add")
		entry2 := cron.Search("test-none")
		t.AssertNE(entry1, nil)
		t.Assert(entry2, nil)
	})
	// keep this
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddSingleton("* * * * * *", func() {
			array.Append(1)
			time.Sleep(50 * time.Second)
		})
		t.Assert(cron.Size(), 1)
		time.Sleep(3500 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})

}

func TestCron_AddOnce1(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddOnce("* * * * * *", func() {
			array.Append(1)
		})
		cron.AddOnce("* * * * * *", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 2)
		time.Sleep(2500 * time.Millisecond)
		t.Assert(array.Len(), 2)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_AddOnce2(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddOnce("@every 2s", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 1)
		time.Sleep(3000 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_AddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddTimes("* * * * * *", 2, func() {
			array.Append(1)
		})
		time.Sleep(3500 * time.Millisecond)
		t.Assert(array.Len(), 2)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_DelayAdd(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAdd(500*time.Millisecond, "* * * * * *", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(800 * time.Millisecond)
		t.Assert(array.Len(), 0)
		t.Assert(cron.Size(), 1)
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 1)
	})
}

func TestCron_DelayAddSingleton(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAddSingleton(500*time.Millisecond, "* * * * * *", func() {
			array.Append(1)
			time.Sleep(10 * time.Second)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(2200 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 1)
	})
}

func TestCron_DelayAddOnce(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAddOnce(500*time.Millisecond, "* * * * * *", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(800 * time.Millisecond)
		t.Assert(array.Len(), 0)
		t.Assert(cron.Size(), 1)
		time.Sleep(2200 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_DelayAddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAddTimes(500*time.Millisecond, "* * * * * *", 2, func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(800 * time.Millisecond)
		t.Assert(array.Len(), 0)
		t.Assert(cron.Size(), 1)
		time.Sleep(3000 * time.Millisecond)
		t.Assert(array.Len(), 2)
		t.Assert(cron.Size(), 0)
	})
}
<commit_msg>improve unit testing case for package gcron<commit_after>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gcron_test

import (
	"testing"
	"time"

	"github.com/gogf/gf/container/garray"
	"github.com/gogf/gf/os/gcron"
	"github.com/gogf/gf/test/gtest"
)

func TestCron_Add_Close(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		_, err1 := cron.Add("* * * * * *", func() {
			//glog.Println("cron1")
			array.Append(1)
		})
		_, err2 := cron.Add("* * * * * *", func() {
			//glog.Println("cron2")
			array.Append(1)
		}, "test")
		_, err3 := cron.Add("* * * * * *", func() {
			array.Append(1)
		}, "test")
		_, err4 := cron.Add("@every 2s", func() {
			//glog.Println("cron3")
			array.Append(1)
		})
		t.Assert(err1, nil)
		t.Assert(err2, nil)
		t.AssertNE(err3, nil)
		t.Assert(err4, nil)
		t.Assert(cron.Size(), 3)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), 2)
		time.Sleep(1400 * time.Millisecond)
		t.Assert(array.Len(), 5)
		cron.Close()
		time.Sleep(1200 * time.Millisecond)
		fixedLength := array.Len()
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), fixedLength)
	})
}

func TestCron_Basic(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		cron.Add("* * * * * *", func() {}, "add")
		//fmt.Println("start", time.Now())
		cron.DelayAdd(time.Second, "* * * * * *", func() {}, "delay_add")
		t.Assert(cron.Size(), 1)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(cron.Size(), 2)

		cron.Remove("delay_add")
		t.Assert(cron.Size(), 1)

		entry1 := cron.Search("add")
		entry2 := cron.Search("test-none")
		t.AssertNE(entry1, nil)
		t.Assert(entry2, nil)
	})
}

func TestCron_Remove(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.Add("* * * * * *", func() {
			array.Append(1)
		}, "add")
		t.Assert(array.Len(), 0)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), 1)

		cron.Remove("add")
		t.Assert(array.Len(), 1)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestCron_AddSingleton(t *testing.T) {
	// un used, can be removed
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		cron.Add("* * * * * *", func() {}, "add")
		cron.DelayAdd(time.Second, "* * * * * *", func() {}, "delay_add")
		t.Assert(cron.Size(), 1)
		time.Sleep(1200 * time.Millisecond)
		t.Assert(cron.Size(), 2)

		cron.Remove("delay_add")
		t.Assert(cron.Size(), 1)

		entry1 := cron.Search("add")
		entry2 := cron.Search("test-none")
		t.AssertNE(entry1, nil)
		t.Assert(entry2, nil)
	})
	// keep this
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddSingleton("* * * * * *", func() {
			array.Append(1)
			time.Sleep(50 * time.Second)
		})
		t.Assert(cron.Size(), 1)
		time.Sleep(3500 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})

}

func TestCron_AddOnce1(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddOnce("* * * * * *", func() {
			array.Append(1)
		})
		cron.AddOnce("* * * * * *", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 2)
		time.Sleep(2500 * time.Millisecond)
		t.Assert(array.Len(), 2)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_AddOnce2(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddOnce("@every 2s", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 1)
		time.Sleep(3000 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_AddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.AddTimes("* * * * * *", 2, func() {
			array.Append(1)
		})
		time.Sleep(3500 * time.Millisecond)
		t.Assert(array.Len(), 2)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_DelayAdd(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAdd(500*time.Millisecond, "* * * * * *", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(800 * time.Millisecond)
		t.Assert(array.Len(), 0)
		t.Assert(cron.Size(), 1)
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 1)
	})
}

func TestCron_DelayAddSingleton(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAddSingleton(500*time.Millisecond, "* * * * * *", func() {
			array.Append(1)
			time.Sleep(10 * time.Second)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(2200 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 1)
	})
}

func TestCron_DelayAddOnce(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAddOnce(500*time.Millisecond, "* * * * * *", func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(800 * time.Millisecond)
		t.Assert(array.Len(), 0)
		t.Assert(cron.Size(), 1)
		time.Sleep(2200 * time.Millisecond)
		t.Assert(array.Len(), 1)
		t.Assert(cron.Size(), 0)
	})
}

func TestCron_DelayAddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cron := gcron.New()
		array := garray.New(true)
		cron.DelayAddTimes(500*time.Millisecond, "* * * * * *", 2, func() {
			array.Append(1)
		})
		t.Assert(cron.Size(), 0)
		time.Sleep(800 * time.Millisecond)
		t.Assert(array.Len(), 0)
		t.Assert(cron.Size(), 1)
		time.Sleep(3000 * time.Millisecond)
		t.Assert(array.Len(), 2)
		t.Assert(cron.Size(), 0)
	})
}
<|endoftext|>


<commit_before>package json

import (
	"encoding/base64"
	"errors"
	"fmt"
	"strings"

	"github.com/cucumber/messages-go/v15"
)

type TestStep struct {
	TestCaseID      string
	Hook            *messages.Hook
	Pickle          *messages.Pickle
	PickleStep      *messages.Pickle_PickleStep
	Step            *messages.GherkinDocument_Feature_Step
	StepDefinitions []*messages.StepDefinition
	Result          *messages.TestStepFinished_TestStepResult
	Background      *messages.GherkinDocument_Feature_Background
	Attachments     []*messages.Attachment
	ExampleRow      *messages.GherkinDocument_Feature_TableRow
}

func ProcessTestStepFinished(testStepFinished *messages.TestStepFinished, lookup *MessageLookup) (error, *TestStep) {
	testCaseStarted := lookup.LookupTestCaseStarted(testStepFinished.TestCaseStartedId)
	if testCaseStarted == nil {
		return errors.New("No testCaseStarted for " + testStepFinished.TestCaseStartedId), nil
	}

	testCase := lookup.LookupTestCase(testCaseStarted.TestCaseId)
	if testCase == nil {
		return errors.New("No testCase for " + testCaseStarted.TestCaseId), nil
	}

	testStep := lookup.LookupTestStep(testStepFinished.TestStepId)
	if testStep == nil {
		return errors.New("No testStep for " + testStepFinished.TestStepId), nil
	}

	if testStep.HookId != "" {
		hook := lookup.LookupHook(testStep.HookId)
		if hook == nil {
			return errors.New("No hook for " + testStep.HookId), nil
		}

		return nil, &TestStep{
			TestCaseID: testCase.Id,
			Hook:       hook,
			Result:     testStepFinished.TestStepResult,
		}
	}

	pickle := lookup.LookupPickle(testCase.PickleId)
	if pickle == nil {
		return errors.New("No pickle for " + testCase.PickleId), nil
	}

	pickleStep := lookup.LookupPickleStep(testStep.PickleStepId)
	if pickleStep == nil {
		return errors.New("No pickleStep for " + testStep.PickleStepId), nil
	}

	var exampleRow *messages.GherkinDocument_Feature_TableRow
	if len(pickle.AstNodeIds) > 1 {
		exampleRow = lookup.LookupExampleRow(pickle.AstNodeIds[1])
	}

	var background *messages.GherkinDocument_Feature_Background
	scenarioStep := lookup.LookupStep(pickleStep.AstNodeIds[0])
	if scenarioStep != nil {
		background = lookup.LookupBackgroundByStepID(scenarioStep.Id)
	}

	return nil, &TestStep{
		TestCaseID:      testCase.Id,
		Step:            lookup.LookupStep(pickleStep.AstNodeIds[0]),
		Pickle:          pickle,
		PickleStep:      pickleStep,
		ExampleRow:      exampleRow,
		Result:          testStepFinished.TestStepResult,
		StepDefinitions: lookup.LookupStepDefinitions(testStep.StepDefinitionIds),
		Background:      background,
		Attachments:     lookup.LookupAttachments(testStepFinished.TestStepId),
	}
}

func TestStepToJSON(step *TestStep) *jsonStep {
	status := strings.ToLower(step.Result.Status.String())
	duration := uint64(0)
	if step.Result.Duration != nil {
		duration = uint64(messages.DurationToGoDuration(*step.Result.Duration))
	}

	if step.Hook != nil {
		return &jsonStep{
			Match: &jsonStepMatch{
				Location: makeSourceReferenceLocation(step.Hook.SourceReference),
			},
			Result: &jsonStepResult{
				Status:       status,
				ErrorMessage: step.Result.Message,
				Duration:     duration,
			},
			Embeddings: makeEmbeddings(step.Attachments),
		}
	}

	location := makeLocation(step.Pickle.Uri, step.Step.Location.Line)
	if step.ExampleRow != nil {
		location = makeLocation(step.Pickle.Uri, step.ExampleRow.Location.Line)
	}

	if len(step.StepDefinitions) == 1 {
		location = makeSourceReferenceLocation(step.StepDefinitions[0].SourceReference)
	}

	jsonStep := &jsonStep{
		Keyword: step.Step.Keyword,
		Name:    step.PickleStep.Text,
		Line:    step.Step.Location.Line,
		Match: &jsonStepMatch{
			Location: location,
		},
		Result: &jsonStepResult{
			Status:       status,
			ErrorMessage: step.Result.Message,
			Duration:     duration,
		},
		Embeddings: makeEmbeddings(step.Attachments),
		Output:     makeOutput(step.Attachments),
	}

	docString := step.Step.GetDocString()
	if docString != nil {
		jsonStep.DocString = &jsonDocString{
			Line:        docString.Location.Line,
			ContentType: docString.MediaType,
			Value:       docString.Content,
		}
	}

	datatable := step.Step.GetDataTable()
	if datatable != nil {
		jsonStep.Rows = make([]*jsonDatatableRow, len(datatable.GetRows()))
		for rowIndex, row := range datatable.GetRows() {
			cells := make([]string, len(row.Cells))
			for cellIndex, cell := range row.Cells {
				cells[cellIndex] = cell.Value
			}

			jsonStep.Rows[rowIndex] = &jsonDatatableRow{
				Cells: cells,
			}
		}
	}

	return jsonStep
}

func makeEmbeddings(attachments []*messages.Attachment) []*jsonEmbedding {
	embeddableAttachments := filterAttachments(attachments, isEmbeddable)
	jsonEmbeddings := make([]*jsonEmbedding, len(embeddableAttachments))

	for index, attachment := range embeddableAttachments {
		var data string
		if attachment.ContentEncoding == messages.Attachment_BASE64 {
			data = attachment.Body
		} else {
			data = base64.StdEncoding.EncodeToString([]byte(attachment.Body))
		}
		jsonEmbeddings[index] = &jsonEmbedding{
			Data:     data,
			MimeType: attachment.MediaType,
		}
	}

	return jsonEmbeddings
}

func makeOutput(attachments []*messages.Attachment) []string {
	outputAttachments := filterAttachments(attachments, isOutput)
	output := make([]string, len(outputAttachments))

	for index, attachment := range outputAttachments {
		output[index] = attachment.GetBody()
	}

	return output
}

func filterAttachments(attachments []*messages.Attachment, filter func(*messages.Attachment) bool) []*messages.Attachment {
	matches := make([]*messages.Attachment, 0)
	for _, attachment := range attachments {
		if filter(attachment) {
			matches = append(matches, attachment)
		}
	}
	return matches
}

func isEmbeddable(attachment *messages.Attachment) bool {
	return !isOutput(attachment)
}

func isOutput(attachment *messages.Attachment) bool {
	return attachment.GetMediaType() == "text/x.cucumber.log+plain"
}

func makeLocation(file string, line uint32) string {
	return fmt.Sprintf("%s:%d", file, line)
}

func makeJavaMethodLocation(
	javaMethod *messages.SourceReference_JavaMethod,
) string {
	typeList := strings.Join(javaMethod.MethodParameterTypes, ",")
	return fmt.Sprintf("%s.%s(%s)", javaMethod.GetClassName(), javaMethod.GetMethodName(), typeList)
}

func makeJavaStackTraceElementLocation(
	javaStackTraceElement *messages.SourceReference_JavaStackTraceElement,
	location *messages.Location,
) string {
	if location != nil {
		return makeLocation(javaStackTraceElement.GetFileName(), location.GetLine())
	}
	return javaStackTraceElement.GetFileName()
}

func makeSourceReferenceLocation(sourceReference *messages.SourceReference) string {
	javaMethod := sourceReference.GetJavaMethod()
	if javaMethod != nil {
		return makeJavaMethodLocation(javaMethod)
	}

	location := sourceReference.GetLocation()
	javaStackTraceElement := sourceReference.GetJavaStackTraceElement()
	if javaStackTraceElement != nil {
		return makeJavaStackTraceElementLocation(javaStackTraceElement, location)
	}

	return makeLocation(sourceReference.GetUri(), location.GetLine())
}
<commit_msg>Add hook attachments when processing testStepFinished<commit_after>package json

import (
	"encoding/base64"
	"errors"
	"fmt"
	"strings"

	"github.com/cucumber/messages-go/v15"
)

type TestStep struct {
	TestCaseID      string
	Hook            *messages.Hook
	Pickle          *messages.Pickle
	PickleStep      *messages.Pickle_PickleStep
	Step            *messages.GherkinDocument_Feature_Step
	StepDefinitions []*messages.StepDefinition
	Result          *messages.TestStepFinished_TestStepResult
	Background      *messages.GherkinDocument_Feature_Background
	Attachments     []*messages.Attachment
	ExampleRow      *messages.GherkinDocument_Feature_TableRow
}

func ProcessTestStepFinished(testStepFinished *messages.TestStepFinished, lookup *MessageLookup) (error, *TestStep) {
	testCaseStarted := lookup.LookupTestCaseStarted(testStepFinished.TestCaseStartedId)
	if testCaseStarted == nil {
		return errors.New("No testCaseStarted for " + testStepFinished.TestCaseStartedId), nil
	}

	testCase := lookup.LookupTestCase(testCaseStarted.TestCaseId)
	if testCase == nil {
		return errors.New("No testCase for " + testCaseStarted.TestCaseId), nil
	}

	testStep := lookup.LookupTestStep(testStepFinished.TestStepId)
	if testStep == nil {
		return errors.New("No testStep for " + testStepFinished.TestStepId), nil
	}

	if testStep.HookId != "" {
		hook := lookup.LookupHook(testStep.HookId)
		if hook == nil {
			return errors.New("No hook for " + testStep.HookId), nil
		}

		return nil, &TestStep{
			TestCaseID:  testCase.Id,
			Hook:        hook,
			Result:      testStepFinished.TestStepResult,
			Attachments: lookup.LookupAttachments(testStepFinished.TestStepId),
		}
	}

	pickle := lookup.LookupPickle(testCase.PickleId)
	if pickle == nil {
		return errors.New("No pickle for " + testCase.PickleId), nil
	}

	pickleStep := lookup.LookupPickleStep(testStep.PickleStepId)
	if pickleStep == nil {
		return errors.New("No pickleStep for " + testStep.PickleStepId), nil
	}

	var exampleRow *messages.GherkinDocument_Feature_TableRow
	if len(pickle.AstNodeIds) > 1 {
		exampleRow = lookup.LookupExampleRow(pickle.AstNodeIds[1])
	}

	var background *messages.GherkinDocument_Feature_Background
	scenarioStep := lookup.LookupStep(pickleStep.AstNodeIds[0])
	if scenarioStep != nil {
		background = lookup.LookupBackgroundByStepID(scenarioStep.Id)
	}

	return nil, &TestStep{
		TestCaseID:      testCase.Id,
		Step:            lookup.LookupStep(pickleStep.AstNodeIds[0]),
		Pickle:          pickle,
		PickleStep:      pickleStep,
		ExampleRow:      exampleRow,
		Result:          testStepFinished.TestStepResult,
		StepDefinitions: lookup.LookupStepDefinitions(testStep.StepDefinitionIds),
		Background:      background,
		Attachments:     lookup.LookupAttachments(testStepFinished.TestStepId),
	}
}

func TestStepToJSON(step *TestStep) *jsonStep {
	status := strings.ToLower(step.Result.Status.String())
	duration := uint64(0)
	if step.Result.Duration != nil {
		duration = uint64(messages.DurationToGoDuration(*step.Result.Duration))
	}

	if step.Hook != nil {
		return &jsonStep{
			Match: &jsonStepMatch{
				Location: makeSourceReferenceLocation(step.Hook.SourceReference),
			},
			Result: &jsonStepResult{
				Status:       status,
				ErrorMessage: step.Result.Message,
				Duration:     duration,
			},
			Embeddings: makeEmbeddings(step.Attachments),
		}
	}

	location := makeLocation(step.Pickle.Uri, step.Step.Location.Line)
	if step.ExampleRow != nil {
		location = makeLocation(step.Pickle.Uri, step.ExampleRow.Location.Line)
	}

	if len(step.StepDefinitions) == 1 {
		location = makeSourceReferenceLocation(step.StepDefinitions[0].SourceReference)
	}

	jsonStep := &jsonStep{
		Keyword: step.Step.Keyword,
		Name:    step.PickleStep.Text,
		Line:    step.Step.Location.Line,
		Match: &jsonStepMatch{
			Location: location,
		},
		Result: &jsonStepResult{
			Status:       status,
			ErrorMessage: step.Result.Message,
			Duration:     duration,
		},
		Embeddings: makeEmbeddings(step.Attachments),
		Output:     makeOutput(step.Attachments),
	}

	docString := step.Step.GetDocString()
	if docString != nil {
		jsonStep.DocString = &jsonDocString{
			Line:        docString.Location.Line,
			ContentType: docString.MediaType,
			Value:       docString.Content,
		}
	}

	datatable := step.Step.GetDataTable()
	if datatable != nil {
		jsonStep.Rows = make([]*jsonDatatableRow, len(datatable.GetRows()))
		for rowIndex, row := range datatable.GetRows() {
			cells := make([]string, len(row.Cells))
			for cellIndex, cell := range row.Cells {
				cells[cellIndex] = cell.Value
			}

			jsonStep.Rows[rowIndex] = &jsonDatatableRow{
				Cells: cells,
			}
		}
	}

	return jsonStep
}

func makeEmbeddings(attachments []*messages.Attachment) []*jsonEmbedding {
	embeddableAttachments := filterAttachments(attachments, isEmbeddable)
	jsonEmbeddings := make([]*jsonEmbedding, len(embeddableAttachments))

	for index, attachment := range embeddableAttachments {
		var data string
		if attachment.ContentEncoding == messages.Attachment_BASE64 {
			data = attachment.Body
		} else {
			data = base64.StdEncoding.EncodeToString([]byte(attachment.Body))
		}
		jsonEmbeddings[index] = &jsonEmbedding{
			Data:     data,
			MimeType: attachment.MediaType,
		}
	}

	return jsonEmbeddings
}

func makeOutput(attachments []*messages.Attachment) []string {
	outputAttachments := filterAttachments(attachments, isOutput)
	output := make([]string, len(outputAttachments))

	for index, attachment := range outputAttachments {
		output[index] = attachment.GetBody()
	}

	return output
}

func filterAttachments(attachments []*messages.Attachment, filter func(*messages.Attachment) bool) []*messages.Attachment {
	matches := make([]*messages.Attachment, 0)
	for _, attachment := range attachments {
		if filter(attachment) {
			matches = append(matches, attachment)
		}
	}
	return matches
}

func isEmbeddable(attachment *messages.Attachment) bool {
	return !isOutput(attachment)
}

func isOutput(attachment *messages.Attachment) bool {
	return attachment.GetMediaType() == "text/x.cucumber.log+plain"
}

func makeLocation(file string, line uint32) string {
	return fmt.Sprintf("%s:%d", file, line)
}

func makeJavaMethodLocation(
	javaMethod *messages.SourceReference_JavaMethod,
) string {
	typeList := strings.Join(javaMethod.MethodParameterTypes, ",")
	return fmt.Sprintf("%s.%s(%s)", javaMethod.GetClassName(), javaMethod.GetMethodName(), typeList)
}

func makeJavaStackTraceElementLocation(
	javaStackTraceElement *messages.SourceReference_JavaStackTraceElement,
	location *messages.Location,
) string {
	if location != nil {
		return makeLocation(javaStackTraceElement.GetFileName(), location.GetLine())
	}
	return javaStackTraceElement.GetFileName()
}

func makeSourceReferenceLocation(sourceReference *messages.SourceReference) string {
	javaMethod := sourceReference.GetJavaMethod()
	if javaMethod != nil {
		return makeJavaMethodLocation(javaMethod)
	}

	location := sourceReference.GetLocation()
	javaStackTraceElement := sourceReference.GetJavaStackTraceElement()
	if javaStackTraceElement != nil {
		return makeJavaStackTraceElementLocation(javaStackTraceElement, location)
	}

	return makeLocation(sourceReference.GetUri(), location.GetLine())
}
<|endoftext|>


<commit_before>package gceimages

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"text/tabwriter"
	"time"

	"github.com/fatih/color"
	"github.com/fatih/images/command/loader"
	"github.com/mitchellh/go-homedir"
	"github.com/shiena/ansicolor"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	compute "google.golang.org/api/compute/v1"
)

// GceImages is responsible of managing GCE images
type GceImages struct {
	// just so we can use the Env and TOML loader more efficiently with out
	// any complex hacks
	Gce struct {
		ProjectID   string `toml:"project_id" json:"project_id"`
		AccountFile string `toml:"account_file" json:"account_file"`
	}

	svc    *compute.ImagesService
	images *compute.ImageList
}

// New returns a new instance of GceImages
func New(args []string) (*GceImages, error) {
	cfg := new(GceImages)
	err := loader.Load(cfg, args)
	if err != nil {
		return nil, err
	}

	if cfg.Gce.ProjectID == "" {
		return nil, errors.New("ProjectID is not set. Please check your configuration.")
	}

	// increase the timeout. Also we need to pass the client with the context itself
	timeout := time.Second * 30
	ctx := context.WithValue(oauth2.NoContext, oauth2.HTTPClient, &http.Client{
		Transport: &http.Transport{TLSHandshakeTimeout: timeout},
		Timeout:   timeout,
	})

	var client *http.Client

	// allowed scopes
	scopes := []string{compute.ComputeScope}

	// Recommended way is explicit passing of credentials json which can be
	// downloaded from console.developers.google under APIs & Auth/Credentials
	// section
	if cfg.Gce.AccountFile != "" {
		// expand shell meta character
		path, err := homedir.Expand(cfg.Gce.AccountFile)
		if err != nil {
			return nil, err
		}

		jsonContent, err := ioutil.ReadFile(path)
		if err != nil {
			return nil, err
		}

		jtwConfig, err := google.JWTConfigFromJSON(jsonContent, scopes...)
		if err != nil {
			return nil, err
		}

		client = jtwConfig.Client(ctx)
	} else {
		// Look for application default credentials, for more details, see:
		// https://developers.google.com/accounts/docs/application-default-credentials
		client, err = google.DefaultClient(ctx, scopes...)
		if err != nil {
			return nil, err
		}
	}

	svc, err := compute.New(client)
	if err != nil {
		return nil, err
	}

	cfg.svc = compute.NewImagesService(svc)
	return cfg, nil
}

// Fetch fetches the given images and stores them internally. Call Print()
// method to output them.
func (g *GceImages) Fetch(args []string) error {
	var err error
	g.images, err = g.svc.List(g.Gce.ProjectID).Do()
	return err
}

// Print prints the stored images to standard output.
func (g *GceImages) Print() {
	if len(g.images.Items) == 0 {
		fmt.Fprintln(os.Stderr, "no images found")
		return
	}

	green := color.New(color.FgGreen).SprintfFunc()

	w := new(tabwriter.Writer)
	w.Init(ansicolor.NewAnsiColorWriter(os.Stdout), 10, 8, 0, '\t', 0)
	defer w.Flush()

	imageDesc := "image"
	if len(g.images.Items) > 1 {
		imageDesc = "images"
	}

	fmt.Fprintln(w, green("GCE (%d %s):", len(g.images.Items), imageDesc))
	fmt.Fprintln(w, "    Name\tID\tStatus\tType\tDeprecated\tCreation Timestamp")

	for i, image := range g.images.Items {
		deprecatedState := ""
		if image.Deprecated != nil {
			deprecatedState = image.Deprecated.State
		}

		fmt.Fprintf(w, "[%d] %s (%s)\t%d\t%s\t%s (%d)\t%s\t%s\n",
			i+1, image.Name, image.Description, image.Id,
			image.Status, image.SourceType, image.DiskSizeGb,
			deprecatedState, image.CreationTimestamp,
		)
	}

}

// Help prints the help message for the given command
func (g *GceImages) Help(command string) string {
	var help string
	switch command {
	case "delete":
		help = newDeleteFlags().helpMsg
	case "modify":
		help = newModifyFlags().helpMsg
	// case "copy":
	// 	help = newCopyFlags().helpMsg
	case "list":
		help = `Usage: images list --provider gce [options]

 List images

Options:
	`
	default:
		return "no help found for command " + command
	}

	global := `
  -project-id      "..."              Project Id (env: IMAGES_GCE_PROJECT_ID)
  -account-file    "..."              Account file (env: IMAGES_GCE_ACCOUNT_FILE)
`

	help += global
	return help
}
<commit_msg>gce: remove copy placeholder<commit_after>package gceimages

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"text/tabwriter"
	"time"

	"github.com/fatih/color"
	"github.com/fatih/images/command/loader"
	"github.com/mitchellh/go-homedir"
	"github.com/shiena/ansicolor"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	compute "google.golang.org/api/compute/v1"
)

// GceImages is responsible of managing GCE images
type GceImages struct {
	// just so we can use the Env and TOML loader more efficiently with out
	// any complex hacks
	Gce struct {
		ProjectID   string `toml:"project_id" json:"project_id"`
		AccountFile string `toml:"account_file" json:"account_file"`
	}

	svc    *compute.ImagesService
	images *compute.ImageList
}

// New returns a new instance of GceImages
func New(args []string) (*GceImages, error) {
	cfg := new(GceImages)
	err := loader.Load(cfg, args)
	if err != nil {
		return nil, err
	}

	if cfg.Gce.ProjectID == "" {
		return nil, errors.New("ProjectID is not set. Please check your configuration.")
	}

	// increase the timeout. Also we need to pass the client with the context itself
	timeout := time.Second * 30
	ctx := context.WithValue(oauth2.NoContext, oauth2.HTTPClient, &http.Client{
		Transport: &http.Transport{TLSHandshakeTimeout: timeout},
		Timeout:   timeout,
	})

	var client *http.Client

	// allowed scopes
	scopes := []string{compute.ComputeScope}

	// Recommended way is explicit passing of credentials json which can be
	// downloaded from console.developers.google under APIs & Auth/Credentials
	// section
	if cfg.Gce.AccountFile != "" {
		// expand shell meta character
		path, err := homedir.Expand(cfg.Gce.AccountFile)
		if err != nil {
			return nil, err
		}

		jsonContent, err := ioutil.ReadFile(path)
		if err != nil {
			return nil, err
		}

		jtwConfig, err := google.JWTConfigFromJSON(jsonContent, scopes...)
		if err != nil {
			return nil, err
		}

		client = jtwConfig.Client(ctx)
	} else {
		// Look for application default credentials, for more details, see:
		// https://developers.google.com/accounts/docs/application-default-credentials
		client, err = google.DefaultClient(ctx, scopes...)
		if err != nil {
			return nil, err
		}
	}

	svc, err := compute.New(client)
	if err != nil {
		return nil, err
	}

	cfg.svc = compute.NewImagesService(svc)
	return cfg, nil
}

// Fetch fetches the given images and stores them internally. Call Print()
// method to output them.
func (g *GceImages) Fetch(args []string) error {
	var err error
	g.images, err = g.svc.List(g.Gce.ProjectID).Do()
	return err
}

// Print prints the stored images to standard output.
func (g *GceImages) Print() {
	if len(g.images.Items) == 0 {
		fmt.Fprintln(os.Stderr, "no images found")
		return
	}

	green := color.New(color.FgGreen).SprintfFunc()

	w := new(tabwriter.Writer)
	w.Init(ansicolor.NewAnsiColorWriter(os.Stdout), 10, 8, 0, '\t', 0)
	defer w.Flush()

	imageDesc := "image"
	if len(g.images.Items) > 1 {
		imageDesc = "images"
	}

	fmt.Fprintln(w, green("GCE (%d %s):", len(g.images.Items), imageDesc))
	fmt.Fprintln(w, "    Name\tID\tStatus\tType\tDeprecated\tCreation Timestamp")

	for i, image := range g.images.Items {
		deprecatedState := ""
		if image.Deprecated != nil {
			deprecatedState = image.Deprecated.State
		}

		fmt.Fprintf(w, "[%d] %s (%s)\t%d\t%s\t%s (%d)\t%s\t%s\n",
			i+1, image.Name, image.Description, image.Id,
			image.Status, image.SourceType, image.DiskSizeGb,
			deprecatedState, image.CreationTimestamp,
		)
	}

}

// Help prints the help message for the given command
func (g *GceImages) Help(command string) string {
	var help string
	switch command {
	case "delete":
		help = newDeleteFlags().helpMsg
	case "modify":
		help = newModifyFlags().helpMsg
	case "list":
		help = `Usage: images list --provider gce [options]

 List images

Options:
	`
	default:
		return "no help found for command " + command
	}

	global := `
  -project-id      "..."              Project Id (env: IMAGES_GCE_PROJECT_ID)
  -account-file    "..."              Account file (env: IMAGES_GCE_ACCOUNT_FILE)
`

	help += global
	return help
}
<|endoftext|>


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

package datadog

import (
	"context"
	"fmt"

	datadogV2 "github.com/DataDog/datadog-api-client-go/api/v2/datadog"

	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
)

var (
	// UserAllowEmptyValues ...
	UserAllowEmptyValues = []string{}
)

// UserGenerator ...
type UserGenerator struct {
	DatadogService
}

func (g *UserGenerator) createResources(users []datadogV2.User) []terraformutils.Resource {
	resources := []terraformutils.Resource{}
	for _, user := range users {
		relations := user.GetRelationships()
		roles := relations.GetRoles()
		// If no roles are present, we can assume user was created via the V1 API
		// Hence, import the user via their handle
		if len(roles.GetData()) == 0 {
			attr := user.GetAttributes()
			resources = append(resources, g.createResource(attr.GetHandle()))
			continue
		}

		resources = append(resources, g.createResource(user.GetId()))
	}
	return resources
}

func (g *UserGenerator) createResource(userID string) terraformutils.Resource {
	return terraformutils.NewSimpleResource(
		userID,
		fmt.Sprintf("user_%s", userID),
		"datadog_user",
		"datadog",
		UserAllowEmptyValues,
	)
}

// InitResources Generate TerraformResources from Datadog API,
// from each user create 1 TerraformResource.
// Need User ID as ID for terraform resource
func (g *UserGenerator) InitResources() error {
	var users []datadogV2.User

	datadogClientV2 := g.Args["datadogClientV2"].(*datadogV2.APIClient)
	authV2 := g.Args["authV2"].(context.Context)

	pageSize := int64(1000)
	pageNumber := int64(0)
	remaining := int64(1)

	for remaining > int64(0) {
		resp, _, err := datadogClientV2.UsersApi.ListUsers(authV2).PageSize(pageSize).PageNumber(pageNumber).Execute()
		if err != nil {
			return err
		}
		users = append(users, resp.GetData()...)

		remaining = resp.Meta.Page.GetTotalCount() - pageSize*pageNumber
		pageNumber++
	}

	g.Resources = g.createResources(users)
	return nil
}
<commit_msg>start at pageCount 1 when calculating remaining<commit_after>// Copyright 2018 The Terraformer Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package datadog

import (
	"context"
	"fmt"
	datadogV2 "github.com/DataDog/datadog-api-client-go/api/v2/datadog"

	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
)

var (
	// UserAllowEmptyValues ...
	UserAllowEmptyValues = []string{}
)

// UserGenerator ...
type UserGenerator struct {
	DatadogService
}

func (g *UserGenerator) createResources(users []datadogV2.User) []terraformutils.Resource {
	resources := []terraformutils.Resource{}
	for _, user := range users {
		relations := user.GetRelationships()
		roles := relations.GetRoles()
		// If no roles are present, we can assume user was created via the V1 API
		// Hence, import the user via their handle
		if len(roles.GetData()) == 0 {
			attr := user.GetAttributes()
			resources = append(resources, g.createResource(attr.GetHandle()))
			continue
		}

		resources = append(resources, g.createResource(user.GetId()))
	}
	return resources
}

func (g *UserGenerator) createResource(userID string) terraformutils.Resource {
	return terraformutils.NewSimpleResource(
		userID,
		fmt.Sprintf("user_%s", userID),
		"datadog_user",
		"datadog",
		UserAllowEmptyValues,
	)
}

// InitResources Generate TerraformResources from Datadog API,
// from each user create 1 TerraformResource.
// Need User ID as ID for terraform resource
func (g *UserGenerator) InitResources() error {
	var users []datadogV2.User

	datadogClientV2 := g.Args["datadogClientV2"].(*datadogV2.APIClient)
	authV2 := g.Args["authV2"].(context.Context)

	pageSize := int64(1000)
	pageNumber := int64(0)
	remaining := int64(1)

	for remaining > int64(0) {
		resp, _, err := datadogClientV2.UsersApi.ListUsers(authV2).PageSize(pageSize).PageNumber(pageNumber).Execute()
		if err != nil {
			return err
		}
		users = append(users, resp.GetData()...)

		remaining = resp.Meta.Page.GetTotalCount() - pageSize*(pageNumber+1)
		pageNumber++
	}

	g.Resources = g.createResources(users)
	return nil
}
<|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.

// HTTP reverse proxy handler

package http

import (
	"io"
	"log"
	"net"
	"strings"
)

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*Request)

	// The Transport used to perform proxy requests.
	// If nil, DefaultTransport is used.
	Transport RoundTripper
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
func NewSingleHostReverseProxy(target *URL) *ReverseProxy {
	director := func(req *Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if q := req.URL.RawQuery; q != "" {
			req.URL.RawPath = req.URL.Path + "?" + q
		} else {
			req.URL.RawPath = req.URL.Path
		}
		req.URL.RawQuery = target.RawQuery
	}
	return &ReverseProxy{Director: director}
}

func (p *ReverseProxy) ServeHTTP(rw ResponseWriter, req *Request) {
	transport := p.Transport
	if transport == nil {
		transport = DefaultTransport
	}

	outreq := new(Request)
	*outreq = *req // includes shallow copies of maps, but okay

	p.Director(outreq)
	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		outreq.Header.Set("X-Forwarded-For", clientIp)
	}

	res, err := transport.RoundTrip(outreq)
	if err != nil {
		log.Printf("http: proxy error: %v", err)
		rw.WriteHeader(StatusInternalServerError)
		return
	}

	hdr := rw.Header()
	for k, vv := range res.Header {
		for _, v := range vv {
			hdr.Add(k, v)
		}
	}

	rw.WriteHeader(res.StatusCode)

	if res.Body != nil {
		io.Copy(rw, res.Body)
	}
}
<commit_msg>http: support for periodic flushing in ReverseProxy<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.

// HTTP reverse proxy handler

package http

import (
	"io"
	"log"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*Request)

	// The Transport used to perform proxy requests.
	// If nil, DefaultTransport is used.
	Transport RoundTripper

	// FlushInterval specifies the flush interval, in
	// nanoseconds, to flush to the client while
	// coping the response body.
	// If zero, no periodic flushing is done.
	FlushInterval int64
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
func NewSingleHostReverseProxy(target *URL) *ReverseProxy {
	director := func(req *Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if q := req.URL.RawQuery; q != "" {
			req.URL.RawPath = req.URL.Path + "?" + q
		} else {
			req.URL.RawPath = req.URL.Path
		}
		req.URL.RawQuery = target.RawQuery
	}
	return &ReverseProxy{Director: director}
}

func (p *ReverseProxy) ServeHTTP(rw ResponseWriter, req *Request) {
	transport := p.Transport
	if transport == nil {
		transport = DefaultTransport
	}

	outreq := new(Request)
	*outreq = *req // includes shallow copies of maps, but okay

	p.Director(outreq)
	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		outreq.Header.Set("X-Forwarded-For", clientIp)
	}

	res, err := transport.RoundTrip(outreq)
	if err != nil {
		log.Printf("http: proxy error: %v", err)
		rw.WriteHeader(StatusInternalServerError)
		return
	}

	hdr := rw.Header()
	for k, vv := range res.Header {
		for _, v := range vv {
			hdr.Add(k, v)
		}
	}

	rw.WriteHeader(res.StatusCode)

	if res.Body != nil {
		var dst io.Writer = rw
		if p.FlushInterval != 0 {
			if wf, ok := rw.(writeFlusher); ok {
				dst = &maxLatencyWriter{dst: wf, latency: p.FlushInterval}
			}
		}
		io.Copy(dst, res.Body)
	}
}

type writeFlusher interface {
	io.Writer
	Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency int64 // nanos

	lk   sync.Mutex // protects init of done, as well Write + Flush
	done chan bool
}

func (m *maxLatencyWriter) Write(p []byte) (n int, err os.Error) {
	m.lk.Lock()
	defer m.lk.Unlock()
	if m.done == nil {
		m.done = make(chan bool)
		go m.flushLoop()
	}
	n, err = m.dst.Write(p)
	if err != nil {
		m.done <- true
	}
	return
}

func (m *maxLatencyWriter) flushLoop() {
	t := time.NewTicker(m.latency)
	defer t.Stop()
	for {
		select {
		case <-t.C:
			m.lk.Lock()
			m.dst.Flush()
			m.lk.Unlock()
		case <-m.done:
			return
		}
	}
	panic("unreached")
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

package strconv_test

import (
	"runtime"
	. "strconv"
	"testing"
)

type itob64Test struct {
	in   int64
	base int
	out  string
}

var itob64tests = []itob64Test{
	{0, 10, "0"},
	{1, 10, "1"},
	{-1, 10, "-1"},
	{12345678, 10, "12345678"},
	{-987654321, 10, "-987654321"},
	{1<<31 - 1, 10, "2147483647"},
	{-1<<31 + 1, 10, "-2147483647"},
	{1 << 31, 10, "2147483648"},
	{-1 << 31, 10, "-2147483648"},
	{1<<31 + 1, 10, "2147483649"},
	{-1<<31 - 1, 10, "-2147483649"},
	{1<<32 - 1, 10, "4294967295"},
	{-1<<32 + 1, 10, "-4294967295"},
	{1 << 32, 10, "4294967296"},
	{-1 << 32, 10, "-4294967296"},
	{1<<32 + 1, 10, "4294967297"},
	{-1<<32 - 1, 10, "-4294967297"},
	{1 << 50, 10, "1125899906842624"},
	{1<<63 - 1, 10, "9223372036854775807"},
	{-1<<63 + 1, 10, "-9223372036854775807"},
	{-1 << 63, 10, "-9223372036854775808"},

	{0, 2, "0"},
	{10, 2, "1010"},
	{-1, 2, "-1"},
	{1 << 15, 2, "1000000000000000"},

	{-8, 8, "-10"},
	{057635436545, 8, "57635436545"},
	{1 << 24, 8, "100000000"},

	{16, 16, "10"},
	{-0x123456789abcdef, 16, "-123456789abcdef"},
	{1<<63 - 1, 16, "7fffffffffffffff"},
	{1<<63 - 1, 2, "111111111111111111111111111111111111111111111111111111111111111"},

	{16, 17, "g"},
	{25, 25, "10"},
	{(((((17*35+24)*35+21)*35+34)*35+12)*35+24)*35 + 32, 35, "holycow"},
	{(((((17*36+24)*36+21)*36+34)*36+12)*36+24)*36 + 32, 36, "holycow"},
}

func TestItoa(t *testing.T) {
	for _, test := range itob64tests {
		s := FormatInt(test.in, test.base)
		if s != test.out {
			t.Errorf("FormatInt(%v, %v) = %v want %v",
				test.in, test.base, s, test.out)
		}
		x := AppendInt([]byte("abc"), test.in, test.base)
		if string(x) != "abc"+test.out {
			t.Errorf("AppendInt(%q, %v, %v) = %q want %v",
				"abc", test.in, test.base, x, test.out)
		}

		if test.in >= 0 {
			s := FormatUint(uint64(test.in), test.base)
			if s != test.out {
				t.Errorf("FormatUint(%v, %v) = %v want %v",
					test.in, test.base, s, test.out)
			}
			x := AppendUint(nil, uint64(test.in), test.base)
			if string(x) != test.out {
				t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
					"abc", uint64(test.in), test.base, x, test.out)
			}
		}

		if test.base == 10 && int64(int(test.in)) == test.in {
			s := Itoa(int(test.in))
			if s != test.out {
				t.Errorf("Itoa(%v) = %v want %v",
					test.in, s, test.out)
			}
		}
	}
}

type uitob64Test struct {
	in   uint64
	base int
	out  string
}

var uitob64tests = []uitob64Test{
	{1<<63 - 1, 10, "9223372036854775807"},
	{1 << 63, 10, "9223372036854775808"},
	{1<<63 + 1, 10, "9223372036854775809"},
	{1<<64 - 2, 10, "18446744073709551614"},
	{1<<64 - 1, 10, "18446744073709551615"},
	{1<<64 - 1, 2, "1111111111111111111111111111111111111111111111111111111111111111"},
}

func TestUitoa(t *testing.T) {
	for _, test := range uitob64tests {
		s := FormatUint(test.in, test.base)
		if s != test.out {
			t.Errorf("FormatUint(%v, %v) = %v want %v",
				test.in, test.base, s, test.out)
		}
		x := AppendUint([]byte("abc"), test.in, test.base)
		if string(x) != "abc"+test.out {
			t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
				"abc", test.in, test.base, x, test.out)
		}

	}
}

func numAllocations(f func()) int {
	memstats := new(runtime.MemStats)
	runtime.ReadMemStats(memstats)
	n0 := memstats.Mallocs
	f()
	runtime.ReadMemStats(memstats)
	return int(memstats.Mallocs - n0)
}

var globalBuf [64]byte

func TestAppendUintDoesntAllocate(t *testing.T) {
	n := numAllocations(func() {
		var buf [64]byte
		AppendInt(buf[:0], 123, 10)
	})
	want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better
	if n != want {
		t.Errorf("with local buffer, did %d allocations, want %d", n, want)
	}
	n = numAllocations(func() {
		AppendInt(globalBuf[:0], 123, 10)
	})
	if n != 0 {
		t.Errorf("with reused buffer, did %d allocations, want 0", n)
	}
}

func BenchmarkFormatInt(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, test := range itob64tests {
			FormatInt(test.in, test.base)
		}
	}
}

func BenchmarkAppendInt(b *testing.B) {
	dst := make([]byte, 0, 30)
	for i := 0; i < b.N; i++ {
		for _, test := range itob64tests {
			AppendInt(dst, test.in, test.base)
		}
	}
}

func BenchmarkFormatUint(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, test := range uitob64tests {
			FormatUint(test.in, test.base)
		}
	}
}

func BenchmarkAppendUint(b *testing.B) {
	dst := make([]byte, 0, 30)
	for i := 0; i < b.N; i++ {
		for _, test := range uitob64tests {
			AppendUint(dst, test.in, test.base)
		}
	}
}
<commit_msg>strconv: run garbage collection before counting allocations in test<commit_after>// Copyright 2009 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.

package strconv_test

import (
	"runtime"
	. "strconv"
	"testing"
)

type itob64Test struct {
	in   int64
	base int
	out  string
}

var itob64tests = []itob64Test{
	{0, 10, "0"},
	{1, 10, "1"},
	{-1, 10, "-1"},
	{12345678, 10, "12345678"},
	{-987654321, 10, "-987654321"},
	{1<<31 - 1, 10, "2147483647"},
	{-1<<31 + 1, 10, "-2147483647"},
	{1 << 31, 10, "2147483648"},
	{-1 << 31, 10, "-2147483648"},
	{1<<31 + 1, 10, "2147483649"},
	{-1<<31 - 1, 10, "-2147483649"},
	{1<<32 - 1, 10, "4294967295"},
	{-1<<32 + 1, 10, "-4294967295"},
	{1 << 32, 10, "4294967296"},
	{-1 << 32, 10, "-4294967296"},
	{1<<32 + 1, 10, "4294967297"},
	{-1<<32 - 1, 10, "-4294967297"},
	{1 << 50, 10, "1125899906842624"},
	{1<<63 - 1, 10, "9223372036854775807"},
	{-1<<63 + 1, 10, "-9223372036854775807"},
	{-1 << 63, 10, "-9223372036854775808"},

	{0, 2, "0"},
	{10, 2, "1010"},
	{-1, 2, "-1"},
	{1 << 15, 2, "1000000000000000"},

	{-8, 8, "-10"},
	{057635436545, 8, "57635436545"},
	{1 << 24, 8, "100000000"},

	{16, 16, "10"},
	{-0x123456789abcdef, 16, "-123456789abcdef"},
	{1<<63 - 1, 16, "7fffffffffffffff"},
	{1<<63 - 1, 2, "111111111111111111111111111111111111111111111111111111111111111"},

	{16, 17, "g"},
	{25, 25, "10"},
	{(((((17*35+24)*35+21)*35+34)*35+12)*35+24)*35 + 32, 35, "holycow"},
	{(((((17*36+24)*36+21)*36+34)*36+12)*36+24)*36 + 32, 36, "holycow"},
}

func TestItoa(t *testing.T) {
	for _, test := range itob64tests {
		s := FormatInt(test.in, test.base)
		if s != test.out {
			t.Errorf("FormatInt(%v, %v) = %v want %v",
				test.in, test.base, s, test.out)
		}
		x := AppendInt([]byte("abc"), test.in, test.base)
		if string(x) != "abc"+test.out {
			t.Errorf("AppendInt(%q, %v, %v) = %q want %v",
				"abc", test.in, test.base, x, test.out)
		}

		if test.in >= 0 {
			s := FormatUint(uint64(test.in), test.base)
			if s != test.out {
				t.Errorf("FormatUint(%v, %v) = %v want %v",
					test.in, test.base, s, test.out)
			}
			x := AppendUint(nil, uint64(test.in), test.base)
			if string(x) != test.out {
				t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
					"abc", uint64(test.in), test.base, x, test.out)
			}
		}

		if test.base == 10 && int64(int(test.in)) == test.in {
			s := Itoa(int(test.in))
			if s != test.out {
				t.Errorf("Itoa(%v) = %v want %v",
					test.in, s, test.out)
			}
		}
	}
}

type uitob64Test struct {
	in   uint64
	base int
	out  string
}

var uitob64tests = []uitob64Test{
	{1<<63 - 1, 10, "9223372036854775807"},
	{1 << 63, 10, "9223372036854775808"},
	{1<<63 + 1, 10, "9223372036854775809"},
	{1<<64 - 2, 10, "18446744073709551614"},
	{1<<64 - 1, 10, "18446744073709551615"},
	{1<<64 - 1, 2, "1111111111111111111111111111111111111111111111111111111111111111"},
}

func TestUitoa(t *testing.T) {
	for _, test := range uitob64tests {
		s := FormatUint(test.in, test.base)
		if s != test.out {
			t.Errorf("FormatUint(%v, %v) = %v want %v",
				test.in, test.base, s, test.out)
		}
		x := AppendUint([]byte("abc"), test.in, test.base)
		if string(x) != "abc"+test.out {
			t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
				"abc", test.in, test.base, x, test.out)
		}

	}
}

func numAllocations(f func()) int {
	runtime.GC()
	memstats := new(runtime.MemStats)
	runtime.ReadMemStats(memstats)
	n0 := memstats.Mallocs
	f()
	runtime.ReadMemStats(memstats)
	return int(memstats.Mallocs - n0)
}

var globalBuf [64]byte

func TestAppendUintDoesntAllocate(t *testing.T) {
	n := numAllocations(func() {
		var buf [64]byte
		AppendInt(buf[:0], 123, 10)
	})
	want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better
	if n != want {
		t.Errorf("with local buffer, did %d allocations, want %d", n, want)
	}
	n = numAllocations(func() {
		AppendInt(globalBuf[:0], 123, 10)
	})
	if n != 0 {
		t.Errorf("with reused buffer, did %d allocations, want 0", n)
	}
}

func BenchmarkFormatInt(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, test := range itob64tests {
			FormatInt(test.in, test.base)
		}
	}
}

func BenchmarkAppendInt(b *testing.B) {
	dst := make([]byte, 0, 30)
	for i := 0; i < b.N; i++ {
		for _, test := range itob64tests {
			AppendInt(dst, test.in, test.base)
		}
	}
}

func BenchmarkFormatUint(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, test := range uitob64tests {
			FormatUint(test.in, test.base)
		}
	}
}

func BenchmarkAppendUint(b *testing.B) {
	dst := make([]byte, 0, 30)
	for i := 0; i < b.N; i++ {
		for _, test := range uitob64tests {
			AppendUint(dst, test.in, test.base)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

package testing

import (
	"flag"
	"fmt"
	"os"
	"time"
)

var matchBenchmarks = flag.String("benchmarks", "", "regular expression to select benchmarks to run")

// An internal type but exported because it is cross-package; part of the implementation
// of gotest.
type Benchmark struct {
	Name string
	F    func(b *B)
}

// B is a type passed to Benchmark functions to manage benchmark
// timing and to specify the number of iterations to run.
type B struct {
	N         int
	benchmark Benchmark
	ns        int64
	bytes     int64
	start     int64
}

// StartTimer starts timing a test.  This function is called automatically
// before a benchmark starts, but it can also used to resume timing after
// a call to StopTimer.
func (b *B) StartTimer() { b.start = time.Nanoseconds() }

// StopTimer stops timing a test.  This can be used to pause the timer
// while performing complex initialization that you don't
// want to measure.
func (b *B) StopTimer() {
	if b.start > 0 {
		b.ns += time.Nanoseconds() - b.start
	}
	b.start = 0
}

// ResetTimer stops the timer and sets the elapsed benchmark time to zero.
func (b *B) ResetTimer() {
	b.start = 0
	b.ns = 0
}

// SetBytes records the number of bytes processed in a single operation.
// If this is called, the benchmark will report ns/op and MB/s.
func (b *B) SetBytes(n int64) { b.bytes = n }

func (b *B) nsPerOp() int64 {
	if b.N <= 0 {
		return 0
	}
	return b.ns / int64(b.N)
}

// runN runs a single benchmark for the specified number of iterations.
func (b *B) runN(n int) {
	b.N = n
	b.ResetTimer()
	b.StartTimer()
	b.benchmark.F(b)
	b.StopTimer()
}

func min(x, y int) int {
	if x > y {
		return y
	}
	return x
}

// roundDown10 rounds a number down to the nearest power of 10.
func roundDown10(n int) int {
	var tens = 0
	// tens = floor(log_10(n))
	for n > 10 {
		n = n / 10
		tens++
	}
	// result = 10^tens
	result := 1
	for i := 0; i < tens; i++ {
		result *= 10
	}
	return result
}

// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
func roundUp(n int) int {
	base := roundDown10(n)
	if n < (2 * base) {
		return 2 * base
	}
	if n < (5 * base) {
		return 5 * base
	}
	return 10 * base
}

// run times the benchmark function.  It gradually increases the number
// of benchmark iterations until the benchmark runs for a second in order
// to get a reasonable measurement.  It prints timing information in this form
//		testing.BenchmarkHello	100000		19 ns/op
func (b *B) run() {
	// Run the benchmark for a single iteration in case it's expensive.
	n := 1
	b.runN(n)
	// Run the benchmark for at least a second.
	for b.ns < 1e9 && n < 1e9 {
		last := n
		// Predict iterations/sec.
		if b.nsPerOp() == 0 {
			n = 1e9
		} else {
			n = 1e9 / int(b.nsPerOp())
		}
		// Run more iterations than we think we'll need for a second (1.5x).
		// Don't grow too fast in case we had timing errors previously.
		n = min(int(1.5*float(n)), 100*last)
		// Round up to something easy to read.
		n = roundUp(n)
		b.runN(n)
	}
	ns := b.nsPerOp()
	mb := ""
	if ns > 0 && b.bytes > 0 {
		mb = fmt.Sprintf("\t%7.2f MB/s", (float64(b.bytes)/1e6)/(float64(ns)/1e9))
	}
	fmt.Printf("%s\t%8d\t%10d ns/op%s\n", b.benchmark.Name, b.N, b.nsPerOp(), mb)
}

// An internal function but exported because it is cross-package; part of the implementation
// of gotest.
func RunBenchmarks(benchmarks []Benchmark) {
	// If no flag was specified, don't run benchmarks.
	if len(*matchBenchmarks) == 0 {
		return
	}
	re, err := CompileRegexp(*matchBenchmarks)
	if err != "" {
		println("invalid regexp for -benchmarks:", err)
		os.Exit(1)
	}
	for _, Benchmark := range benchmarks {
		if !re.MatchString(Benchmark.Name) {
			continue
		}
		b := &B{benchmark: Benchmark}
		b.run()
	}
}
<commit_msg>testing/benchmark: paranoia - make sure n always grows<commit_after>// Copyright 2009 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.

package testing

import (
	"flag"
	"fmt"
	"os"
	"time"
)

var matchBenchmarks = flag.String("benchmarks", "", "regular expression to select benchmarks to run")

// An internal type but exported because it is cross-package; part of the implementation
// of gotest.
type Benchmark struct {
	Name string
	F    func(b *B)
}

// B is a type passed to Benchmark functions to manage benchmark
// timing and to specify the number of iterations to run.
type B struct {
	N         int
	benchmark Benchmark
	ns        int64
	bytes     int64
	start     int64
}

// StartTimer starts timing a test.  This function is called automatically
// before a benchmark starts, but it can also used to resume timing after
// a call to StopTimer.
func (b *B) StartTimer() { b.start = time.Nanoseconds() }

// StopTimer stops timing a test.  This can be used to pause the timer
// while performing complex initialization that you don't
// want to measure.
func (b *B) StopTimer() {
	if b.start > 0 {
		b.ns += time.Nanoseconds() - b.start
	}
	b.start = 0
}

// ResetTimer stops the timer and sets the elapsed benchmark time to zero.
func (b *B) ResetTimer() {
	b.start = 0
	b.ns = 0
}

// SetBytes records the number of bytes processed in a single operation.
// If this is called, the benchmark will report ns/op and MB/s.
func (b *B) SetBytes(n int64) { b.bytes = n }

func (b *B) nsPerOp() int64 {
	if b.N <= 0 {
		return 0
	}
	return b.ns / int64(b.N)
}

// runN runs a single benchmark for the specified number of iterations.
func (b *B) runN(n int) {
	b.N = n
	b.ResetTimer()
	b.StartTimer()
	b.benchmark.F(b)
	b.StopTimer()
}

func min(x, y int) int {
	if x > y {
		return y
	}
	return x
}

func max(x, y int) int {
	if x < y {
		return y
	}
	return x
}

// roundDown10 rounds a number down to the nearest power of 10.
func roundDown10(n int) int {
	var tens = 0
	// tens = floor(log_10(n))
	for n > 10 {
		n = n / 10
		tens++
	}
	// result = 10^tens
	result := 1
	for i := 0; i < tens; i++ {
		result *= 10
	}
	return result
}

// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
func roundUp(n int) int {
	base := roundDown10(n)
	if n < (2 * base) {
		return 2 * base
	}
	if n < (5 * base) {
		return 5 * base
	}
	return 10 * base
}

// run times the benchmark function.  It gradually increases the number
// of benchmark iterations until the benchmark runs for a second in order
// to get a reasonable measurement.  It prints timing information in this form
//		testing.BenchmarkHello	100000		19 ns/op
func (b *B) run() {
	// Run the benchmark for a single iteration in case it's expensive.
	n := 1
	b.runN(n)
	// Run the benchmark for at least a second.
	for b.ns < 1e9 && n < 1e9 {
		last := n
		// Predict iterations/sec.
		if b.nsPerOp() == 0 {
			n = 1e9
		} else {
			n = 1e9 / int(b.nsPerOp())
		}
		// Run more iterations than we think we'll need for a second (1.5x).
		// Don't grow too fast in case we had timing errors previously.
		// Be sure to run at least one more than last time.
		n = max(min(n+n/2, 100*last), last+1)
		// Round up to something easy to read.
		n = roundUp(n)
		b.runN(n)
	}
	ns := b.nsPerOp()
	mb := ""
	if ns > 0 && b.bytes > 0 {
		mb = fmt.Sprintf("\t%7.2f MB/s", (float64(b.bytes)/1e6)/(float64(ns)/1e9))
	}
	fmt.Printf("%s\t%8d\t%10d ns/op%s\n", b.benchmark.Name, b.N, b.nsPerOp(), mb)
}

// An internal function but exported because it is cross-package; part of the implementation
// of gotest.
func RunBenchmarks(benchmarks []Benchmark) {
	// If no flag was specified, don't run benchmarks.
	if len(*matchBenchmarks) == 0 {
		return
	}
	re, err := CompileRegexp(*matchBenchmarks)
	if err != "" {
		println("invalid regexp for -benchmarks:", err)
		os.Exit(1)
	}
	for _, Benchmark := range benchmarks {
		if !re.MatchString(Benchmark.Name) {
			continue
		}
		b := &B{benchmark: Benchmark}
		b.run()
	}
}
<|endoftext|>


<commit_before><commit_msg>Apply govet/golint suggestions<commit_after><|endoftext|>