diff --git a/third/github.com/pierrec/lz4/lz4c/main.go b/third/github.com/pierrec/lz4/lz4c/main.go deleted file mode 100644 index 849e056df..000000000 --- a/third/github.com/pierrec/lz4/lz4c/main.go +++ /dev/null @@ -1,124 +0,0 @@ -// Command line utility for the lz4 package. -package main - -import ( - // "bytes" - - "flag" - "fmt" - "io" - "log" - "os" - "path" - "runtime" - "strings" - - "github.com/gogf/gf/third/github.com/pierrec/lz4" - "github.com/gogf/gf/third/github.com/pkg/profile" -) - -func main() { - // Process command line arguments - var ( - blockMaxSizeDefault = 4 << 20 - flagStdout = flag.Bool("c", false, "output to stdout") - flagDecompress = flag.Bool("d", false, "decompress flag") - flagBlockMaxSize = flag.Int("B", blockMaxSizeDefault, "block max size [64Kb,256Kb,1Mb,4Mb]") - flagBlockChecksum = flag.Bool("BX", false, "enable block checksum") - flagStreamChecksum = flag.Bool("Sx", false, "disable stream checksum") - flagCompressionLevel = flag.Int("l", 0, "compression level (0=fastest)") - profileName = flag.String("p", "", "path to the profile file") - mode = flag.String("profile.mode", "", "enable profiling mode, one of [cpu, mem, mutex, block]") - ) - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage:\n\t%s [arg] [input]...\n\tNo input means [de]compress stdin to stdout\n\n", os.Args[0]) - flag.PrintDefaults() - } - flag.Parse() - - ppath := profile.ProfilePath(*profileName) - switch *mode { - case "cpu": - defer profile.Start(profile.CPUProfile, ppath).Stop() - case "mem": - defer profile.Start(profile.MemProfile, ppath).Stop() - case "mutex": - defer profile.Start(profile.MutexProfile, ppath).Stop() - case "block": - defer profile.Start(profile.BlockProfile, ppath).Stop() - default: - // do nothing - } - flag.Parse() - - // Use all CPUs - runtime.GOMAXPROCS(runtime.NumCPU()) - - zr := lz4.NewReader(nil) - zw := lz4.NewWriter(nil) - zh := lz4.Header{ - BlockChecksum: *flagBlockChecksum, - BlockMaxSize: *flagBlockMaxSize, - NoChecksum: *flagStreamChecksum, - CompressionLevel: *flagCompressionLevel, - } - - worker := func(in io.Reader, out io.Writer) { - if *flagDecompress { - zr.Reset(in) - if _, err := io.Copy(out, zr); err != nil { - log.Fatalf("Error while decompressing input: %v", err) - } - } else { - zw.Reset(out) - zw.Header = zh - if _, err := io.Copy(zw, in); err != nil { - log.Fatalf("Error while compressing input: %v", err) - } - if err := zw.Close(); err != nil { - log.Fatalf("Error while closing stream: %v", err) - } - } - } - - // No input means [de]compress stdin to stdout - if len(flag.Args()) == 0 { - worker(os.Stdin, os.Stdout) - os.Exit(0) - } - - // Compress or decompress all input files - for _, inputFileName := range flag.Args() { - outputFileName := path.Clean(inputFileName) - - if !*flagStdout { - if *flagDecompress { - outputFileName = strings.TrimSuffix(outputFileName, lz4.Extension) - if outputFileName == inputFileName { - log.Fatalf("Invalid output file name: same as input: %s", inputFileName) - } - } else { - outputFileName += lz4.Extension - } - } - - inputFile, err := os.Open(inputFileName) - if err != nil { - log.Fatalf("Error while opening input: %v", err) - } - - outputFile := os.Stdout - if !*flagStdout { - outputFile, err = os.Create(outputFileName) - if err != nil { - log.Fatalf("Error while opening output: %v", err) - } - } - worker(inputFile, outputFile) - - inputFile.Close() - if !*flagStdout { - outputFile.Close() - } - } -} diff --git a/third/github.com/stathat/go/.gitignore b/third/github.com/stathat/go/.gitignore new file mode 100644 index 000000000..00268614f --- /dev/null +++ b/third/github.com/stathat/go/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/third/github.com/stathat/go/LICENSE b/third/github.com/stathat/go/LICENSE new file mode 100644 index 000000000..814c09c66 --- /dev/null +++ b/third/github.com/stathat/go/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012 Numerotron Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third/github.com/stathat/go/README.md b/third/github.com/stathat/go/README.md new file mode 100644 index 000000000..4e2fb6e30 --- /dev/null +++ b/third/github.com/stathat/go/README.md @@ -0,0 +1,67 @@ +stathat +======= + +This is a Go package for posting stats to your StatHat account. + +For more information about StatHat, visit [www.stathat.com](http://www.stathat.com). + +Installation +------------ + +Use `go get`: + + go get github.com/stathat/go + +That's it. + +Import it like this: + + import ( + "github.com/stathat/go" + ) + +Usage +----- + +The easiest way to use the package is with the EZ API functions. You can add stats +directly in your code by just adding a call with a new stat name. Once StatHat +receives the call, a new stat will be created for you. + +To post a count of 1 to a stat: + + stathat.PostEZCountOne("messages sent - female to male", "something@stathat.com") + +To specify the count: + + stathat.PostEZCount("messages sent - male to male", "something@stathat.com", 37) + +To post a value: + + stathat.PostEZValue("ws0 load average", "something@stathat.com", 0.372) + +There are also functions for the classic API. The drawback to the classic API is +that you need to create the stats using the web interface and copy the keys it +gives you into your code. + +To post a count of 1 to a stat using the classic API: + + stathat.PostCountOne("statkey", "userkey") + +To specify the count: + + stathat.PostCount("statkey", "userkey", 37) + +To post a value: + + stathat.PostValue("statkey", "userkey", 0.372) + +Contact us +---------- + +We'd love to hear from you if you are using this in your projects! Please drop us a +line: [@stat_hat](http://twitter.com/stat_hat) or [contact us here](http://www.stathat.com/docs/contact). + +About +----- + +Written by Patrick Crosby at [StatHat](http://www.stathat.com). Twitter: [@stat_hat](http://twitter.com/stat_hat) diff --git a/third/github.com/stathat/go/example_test.go b/third/github.com/stathat/go/example_test.go new file mode 100644 index 000000000..26d6399c1 --- /dev/null +++ b/third/github.com/stathat/go/example_test.go @@ -0,0 +1,29 @@ +// Copyright (C) 2012 Numerotron Inc. +// Use of this source code is governed by an MIT-style license +// that can be found in the LICENSE file. + +package stathat_test + +import ( + "log" + + stathat "github.com/stathat/go" +) + +func ExamplePostEZCountOne() { + log.Printf("starting example") + stathat.Verbose = true + err := stathat.PostEZCountOne("go example test run", "nobody@stathat.com") + if err != nil { + log.Printf("error posting ez count one: %v", err) + return + } + // If using this with a valid account, this could be helpful in a short script: + /* + ok := stathat.WaitUntilFinished(5 * time.Second) + if ok { + fmt.Println("ok") + } + */ + // Output: +} diff --git a/third/github.com/stathat/go/stathat.go b/third/github.com/stathat/go/stathat.go new file mode 100644 index 000000000..fa001b23f --- /dev/null +++ b/third/github.com/stathat/go/stathat.go @@ -0,0 +1,605 @@ +// Copyright (C) 2012 Numerotron Inc. +// Use of this source code is governed by an MIT-style license +// that can be found in the LICENSE file. + +// Copyright 2012 Numerotron Inc. +// Use of this source code is governed by an MIT-style license +// that can be found in the LICENSE file. +// +// Developed at www.stathat.com by Patrick Crosby +// Contact us on twitter with any questions: twitter.com/stat_hat + +// The stathat package makes it easy to post any values to your StatHat +// account. +package stathat + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/url" + "strconv" + "sync" + "time" +) + +const hostname = "api.stathat.com" + +type statKind int + +const ( + _ = iota + kcounter statKind = iota + kvalue +) + +func (sk statKind) classicPath() string { + switch sk { + case kcounter: + return "/c" + case kvalue: + return "/v" + } + return "" +} + +type apiKind int + +const ( + _ = iota + classic apiKind = iota + ez +) + +func (ak apiKind) path(sk statKind) string { + switch ak { + case ez: + return "/ez" + case classic: + return sk.classicPath() + } + return "" +} + +type statReport struct { + StatKey string + UserKey string + Value float64 + Timestamp int64 + statType statKind + apiType apiKind +} + +// Reporter describes an interface for communicating with the StatHat API +type Reporter interface { + PostCount(statKey, userKey string, count int) error + PostCountTime(statKey, userKey string, count int, timestamp int64) error + PostCountOne(statKey, userKey string) error + PostValue(statKey, userKey string, value float64) error + PostValueTime(statKey, userKey string, value float64, timestamp int64) error + PostEZCountOne(statName, ezkey string) error + PostEZCount(statName, ezkey string, count int) error + PostEZCountTime(statName, ezkey string, count int, timestamp int64) error + PostEZValue(statName, ezkey string, value float64) error + PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error + WaitUntilFinished(timeout time.Duration) bool +} + +// BasicReporter is a StatHat client that can report stat values/counts to the servers. +type BasicReporter struct { + reports chan *statReport + done chan bool + client *http.Client + wg *sync.WaitGroup +} + +// NewReporter returns a new Reporter. You must specify the channel bufferSize and the +// goroutine poolSize. You can pass in nil for the transport and it will create an +// http transport with MaxIdleConnsPerHost set to the goroutine poolSize. Note if you +// pass in your own transport, it's a good idea to have its MaxIdleConnsPerHost be set +// to at least the poolSize to allow for effective connection reuse. +func NewReporter(bufferSize, poolSize int, transport http.RoundTripper) Reporter { + r := new(BasicReporter) + if transport == nil { + transport = &http.Transport{ + // Allow for an idle connection per goroutine. + MaxIdleConnsPerHost: poolSize, + } + } + r.client = &http.Client{Transport: transport} + r.reports = make(chan *statReport, bufferSize) + r.done = make(chan bool) + r.wg = new(sync.WaitGroup) + for i := 0; i < poolSize; i++ { + r.wg.Add(1) + go r.processReports() + } + return r +} + +type statCache struct { + counterStats map[string]int + valueStats map[string][]float64 +} + +func (sc *statCache) AverageValue(statName string) float64 { + total := 0.0 + values := sc.valueStats[statName] + if len(values) == 0 { + return total + } + for _, value := range values { + total += value + } + return total / float64(len(values)) +} + +// BatchReporter wraps an existing Reporter in order to implement sending stats +// to the StatHat server in batch. The flow is only available for the EZ API. +// The following describes how stats are sent: +// 1.) PostEZCountOne is called and adds the stat request to a queue. +// 2.) PostEZCountOne is called again on the same stat, the value in the queue is incremented. +// 3.) After batchInterval amount of time, all stat requests from the queue are +// sent to the server. +type BatchReporter struct { + sync.Mutex + r Reporter + batchInterval time.Duration + caches map[string]*statCache + shutdownBatchCh chan struct{} +} + +// DefaultReporter is the default instance of *Reporter. +var DefaultReporter = NewReporter(100000, 10, nil) + +var testingEnv = false + +type testPost struct { + url string + values url.Values +} + +var testPostChannel chan *testPost + +// The Verbose flag determines if the package should write verbose output to stdout. +var Verbose = false + +func setTesting() { + testingEnv = true + testPostChannel = make(chan *testPost) +} + +func newEZStatCount(statName, ezkey string, count int) *statReport { + return &statReport{StatKey: statName, + UserKey: ezkey, + Value: float64(count), + statType: kcounter, + apiType: ez} +} + +func newEZStatValue(statName, ezkey string, value float64) *statReport { + return &statReport{StatKey: statName, + UserKey: ezkey, + Value: value, + statType: kvalue, + apiType: ez} +} + +func newClassicStatCount(statKey, userKey string, count int) *statReport { + return &statReport{StatKey: statKey, + UserKey: userKey, + Value: float64(count), + statType: kcounter, + apiType: classic} +} + +func newClassicStatValue(statKey, userKey string, value float64) *statReport { + return &statReport{StatKey: statKey, + UserKey: userKey, + Value: value, + statType: kvalue, + apiType: classic} +} + +func (sr *statReport) values() url.Values { + switch sr.apiType { + case ez: + return sr.ezValues() + case classic: + return sr.classicValues() + } + + return nil +} + +func (sr *statReport) ezValues() url.Values { + switch sr.statType { + case kcounter: + return sr.ezCounterValues() + case kvalue: + return sr.ezValueValues() + } + return nil +} + +func (sr *statReport) classicValues() url.Values { + switch sr.statType { + case kcounter: + return sr.classicCounterValues() + case kvalue: + return sr.classicValueValues() + } + return nil +} + +func (sr *statReport) ezCommonValues() url.Values { + result := make(url.Values) + result.Set("stat", sr.StatKey) + result.Set("ezkey", sr.UserKey) + if sr.Timestamp > 0 { + result.Set("t", sr.timeString()) + } + return result +} + +func (sr *statReport) classicCommonValues() url.Values { + result := make(url.Values) + result.Set("key", sr.StatKey) + result.Set("ukey", sr.UserKey) + if sr.Timestamp > 0 { + result.Set("t", sr.timeString()) + } + return result +} + +func (sr *statReport) ezCounterValues() url.Values { + result := sr.ezCommonValues() + result.Set("count", sr.valueString()) + return result +} + +func (sr *statReport) ezValueValues() url.Values { + result := sr.ezCommonValues() + result.Set("value", sr.valueString()) + return result +} + +func (sr *statReport) classicCounterValues() url.Values { + result := sr.classicCommonValues() + result.Set("count", sr.valueString()) + return result +} + +func (sr *statReport) classicValueValues() url.Values { + result := sr.classicCommonValues() + result.Set("value", sr.valueString()) + return result +} + +func (sr *statReport) valueString() string { + return strconv.FormatFloat(sr.Value, 'g', -1, 64) +} + +func (sr *statReport) timeString() string { + return strconv.FormatInt(sr.Timestamp, 10) +} + +func (sr *statReport) path() string { + return sr.apiType.path(sr.statType) +} + +func (sr *statReport) url() string { + return fmt.Sprintf("https://%s%s", hostname, sr.path()) +} + +// Using the classic API, posts a count to a stat using DefaultReporter. +func PostCount(statKey, userKey string, count int) error { + return DefaultReporter.PostCount(statKey, userKey, count) +} + +// Using the classic API, posts a count to a stat using DefaultReporter at a specific +// time. +func PostCountTime(statKey, userKey string, count int, timestamp int64) error { + return DefaultReporter.PostCountTime(statKey, userKey, count, timestamp) +} + +// Using the classic API, posts a count of 1 to a stat using DefaultReporter. +func PostCountOne(statKey, userKey string) error { + return DefaultReporter.PostCountOne(statKey, userKey) +} + +// Using the classic API, posts a value to a stat using DefaultReporter. +func PostValue(statKey, userKey string, value float64) error { + return DefaultReporter.PostValue(statKey, userKey, value) +} + +// Using the classic API, posts a value to a stat at a specific time using DefaultReporter. +func PostValueTime(statKey, userKey string, value float64, timestamp int64) error { + return DefaultReporter.PostValueTime(statKey, userKey, value, timestamp) +} + +// Using the EZ API, posts a count of 1 to a stat using DefaultReporter. +func PostEZCountOne(statName, ezkey string) error { + return DefaultReporter.PostEZCountOne(statName, ezkey) +} + +// Using the EZ API, posts a count to a stat using DefaultReporter. +func PostEZCount(statName, ezkey string, count int) error { + return DefaultReporter.PostEZCount(statName, ezkey, count) +} + +// Using the EZ API, posts a count to a stat at a specific time using DefaultReporter. +func PostEZCountTime(statName, ezkey string, count int, timestamp int64) error { + return DefaultReporter.PostEZCountTime(statName, ezkey, count, timestamp) +} + +// Using the EZ API, posts a value to a stat using DefaultReporter. +func PostEZValue(statName, ezkey string, value float64) error { + return DefaultReporter.PostEZValue(statName, ezkey, value) +} + +// Using the EZ API, posts a value to a stat at a specific time using DefaultReporter. +func PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error { + return DefaultReporter.PostEZValueTime(statName, ezkey, value, timestamp) +} + +// Wait for all stats to be sent, or until timeout. Useful for simple command- +// line apps to defer a call to this in main() +func WaitUntilFinished(timeout time.Duration) bool { + return DefaultReporter.WaitUntilFinished(timeout) +} + +// Using the classic API, posts a count to a stat. +func (r *BasicReporter) PostCount(statKey, userKey string, count int) error { + r.add(newClassicStatCount(statKey, userKey, count)) + return nil +} + +// Using the classic API, posts a count to a stat at a specific time. +func (r *BasicReporter) PostCountTime(statKey, userKey string, count int, timestamp int64) error { + x := newClassicStatCount(statKey, userKey, count) + x.Timestamp = timestamp + r.add(x) + return nil +} + +// Using the classic API, posts a count of 1 to a stat. +func (r *BasicReporter) PostCountOne(statKey, userKey string) error { + return r.PostCount(statKey, userKey, 1) +} + +// Using the classic API, posts a value to a stat. +func (r *BasicReporter) PostValue(statKey, userKey string, value float64) error { + r.add(newClassicStatValue(statKey, userKey, value)) + return nil +} + +// Using the classic API, posts a value to a stat at a specific time. +func (r *BasicReporter) PostValueTime(statKey, userKey string, value float64, timestamp int64) error { + x := newClassicStatValue(statKey, userKey, value) + x.Timestamp = timestamp + r.add(x) + return nil +} + +// Using the EZ API, posts a count of 1 to a stat. +func (r *BasicReporter) PostEZCountOne(statName, ezkey string) error { + return r.PostEZCount(statName, ezkey, 1) +} + +// Using the EZ API, posts a count to a stat. +func (r *BasicReporter) PostEZCount(statName, ezkey string, count int) error { + r.add(newEZStatCount(statName, ezkey, count)) + return nil +} + +// Using the EZ API, posts a count to a stat at a specific time. +func (r *BasicReporter) PostEZCountTime(statName, ezkey string, count int, timestamp int64) error { + x := newEZStatCount(statName, ezkey, count) + x.Timestamp = timestamp + r.add(x) + return nil +} + +// Using the EZ API, posts a value to a stat. +func (r *BasicReporter) PostEZValue(statName, ezkey string, value float64) error { + r.add(newEZStatValue(statName, ezkey, value)) + return nil +} + +// Using the EZ API, posts a value to a stat at a specific time. +func (r *BasicReporter) PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error { + x := newEZStatValue(statName, ezkey, value) + x.Timestamp = timestamp + r.add(x) + return nil +} + +func (r *BasicReporter) processReports() { + for sr := range r.reports { + if Verbose { + log.Printf("posting stat to stathat: %s, %v", sr.url(), sr.values()) + } + + if testingEnv { + if Verbose { + log.Printf("in test mode, putting stat on testPostChannel") + } + testPostChannel <- &testPost{sr.url(), sr.values()} + continue + } + + resp, err := r.client.PostForm(sr.url(), sr.values()) + if err != nil { + log.Printf("error posting stat to stathat: %s", err) + continue + } + + if Verbose { + body, _ := ioutil.ReadAll(resp.Body) + log.Printf("stathat post result: %s", body) + } else { + // Read the body even if we don't intend to use it. Otherwise golang won't pool the connection. + // See also: http://stackoverflow.com/questions/17948827/reusing-http-connections-in-golang/17953506#17953506 + io.Copy(ioutil.Discard, resp.Body) + } + + resp.Body.Close() + } + r.wg.Done() +} + +func (r *BasicReporter) add(rep *statReport) { + select { + case r.reports <- rep: + default: + } +} + +func (r *BasicReporter) finish() { + close(r.reports) + r.wg.Wait() + r.done <- true +} + +// Wait for all stats to be sent, or until timeout. Useful for simple command- +// line apps to defer a call to this in main() +func (r *BasicReporter) WaitUntilFinished(timeout time.Duration) bool { + go r.finish() + select { + case <-r.done: + return true + case <-time.After(timeout): + return false + } +} + +// NewBatchReporter creates a batching stat reporter. The interval parameter +// specifies how often stats should be posted to the StatHat server. +func NewBatchReporter(reporter Reporter, interval time.Duration) Reporter { + + br := &BatchReporter{ + r: reporter, + batchInterval: interval, + caches: make(map[string]*statCache), + shutdownBatchCh: make(chan struct{}), + } + + go br.batchLoop() + + return br +} + +func (br *BatchReporter) getEZCache(ezkey string) *statCache { + var cache *statCache + var ok bool + + // Fetch ezkey cache + if cache, ok = br.caches[ezkey]; !ok { + cache = &statCache{ + counterStats: make(map[string]int), + valueStats: make(map[string][]float64), + } + br.caches[ezkey] = cache + } + + return cache +} + +func (br *BatchReporter) PostEZCount(statName, ezkey string, count int) error { + br.Lock() + defer br.Unlock() + + // Increment stat by count + br.getEZCache(ezkey).counterStats[statName] += count + + return nil +} + +func (br *BatchReporter) PostEZCountOne(statName, ezkey string) error { + return br.PostEZCount(statName, ezkey, 1) +} + +func (br *BatchReporter) PostEZValue(statName, ezkey string, value float64) error { + br.Lock() + defer br.Unlock() + + // Update value cache + cache := br.getEZCache(ezkey) + cache.valueStats[statName] = append(cache.valueStats[statName], value) + + return nil +} + +func (br *BatchReporter) batchPost() { + + // Copy and clear cache + br.Lock() + caches := br.caches + br.caches = make(map[string]*statCache) + br.Unlock() + + // Post stats + for ezkey, cache := range caches { + // Post counters + for statName, count := range cache.counterStats { + br.r.PostEZCount(statName, ezkey, count) + } + + // Post values + for statName := range cache.valueStats { + br.r.PostEZValue(statName, ezkey, cache.AverageValue(statName)) + } + } +} + +func (br *BatchReporter) batchLoop() { + for { + select { + case <-br.shutdownBatchCh: + return + case <-time.After(br.batchInterval): + br.batchPost() + } + } +} + +func (br *BatchReporter) PostCount(statKey, userKey string, count int) error { + return br.r.PostCount(statKey, userKey, count) +} + +func (br *BatchReporter) PostCountTime(statKey, userKey string, count int, timestamp int64) error { + return br.r.PostCountTime(statKey, userKey, count, timestamp) +} + +func (br *BatchReporter) PostCountOne(statKey, userKey string) error { + return br.r.PostCountOne(statKey, userKey) +} + +func (br *BatchReporter) PostValue(statKey, userKey string, value float64) error { + return br.r.PostValue(statKey, userKey, value) +} + +func (br *BatchReporter) PostValueTime(statKey, userKey string, value float64, timestamp int64) error { + return br.r.PostValueTime(statKey, userKey, value, timestamp) +} + +func (br *BatchReporter) PostEZCountTime(statName, ezkey string, count int, timestamp int64) error { + return br.r.PostEZCountTime(statName, ezkey, count, timestamp) +} + +func (br *BatchReporter) PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error { + return br.r.PostEZValueTime(statName, ezkey, value, timestamp) +} + +func (br *BatchReporter) WaitUntilFinished(timeout time.Duration) bool { + // Shut down batch loop + close(br.shutdownBatchCh) + + // One last post + br.batchPost() + + return br.r.WaitUntilFinished(timeout) +} diff --git a/third/github.com/stathat/go/stathat_test.go b/third/github.com/stathat/go/stathat_test.go new file mode 100644 index 000000000..78732f3a7 --- /dev/null +++ b/third/github.com/stathat/go/stathat_test.go @@ -0,0 +1,319 @@ +// Copyright (C) 2012 Numerotron Inc. +// Use of this source code is governed by an MIT-style license +// that can be found in the LICENSE file. + +package stathat + +import ( + "testing" +) + +func TestNewEZStatCount(t *testing.T) { + setTesting() + x := newEZStatCount("abc", "pc@pc.com", 1) + if x == nil { + t.Fatalf("expected a StatReport object") + } + if x.statType != kcounter { + t.Errorf("expected counter") + } + if x.apiType != ez { + t.Errorf("expected EZ api") + } + if x.StatKey != "abc" { + t.Errorf("expected abc") + } + if x.UserKey != "pc@pc.com" { + t.Errorf("expected pc@pc.com") + } + if x.Value != 1.0 { + t.Errorf("expected 1.0") + } + if x.Timestamp != 0 { + t.Errorf("expected 0") + } +} + +func TestNewEZStatValue(t *testing.T) { + setTesting() + x := newEZStatValue("abc", "pc@pc.com", 3.14159) + if x == nil { + t.Fatalf("expected a StatReport object") + } + if x.statType != kvalue { + t.Errorf("expected value") + } + if x.apiType != ez { + t.Errorf("expected EZ api") + } + if x.StatKey != "abc" { + t.Errorf("expected abc") + } + if x.UserKey != "pc@pc.com" { + t.Errorf("expected pc@pc.com") + } + if x.Value != 3.14159 { + t.Errorf("expected 3.14159") + } +} + +func TestNewClassicStatCount(t *testing.T) { + setTesting() + x := newClassicStatCount("statkey", "userkey", 1) + if x == nil { + t.Fatalf("expected a StatReport object") + } + if x.statType != kcounter { + t.Errorf("expected counter") + } + if x.apiType != classic { + t.Errorf("expected CLASSIC api") + } + if x.StatKey != "statkey" { + t.Errorf("expected statkey") + } + if x.UserKey != "userkey" { + t.Errorf("expected userkey") + } + if x.Value != 1.0 { + t.Errorf("expected 1.0") + } + if x.Timestamp != 0 { + t.Errorf("expected 0") + } +} + +func TestNewClassicStatValue(t *testing.T) { + setTesting() + x := newClassicStatValue("statkey", "userkey", 2.28) + if x == nil { + t.Fatalf("expected a StatReport object") + } + if x.statType != kvalue { + t.Errorf("expected value") + } + if x.apiType != classic { + t.Errorf("expected CLASSIC api") + } + if x.StatKey != "statkey" { + t.Errorf("expected statkey") + } + if x.UserKey != "userkey" { + t.Errorf("expected userkey") + } + if x.Value != 2.28 { + t.Errorf("expected 2.28") + } +} + +func TestURLValues(t *testing.T) { + setTesting() + x := newEZStatCount("abc", "pc@pc.com", 1) + v := x.values() + if v == nil { + t.Fatalf("expected url values") + } + if v.Get("stat") != "abc" { + t.Errorf("expected abc") + } + if v.Get("ezkey") != "pc@pc.com" { + t.Errorf("expected pc@pc.com") + } + if v.Get("count") != "1" { + t.Errorf("expected count of 1") + } + + y := newEZStatValue("abc", "pc@pc.com", 3.14159) + v = y.values() + if v == nil { + t.Fatalf("expected url values") + } + if v.Get("stat") != "abc" { + t.Errorf("expected abc") + } + if v.Get("ezkey") != "pc@pc.com" { + t.Errorf("expected pc@pc.com") + } + if v.Get("value") != "3.14159" { + t.Errorf("expected value of 3.14159") + } + + a := newClassicStatCount("statkey", "userkey", 1) + v = a.values() + if v == nil { + t.Fatalf("expected url values") + } + if v.Get("key") != "statkey" { + t.Errorf("expected statkey") + } + if v.Get("ukey") != "userkey" { + t.Errorf("expected userkey") + } + if v.Get("count") != "1" { + t.Errorf("expected count of 1") + } + + b := newClassicStatValue("statkey", "userkey", 2.28) + v = b.values() + if v == nil { + t.Fatalf("expected url values") + } + if v.Get("key") != "statkey" { + t.Errorf("expected statkey") + } + if v.Get("ukey") != "userkey" { + t.Errorf("expected userkey") + } + if v.Get("value") != "2.28" { + t.Errorf("expected value of 2.28") + } +} + +func TestPaths(t *testing.T) { + if ez.path(kcounter) != "/ez" { + t.Errorf("expected /ez") + } + if ez.path(kvalue) != "/ez" { + t.Errorf("expected /ez") + } + if classic.path(kcounter) != "/c" { + t.Errorf("expected /c") + } + if classic.path(kvalue) != "/v" { + t.Errorf("expected /v") + } + + x := newEZStatCount("abc", "pc@pc.com", 1) + if x.path() != "/ez" { + t.Errorf("expected /ez") + } + y := newEZStatValue("abc", "pc@pc.com", 3.14159) + if y.path() != "/ez" { + t.Errorf("expected /ez") + } + a := newClassicStatCount("statkey", "userkey", 1) + if a.path() != "/c" { + t.Errorf("expected /c") + } + b := newClassicStatValue("statkey", "userkey", 2.28) + if b.path() != "/v" { + t.Errorf("expected /v") + } +} + +func TestPosts(t *testing.T) { + setTesting() + PostCountOne("statkey", "userkey") + p := <-testPostChannel + if p.url != "https://api.stathat.com/c" { + t.Errorf("expected classic count url") + } + if p.values.Get("key") != "statkey" { + t.Errorf("expected statkey") + } + if p.values.Get("ukey") != "userkey" { + t.Errorf("expected userkey") + } + if p.values.Get("count") != "1" { + t.Errorf("expected count of 1") + } + + PostCount("statkey", "userkey", 13) + p = <-testPostChannel + if p.url != "https://api.stathat.com/c" { + t.Errorf("expected classic count url") + } + if p.values.Get("key") != "statkey" { + t.Errorf("expected statkey") + } + if p.values.Get("ukey") != "userkey" { + t.Errorf("expected userkey") + } + if p.values.Get("count") != "13" { + t.Errorf("expected count of 13") + } + + PostValue("statkey", "userkey", 9.312) + p = <-testPostChannel + if p.url != "https://api.stathat.com/v" { + t.Errorf("expected classic value url") + } + if p.values.Get("key") != "statkey" { + t.Errorf("expected statkey") + } + if p.values.Get("ukey") != "userkey" { + t.Errorf("expected userkey") + } + if p.values.Get("value") != "9.312" { + t.Errorf("expected value of 9.312") + } + + PostEZCountOne("a stat", "pc@pc.com") + p = <-testPostChannel + if p.url != "https://api.stathat.com/ez" { + t.Errorf("expected ez url") + } + if p.values.Get("stat") != "a stat" { + t.Errorf("expected a stat") + } + if p.values.Get("ezkey") != "pc@pc.com" { + t.Errorf("expected pc@pc.com") + } + if p.values.Get("count") != "1" { + t.Errorf("expected count of 1") + } + + PostEZCount("a stat", "pc@pc.com", 213) + p = <-testPostChannel + if p.url != "https://api.stathat.com/ez" { + t.Errorf("expected ez url") + } + if p.values.Get("stat") != "a stat" { + t.Errorf("expected a stat") + } + if p.values.Get("ezkey") != "pc@pc.com" { + t.Errorf("expected pc@pc.com") + } + if p.values.Get("count") != "213" { + t.Errorf("expected count of 213") + } + + PostEZValue("a stat", "pc@pc.com", 2.13) + p = <-testPostChannel + if p.url != "https://api.stathat.com/ez" { + t.Errorf("expected ez url") + } + if p.values.Get("stat") != "a stat" { + t.Errorf("expected a stat") + } + if p.values.Get("ezkey") != "pc@pc.com" { + t.Errorf("expected pc@pc.com") + } + if p.values.Get("value") != "2.13" { + t.Errorf("expected value of 2.13") + } + + PostCountTime("statkey", "userkey", 13, 100000) + p = <-testPostChannel + if p.values.Get("t") != "100000" { + t.Errorf("expected t value of 100000, got %s", p.values.Get("t")) + } + + PostValueTime("statkey", "userkey", 9.312, 200000) + p = <-testPostChannel + if p.values.Get("t") != "200000" { + t.Errorf("expected t value of 200000, got %s", p.values.Get("t")) + } + + PostEZCountTime("a stat", "pc@pc.com", 213, 300000) + p = <-testPostChannel + if p.values.Get("t") != "300000" { + t.Errorf("expected t value of 300000, got %s", p.values.Get("t")) + } + + PostEZValueTime("a stat", "pc@pc.com", 2.13, 400000) + p = <-testPostChannel + if p.values.Get("t") != "400000" { + t.Errorf("expected t value of 400000, got %s", p.values.Get("t")) + } +}