@ -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 | |||
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 | |||
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 |
@ -1,3 +1,3 @@ | |||
package version | |||
const Version = "0.4.0" | |||
const Version = "0.6.0" |