@ -0,0 +1,19 @@ | |||||
# top-most EditorConfig file | |||||
root = true | |||||
# Unix-style newlines with a newline ending every file | |||||
[*] | |||||
charset = utf-8 | |||||
end_of_line = lf | |||||
insert_final_newline = true | |||||
trim_trailing_whitespace = true | |||||
[Makefile] | |||||
indent_style = tab | |||||
[*.sh] | |||||
indent_style = tab | |||||
[*.proto] | |||||
indent_style = space | |||||
indent_size = 2 |
@ -0,0 +1,193 @@ | |||||
Tendermint Libraries | |||||
Copyright (C) 2017 Tendermint | |||||
Apache License | |||||
Version 2.0, January 2004 | |||||
https://www.apache.org/licenses/ | |||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||||
1. Definitions. | |||||
"License" shall mean the terms and conditions for use, reproduction, | |||||
and distribution as defined by Sections 1 through 9 of this document. | |||||
"Licensor" shall mean the copyright owner or entity authorized by | |||||
the copyright owner that is granting the License. | |||||
"Legal Entity" shall mean the union of the acting entity and all | |||||
other entities that control, are controlled by, or are under common | |||||
control with that entity. For the purposes of this definition, | |||||
"control" means (i) the power, direct or indirect, to cause the | |||||
direction or management of such entity, whether by contract or | |||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||||
outstanding shares, or (iii) beneficial ownership of such entity. | |||||
"You" (or "Your") shall mean an individual or Legal Entity | |||||
exercising permissions granted by this License. | |||||
"Source" form shall mean the preferred form for making modifications, | |||||
including but not limited to software source code, documentation | |||||
source, and configuration files. | |||||
"Object" form shall mean any form resulting from mechanical | |||||
transformation or translation of a Source form, including but | |||||
not limited to compiled object code, generated documentation, | |||||
and conversions to other media types. | |||||
"Work" shall mean the work of authorship, whether in Source or | |||||
Object form, made available under the License, as indicated by a | |||||
copyright notice that is included in or attached to the work | |||||
(an example is provided in the Appendix below). | |||||
"Derivative Works" shall mean any work, whether in Source or Object | |||||
form, that is based on (or derived from) the Work and for which the | |||||
editorial revisions, annotations, elaborations, or other modifications | |||||
represent, as a whole, an original work of authorship. For the purposes | |||||
of this License, Derivative Works shall not include works that remain | |||||
separable from, or merely link (or bind by name) to the interfaces of, | |||||
the Work and Derivative Works thereof. | |||||
"Contribution" shall mean any work of authorship, including | |||||
the original version of the Work and any modifications or additions | |||||
to that Work or Derivative Works thereof, that is intentionally | |||||
submitted to Licensor for inclusion in the Work by the copyright owner | |||||
or by an individual or Legal Entity authorized to submit on behalf of | |||||
the copyright owner. For the purposes of this definition, "submitted" | |||||
means any form of electronic, verbal, or written communication sent | |||||
to the Licensor or its representatives, including but not limited to | |||||
communication on electronic mailing lists, source code control systems, | |||||
and issue tracking systems that are managed by, or on behalf of, the | |||||
Licensor for the purpose of discussing and improving the Work, but | |||||
excluding communication that is conspicuously marked or otherwise | |||||
designated in writing by the copyright owner as "Not a Contribution." | |||||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||||
on behalf of whom a Contribution has been received by Licensor and | |||||
subsequently incorporated within the Work. | |||||
2. Grant of Copyright License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
copyright license to reproduce, prepare Derivative Works of, | |||||
publicly display, publicly perform, sublicense, and distribute the | |||||
Work and such Derivative Works in Source or Object form. | |||||
3. Grant of Patent License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
(except as stated in this section) patent license to make, have made, | |||||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||||
where such license applies only to those patent claims licensable | |||||
by such Contributor that are necessarily infringed by their | |||||
Contribution(s) alone or by combination of their Contribution(s) | |||||
with the Work to which such Contribution(s) was submitted. If You | |||||
institute patent litigation against any entity (including a | |||||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||||
or a Contribution incorporated within the Work constitutes direct | |||||
or contributory patent infringement, then any patent licenses | |||||
granted to You under this License for that Work shall terminate | |||||
as of the date such litigation is filed. | |||||
4. Redistribution. You may reproduce and distribute copies of the | |||||
Work or Derivative Works thereof in any medium, with or without | |||||
modifications, and in Source or Object form, provided that You | |||||
meet the following conditions: | |||||
(a) You must give any other recipients of the Work or | |||||
Derivative Works a copy of this License; and | |||||
(b) You must cause any modified files to carry prominent notices | |||||
stating that You changed the files; and | |||||
(c) You must retain, in the Source form of any Derivative Works | |||||
that You distribute, all copyright, patent, trademark, and | |||||
attribution notices from the Source form of the Work, | |||||
excluding those notices that do not pertain to any part of | |||||
the Derivative Works; and | |||||
(d) If the Work includes a "NOTICE" text file as part of its | |||||
distribution, then any Derivative Works that You distribute must | |||||
include a readable copy of the attribution notices contained | |||||
within such NOTICE file, excluding those notices that do not | |||||
pertain to any part of the Derivative Works, in at least one | |||||
of the following places: within a NOTICE text file distributed | |||||
as part of the Derivative Works; within the Source form or | |||||
documentation, if provided along with the Derivative Works; or, | |||||
within a display generated by the Derivative Works, if and | |||||
wherever such third-party notices normally appear. The contents | |||||
of the NOTICE file are for informational purposes only and | |||||
do not modify the License. You may add Your own attribution | |||||
notices within Derivative Works that You distribute, alongside | |||||
or as an addendum to the NOTICE text from the Work, provided | |||||
that such additional attribution notices cannot be construed | |||||
as modifying the License. | |||||
You may add Your own copyright statement to Your modifications and | |||||
may provide additional or different license terms and conditions | |||||
for use, reproduction, or distribution of Your modifications, or | |||||
for any such Derivative Works as a whole, provided Your use, | |||||
reproduction, and distribution of the Work otherwise complies with | |||||
the conditions stated in this License. | |||||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||||
any Contribution intentionally submitted for inclusion in the Work | |||||
by You to the Licensor shall be under the terms and conditions of | |||||
this License, without any additional terms or conditions. | |||||
Notwithstanding the above, nothing herein shall supersede or modify | |||||
the terms of any separate license agreement you may have executed | |||||
with Licensor regarding such Contributions. | |||||
6. Trademarks. This License does not grant permission to use the trade | |||||
names, trademarks, service marks, or product names of the Licensor, | |||||
except as required for reasonable and customary use in describing the | |||||
origin of the Work and reproducing the content of the NOTICE file. | |||||
7. Disclaimer of Warranty. Unless required by applicable law or | |||||
agreed to in writing, Licensor provides the Work (and each | |||||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
implied, including, without limitation, any warranties or conditions | |||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||||
appropriateness of using or redistributing the Work and assume any | |||||
risks associated with Your exercise of permissions under this License. | |||||
8. Limitation of Liability. In no event and under no legal theory, | |||||
whether in tort (including negligence), contract, or otherwise, | |||||
unless required by applicable law (such as deliberate and grossly | |||||
negligent acts) or agreed to in writing, shall any Contributor be | |||||
liable to You for damages, including any direct, indirect, special, | |||||
incidental, or consequential damages of any character arising as a | |||||
result of this License or out of the use or inability to use the | |||||
Work (including but not limited to damages for loss of goodwill, | |||||
work stoppage, computer failure or malfunction, or any and all | |||||
other commercial damages or losses), even if such Contributor | |||||
has been advised of the possibility of such damages. | |||||
9. Accepting Warranty or Additional Liability. While redistributing | |||||
the Work or Derivative Works thereof, You may choose to offer, | |||||
and charge a fee for, acceptance of support, warranty, indemnity, | |||||
or other liability obligations and/or rights consistent with this | |||||
License. However, in accepting such obligations, You may act only | |||||
on Your own behalf and on Your sole responsibility, not on behalf | |||||
of any other Contributor, and only if You agree to indemnify, | |||||
defend, and hold each Contributor harmless for any liability | |||||
incurred by, or claims asserted against, such Contributor by reason | |||||
of your accepting any such warranty or additional liability. | |||||
END OF TERMS AND CONDITIONS | |||||
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. |
@ -0,0 +1,49 @@ | |||||
# TMLIBS | |||||
This repo is a home for various small packages. | |||||
## autofile | |||||
Autofile is file access with automatic log rotation. A group of files is maintained and rotation happens | |||||
when the leading file gets too big. Provides a reader for reading from the file group. | |||||
## cli | |||||
CLI wraps the `cobra` and `viper` packages and handles some common elements of building a CLI like flags and env vars for the home directory and the logger. | |||||
## clist | |||||
Clist provides a linekd list that is safe for concurrent access by many readers. | |||||
## common | |||||
Common provides a hodgepodge of useful functions. | |||||
## db | |||||
DB provides a database interface and a number of implementions, including ones using an in-memory map, the filesystem directory structure, | |||||
an implemention of LevelDB in Go, and the official LevelDB in C. | |||||
## events | |||||
Events is a synchronous PubSub package. | |||||
## flowrate | |||||
Flowrate is a fork of https://github.com/mxk/go-flowrate that added a `SetREMA` method. | |||||
## log | |||||
Log is a log package structured around key-value pairs that allows logging level to be set differently for different keys. | |||||
## merkle | |||||
Merkle provides a simple static merkle tree and corresponding proofs. | |||||
## process | |||||
Process is a simple utility for spawning OS processes. | |||||
## pubsub | |||||
PubSub is an asynchronous PubSub package. |
@ -0,0 +1,14 @@ | |||||
package common | |||||
import ( | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func TestIntInSlice(t *testing.T) { | |||||
assert.True(t, IntInSlice(1, []int{1, 2, 3})) | |||||
assert.False(t, IntInSlice(4, []int{1, 2, 3})) | |||||
assert.True(t, IntInSlice(0, []int{0})) | |||||
assert.False(t, IntInSlice(0, []int{})) | |||||
} |
@ -0,0 +1,120 @@ | |||||
package common | |||||
import ( | |||||
"bytes" | |||||
"encoding/json" | |||||
"fmt" | |||||
"io" | |||||
mrand "math/rand" | |||||
"sync" | |||||
"testing" | |||||
"time" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func TestRandStr(t *testing.T) { | |||||
l := 243 | |||||
s := RandStr(l) | |||||
assert.Equal(t, l, len(s)) | |||||
} | |||||
func TestRandBytes(t *testing.T) { | |||||
l := 243 | |||||
b := RandBytes(l) | |||||
assert.Equal(t, l, len(b)) | |||||
} | |||||
func TestRandIntn(t *testing.T) { | |||||
n := 243 | |||||
for i := 0; i < 100; i++ { | |||||
x := RandIntn(n) | |||||
assert.True(t, x < n) | |||||
} | |||||
} | |||||
// It is essential that these tests run and never repeat their outputs | |||||
// lest we've been pwned and the behavior of our randomness is controlled. | |||||
// See Issues: | |||||
// * https://github.com/tendermint/tmlibs/issues/99 | |||||
// * https://github.com/tendermint/tendermint/issues/973 | |||||
func TestUniqueRng(t *testing.T) { | |||||
buf := new(bytes.Buffer) | |||||
outputs := make(map[string][]int) | |||||
for i := 0; i < 100; i++ { | |||||
testThemAll(buf) | |||||
output := buf.String() | |||||
buf.Reset() | |||||
runs, seen := outputs[output] | |||||
if seen { | |||||
t.Errorf("Run #%d's output was already seen in previous runs: %v", i, runs) | |||||
} | |||||
outputs[output] = append(outputs[output], i) | |||||
} | |||||
} | |||||
func testThemAll(out io.Writer) { | |||||
// Reset the internal PRNG | |||||
reset() | |||||
// Set math/rand's Seed so that any direct invocations | |||||
// of math/rand will reveal themselves. | |||||
mrand.Seed(1) | |||||
perm := RandPerm(10) | |||||
blob, _ := json.Marshal(perm) | |||||
fmt.Fprintf(out, "perm: %s\n", blob) | |||||
fmt.Fprintf(out, "randInt: %d\n", RandInt()) | |||||
fmt.Fprintf(out, "randUint: %d\n", RandUint()) | |||||
fmt.Fprintf(out, "randIntn: %d\n", RandIntn(97)) | |||||
fmt.Fprintf(out, "randInt31: %d\n", RandInt31()) | |||||
fmt.Fprintf(out, "randInt32: %d\n", RandInt32()) | |||||
fmt.Fprintf(out, "randInt63: %d\n", RandInt63()) | |||||
fmt.Fprintf(out, "randInt64: %d\n", RandInt64()) | |||||
fmt.Fprintf(out, "randUint32: %d\n", RandUint32()) | |||||
fmt.Fprintf(out, "randUint64: %d\n", RandUint64()) | |||||
fmt.Fprintf(out, "randUint16Exp: %d\n", RandUint16Exp()) | |||||
fmt.Fprintf(out, "randUint32Exp: %d\n", RandUint32Exp()) | |||||
fmt.Fprintf(out, "randUint64Exp: %d\n", RandUint64Exp()) | |||||
} | |||||
func TestRngConcurrencySafety(t *testing.T) { | |||||
var wg sync.WaitGroup | |||||
for i := 0; i < 100; i++ { | |||||
wg.Add(1) | |||||
go func() { | |||||
defer wg.Done() | |||||
_ = RandUint64() | |||||
<-time.After(time.Millisecond * time.Duration(RandIntn(100))) | |||||
_ = RandPerm(3) | |||||
}() | |||||
} | |||||
wg.Wait() | |||||
} | |||||
func BenchmarkRandBytes10B(b *testing.B) { | |||||
benchmarkRandBytes(b, 10) | |||||
} | |||||
func BenchmarkRandBytes100B(b *testing.B) { | |||||
benchmarkRandBytes(b, 100) | |||||
} | |||||
func BenchmarkRandBytes1KiB(b *testing.B) { | |||||
benchmarkRandBytes(b, 1024) | |||||
} | |||||
func BenchmarkRandBytes10KiB(b *testing.B) { | |||||
benchmarkRandBytes(b, 10*1024) | |||||
} | |||||
func BenchmarkRandBytes100KiB(b *testing.B) { | |||||
benchmarkRandBytes(b, 100*1024) | |||||
} | |||||
func BenchmarkRandBytes1MiB(b *testing.B) { | |||||
benchmarkRandBytes(b, 1024*1024) | |||||
} | |||||
func benchmarkRandBytes(b *testing.B, n int) { | |||||
for i := 0; i < b.N; i++ { | |||||
_ = RandBytes(n) | |||||
} | |||||
b.ReportAllocs() | |||||
} |
@ -0,0 +1,92 @@ | |||||
package common | |||||
import ( | |||||
"testing" | |||||
"time" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func TestDefaultTicker(t *testing.T) { | |||||
ticker := defaultTickerMaker(time.Millisecond * 10) | |||||
<-ticker.Chan() | |||||
ticker.Stop() | |||||
} | |||||
func TestRepeat(t *testing.T) { | |||||
ch := make(chan time.Time, 100) | |||||
lt := time.Time{} // zero time is year 1 | |||||
// tick fires `cnt` times for each second. | |||||
tick := func(cnt int) { | |||||
for i := 0; i < cnt; i++ { | |||||
lt = lt.Add(time.Second) | |||||
ch <- lt | |||||
} | |||||
} | |||||
// tock consumes Ticker.Chan() events `cnt` times. | |||||
tock := func(t *testing.T, rt *RepeatTimer, cnt int) { | |||||
for i := 0; i < cnt; i++ { | |||||
timeout := time.After(time.Second * 10) | |||||
select { | |||||
case <-rt.Chan(): | |||||
case <-timeout: | |||||
panic("expected RepeatTimer to fire") | |||||
} | |||||
} | |||||
done := true | |||||
select { | |||||
case <-rt.Chan(): | |||||
done = false | |||||
default: | |||||
} | |||||
assert.True(t, done) | |||||
} | |||||
tm := NewLogicalTickerMaker(ch) | |||||
dur := time.Duration(10 * time.Millisecond) // less than a second | |||||
rt := NewRepeatTimerWithTickerMaker("bar", dur, tm) | |||||
// Start at 0. | |||||
tock(t, rt, 0) | |||||
tick(1) // init time | |||||
tock(t, rt, 0) | |||||
tick(1) // wait 1 periods | |||||
tock(t, rt, 1) | |||||
tick(2) // wait 2 periods | |||||
tock(t, rt, 2) | |||||
tick(3) // wait 3 periods | |||||
tock(t, rt, 3) | |||||
tick(4) // wait 4 periods | |||||
tock(t, rt, 4) | |||||
// Multiple resets leads to no firing. | |||||
for i := 0; i < 20; i++ { | |||||
time.Sleep(time.Millisecond) | |||||
rt.Reset() | |||||
} | |||||
// After this, it works as new. | |||||
tock(t, rt, 0) | |||||
tick(1) // init time | |||||
tock(t, rt, 0) | |||||
tick(1) // wait 1 periods | |||||
tock(t, rt, 1) | |||||
tick(2) // wait 2 periods | |||||
tock(t, rt, 2) | |||||
tick(3) // wait 3 periods | |||||
tock(t, rt, 3) | |||||
tick(4) // wait 4 periods | |||||
tock(t, rt, 4) | |||||
// After a stop, nothing more is sent. | |||||
rt.Stop() | |||||
tock(t, rt, 0) | |||||
// Another stop panics. | |||||
assert.Panics(t, func() { rt.Stop() }) | |||||
} |
@ -0,0 +1,14 @@ | |||||
package common | |||||
import ( | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func TestStringInSlice(t *testing.T) { | |||||
assert.True(t, StringInSlice("a", []string{"a", "b", "c"})) | |||||
assert.False(t, StringInSlice("d", []string{"a", "b", "c"})) | |||||
assert.True(t, StringInSlice("", []string{""})) | |||||
assert.False(t, StringInSlice("", []string{})) | |||||
} |
@ -0,0 +1,78 @@ | |||||
package common | |||||
import ( | |||||
"sync" | |||||
"testing" | |||||
"time" | |||||
// make govet noshadow happy... | |||||
asrt "github.com/stretchr/testify/assert" | |||||
) | |||||
type thCounter struct { | |||||
input chan struct{} | |||||
mtx sync.Mutex | |||||
count int | |||||
} | |||||
func (c *thCounter) Increment() { | |||||
c.mtx.Lock() | |||||
c.count++ | |||||
c.mtx.Unlock() | |||||
} | |||||
func (c *thCounter) Count() int { | |||||
c.mtx.Lock() | |||||
val := c.count | |||||
c.mtx.Unlock() | |||||
return val | |||||
} | |||||
// Read should run in a go-routine and | |||||
// updates count by one every time a packet comes in | |||||
func (c *thCounter) Read() { | |||||
for range c.input { | |||||
c.Increment() | |||||
} | |||||
} | |||||
func TestThrottle(test *testing.T) { | |||||
assert := asrt.New(test) | |||||
ms := 50 | |||||
delay := time.Duration(ms) * time.Millisecond | |||||
longwait := time.Duration(2) * delay | |||||
t := NewThrottleTimer("foo", delay) | |||||
// start at 0 | |||||
c := &thCounter{input: t.Ch} | |||||
assert.Equal(0, c.Count()) | |||||
go c.Read() | |||||
// waiting does nothing | |||||
time.Sleep(longwait) | |||||
assert.Equal(0, c.Count()) | |||||
// send one event adds one | |||||
t.Set() | |||||
time.Sleep(longwait) | |||||
assert.Equal(1, c.Count()) | |||||
// send a burst adds one | |||||
for i := 0; i < 5; i++ { | |||||
t.Set() | |||||
} | |||||
time.Sleep(longwait) | |||||
assert.Equal(2, c.Count()) | |||||
// send 12, over 2 delay sections, adds 3 | |||||
short := time.Duration(ms/5) * time.Millisecond | |||||
for i := 0; i < 13; i++ { | |||||
t.Set() | |||||
time.Sleep(short) | |||||
} | |||||
time.Sleep(longwait) | |||||
assert.Equal(5, c.Count()) | |||||
close(t.Ch) | |||||
} |
@ -1,78 +0,0 @@ | |||||
// DEPRECATED! Use newer log package. | |||||
package logger | |||||
import ( | |||||
"os" | |||||
"github.com/tendermint/log15" | |||||
. "github.com/tendermint/tmlibs/common" | |||||
) | |||||
var mainHandler log15.Handler | |||||
var bypassHandler log15.Handler | |||||
func init() { | |||||
resetWithLogLevel("debug") | |||||
} | |||||
func SetLogLevel(logLevel string) { | |||||
resetWithLogLevel(logLevel) | |||||
} | |||||
func resetWithLogLevel(logLevel string) { | |||||
// main handler | |||||
//handlers := []log15.Handler{} | |||||
mainHandler = log15.LvlFilterHandler( | |||||
getLevel(logLevel), | |||||
log15.StreamHandler(os.Stdout, log15.TerminalFormat()), | |||||
) | |||||
//handlers = append(handlers, mainHandler) | |||||
// bypass handler for not filtering on global logLevel. | |||||
bypassHandler = log15.StreamHandler(os.Stdout, log15.TerminalFormat()) | |||||
//handlers = append(handlers, bypassHandler) | |||||
// By setting handlers on the root, we handle events from all loggers. | |||||
log15.Root().SetHandler(mainHandler) | |||||
} | |||||
// See go-wire/log for an example of usage. | |||||
func MainHandler() log15.Handler { | |||||
return mainHandler | |||||
} | |||||
func New(ctx ...interface{}) log15.Logger { | |||||
return NewMain(ctx...) | |||||
} | |||||
func BypassHandler() log15.Handler { | |||||
return bypassHandler | |||||
} | |||||
func NewMain(ctx ...interface{}) log15.Logger { | |||||
return log15.Root().New(ctx...) | |||||
} | |||||
func NewBypass(ctx ...interface{}) log15.Logger { | |||||
bypass := log15.New(ctx...) | |||||
bypass.SetHandler(bypassHandler) | |||||
return bypass | |||||
} | |||||
func getLevel(lvlString string) log15.Lvl { | |||||
lvl, err := log15.LvlFromString(lvlString) | |||||
if err != nil { | |||||
Exit(Fmt("Invalid log level %v: %v", lvlString, err)) | |||||
} | |||||
return lvl | |||||
} | |||||
//---------------------------------------- | |||||
// Exported from log15 | |||||
var LvlFilterHandler = log15.LvlFilterHandler | |||||
var LvlDebug = log15.LvlDebug | |||||
var LvlInfo = log15.LvlInfo | |||||
var LvlNotice = log15.LvlNotice | |||||
var LvlWarn = log15.LvlWarn | |||||
var LvlError = log15.LvlError |
@ -1,76 +0,0 @@ | |||||
package process | |||||
import ( | |||||
"fmt" | |||||
"io" | |||||
"os" | |||||
"os/exec" | |||||
"time" | |||||
) | |||||
type Process struct { | |||||
Label string | |||||
ExecPath string | |||||
Args []string | |||||
Pid int | |||||
StartTime time.Time | |||||
EndTime time.Time | |||||
Cmd *exec.Cmd `json:"-"` | |||||
ExitState *os.ProcessState `json:"-"` | |||||
InputFile io.Reader `json:"-"` | |||||
OutputFile io.WriteCloser `json:"-"` | |||||
WaitCh chan struct{} `json:"-"` | |||||
} | |||||
// execPath: command name | |||||
// args: args to command. (should not include name) | |||||
func StartProcess(label string, dir string, execPath string, args []string, inFile io.Reader, outFile io.WriteCloser) (*Process, error) { | |||||
cmd := exec.Command(execPath, args...) | |||||
cmd.Dir = dir | |||||
cmd.Stdout = outFile | |||||
cmd.Stderr = outFile | |||||
cmd.Stdin = inFile | |||||
if err := cmd.Start(); err != nil { | |||||
return nil, err | |||||
} | |||||
proc := &Process{ | |||||
Label: label, | |||||
ExecPath: execPath, | |||||
Args: args, | |||||
Pid: cmd.Process.Pid, | |||||
StartTime: time.Now(), | |||||
Cmd: cmd, | |||||
ExitState: nil, | |||||
InputFile: inFile, | |||||
OutputFile: outFile, | |||||
WaitCh: make(chan struct{}), | |||||
} | |||||
go func() { | |||||
err := proc.Cmd.Wait() | |||||
if err != nil { | |||||
// fmt.Printf("Process exit: %v\n", err) | |||||
if exitError, ok := err.(*exec.ExitError); ok { | |||||
proc.ExitState = exitError.ProcessState | |||||
} | |||||
} | |||||
proc.ExitState = proc.Cmd.ProcessState | |||||
proc.EndTime = time.Now() // TODO make this goroutine-safe | |||||
err = proc.OutputFile.Close() | |||||
if err != nil { | |||||
fmt.Printf("Error closing output file for %v: %v\n", proc.Label, err) | |||||
} | |||||
close(proc.WaitCh) | |||||
}() | |||||
return proc, nil | |||||
} | |||||
func (proc *Process) StopProcess(kill bool) error { | |||||
defer proc.OutputFile.Close() | |||||
if kill { | |||||
// fmt.Printf("Killing process %v\n", proc.Cmd.Process) | |||||
return proc.Cmd.Process.Kill() | |||||
} else { | |||||
// fmt.Printf("Stopping process %v\n", proc.Cmd.Process) | |||||
return proc.Cmd.Process.Signal(os.Interrupt) | |||||
} | |||||
} |
@ -1,22 +0,0 @@ | |||||
package process | |||||
import ( | |||||
. "github.com/tendermint/tmlibs/common" | |||||
) | |||||
// Runs a command and gets the result. | |||||
func Run(dir string, command string, args []string) (string, bool, error) { | |||||
outFile := NewBufferCloser(nil) | |||||
proc, err := StartProcess("", dir, command, args, nil, outFile) | |||||
if err != nil { | |||||
return "", false, err | |||||
} | |||||
<-proc.WaitCh | |||||
if proc.ExitState.Success() { | |||||
return outFile.String(), true, nil | |||||
} else { | |||||
return outFile.String(), false, nil | |||||
} | |||||
} |
@ -1,12 +1,15 @@ | |||||
#!/usr/bin/env bash | #!/usr/bin/env bash | ||||
set -e | set -e | ||||
echo "" > coverage.txt | |||||
# run the linter | |||||
# make metalinter_test | |||||
# run the unit tests with coverage | |||||
echo "" > coverage.txt | |||||
for d in $(go list ./... | grep -v vendor); do | for d in $(go list ./... | grep -v vendor); do | ||||
go test -race -coverprofile=profile.out -covermode=atomic "$d" | |||||
if [ -f profile.out ]; then | |||||
cat profile.out >> coverage.txt | |||||
rm profile.out | |||||
fi | |||||
go test -race -coverprofile=profile.out -covermode=atomic "$d" | |||||
if [ -f profile.out ]; then | |||||
cat profile.out >> coverage.txt | |||||
rm profile.out | |||||
fi | |||||
done | done |
@ -1,3 +1,3 @@ | |||||
package version | package version | ||||
const Version = "0.4.0" | |||||
const Version = "0.6.0" |