Skip to content

Commit 941181e

Browse files
committed
timeit: replace flag with alecthomas/kong, test with testscript and gotest.tools
testscript sleepit
1 parent 58207e3 commit 941181e

File tree

20 files changed

+559
-559
lines changed

20 files changed

+559
-559
lines changed

CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Fix [#1](https://github.com/marco-m/timeit/issues/1) as non reproducible. In any case, added manual test configuration in testdata/terraform.
1313
- Fix tests about signal handling. Code was already correct, but tests were not.
1414

15-
### Changed, breaking
15+
### Breaking
16+
17+
- due to the introduction of package kong to parse the command-line, now `timeit` wants flags specified with two hyphens, for example `--ticker` instead of `-ticker`.
1618

1719
### Changed
1820

19-
- Upgrade to Go 1.16
21+
- Upgrade to Go 1.19
2022
- Upgrade dependencies
23+
- Integration tests are now driven by package [rogpeppe/go-internal/testscript](http://github.com/rogpeppe/go-internal).
2124

2225
### New
2326

@@ -43,7 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4346
Terminate immediately after N signals.
4447
Default is to terminate only when the cleanup phase has completed.
4548
```
46-
- the sleepit helper now has also a good series of tests.
49+
- The sleepit helper now has also a good series of tests.
4750
- Add CI: build and test with GitHub Actions, for platforms: Linux, macOS, Windows.
4851
- Add basic and experimental support for Windows. Untested: signal handling.
4952

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ example:
2828

2929
Time a command and print intermediate timings (color output by default):
3030

31-
$ timeit -ticker 30s sleep 60
32-
timeit ticker: running since 30.001s
33-
timeit ticker: running since 1m0.004s
31+
$ timeit --ticker 30s sleep 60
32+
timeit ticker: running for 30s
33+
timeit ticker: running for 1m
3434
timeit results:
3535
real: 1m0.005s
3636

3737
Check online if there is a more recent version:
3838

39-
$ timeit -check-version
39+
$ timeit --check-version
4040
installed version v0.2.1 is older than the latest version v0.3.0
4141
To upgrade visit https://github.com/marco-m/timeit
4242

Taskfile.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ vars:
77
sh: git describe --long --dirty --always
88
SHORT_VERSION:
99
sh: git describe --abbrev=0 --always
10-
LDFLAGS: -w -s -X main.fullVersion={{.FULL_VERSION}} -X main.shortVersion={{.SHORT_VERSION}}
10+
LDFLAGS: >
11+
-w -s
12+
-X github.com/marco-m/timeit/pkg/timeit.fullVersion={{.FULL_VERSION}}
13+
-X github.com/marco-m/timeit/pkg/timeit.shortVersion={{.SHORT_VERSION}}
1114
1215
tasks:
1316
default:
@@ -23,8 +26,6 @@ tasks:
2326
dir: bin
2427
cmds:
2528
- go build -v -ldflags="{{.LDFLAGS}}" ../cmd/timeit
26-
sources: [../cmd/timeit/*.go]
27-
generates: [timeit]
2829
sleepit:
2930
desc: Build the sleepit executable
3031
dir: bin

cmd/sleepit/main.go

Lines changed: 3 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,14 @@
11
// This code is released under the MIT License
2-
// Copyright (c) 2020 Marco Molteni and the timeit contributors.
2+
// Copyright (c) 2020-2023 Marco Molteni and the timeit contributors.
33

44
package main
55

66
import (
7-
"flag"
8-
"fmt"
97
"os"
10-
"os/signal"
11-
"time"
12-
)
13-
14-
const usage = `sleepit: sleep for the specified duration, optionally handling signals
15-
When the line "sleepit: ready" is printed, it means that it is safe to send signals to it
16-
17-
Usage: sleepit <command> [<args>]
188

19-
Commands
20-
21-
default Use default action: on reception of SIGINT terminate abruptly
22-
handle Handle signals: on reception of SIGINT perform cleanup before exiting
23-
version Show the sleepit version`
24-
25-
var (
26-
// Filled by the linker.
27-
fullVersion = "unknown" // example: v0.0.9-8-g941583d027-dirty
9+
"github.com/marco-m/timeit/pkg/sleepit"
2810
)
2911

3012
func main() {
31-
os.Exit(run(os.Args[1:]))
32-
}
33-
34-
func run(args []string) int {
35-
if len(args) < 1 {
36-
fmt.Fprintln(os.Stderr, usage)
37-
return 2
38-
}
39-
40-
defaultCmd := flag.NewFlagSet("default", flag.ExitOnError)
41-
defaultSleep := defaultCmd.Duration("sleep", 5*time.Second, "Sleep duration")
42-
43-
handleCmd := flag.NewFlagSet("handle", flag.ExitOnError)
44-
handleSleep := handleCmd.Duration("sleep", 5*time.Second, "Sleep duration")
45-
handleCleanup := handleCmd.Duration("cleanup", 5*time.Second, "Cleanup duration")
46-
handleTermAfter := handleCmd.Int("term-after", 0,
47-
"Terminate immediately after `N` signals.\n"+
48-
"Default is to terminate only when the cleanup phase has completed.")
49-
50-
versionCmd := flag.NewFlagSet("version", flag.ExitOnError)
51-
52-
switch args[0] {
53-
54-
case "default":
55-
defaultCmd.Parse(args[1:])
56-
if len(defaultCmd.Args()) > 0 {
57-
fmt.Fprintf(os.Stderr, "default: unexpected arguments: %v\n", defaultCmd.Args())
58-
return 2
59-
}
60-
return supervisor(*defaultSleep, 0, 0, nil)
61-
62-
case "handle":
63-
handleCmd.Parse(args[1:])
64-
if *handleTermAfter == 1 {
65-
fmt.Fprintf(os.Stderr, "handle: term-after cannot be 1\n")
66-
return 2
67-
}
68-
if len(handleCmd.Args()) > 0 {
69-
fmt.Fprintf(os.Stderr, "handle: unexpected arguments: %v\n", handleCmd.Args())
70-
return 2
71-
}
72-
sigCh := make(chan os.Signal, 1)
73-
signal.Notify(sigCh, os.Interrupt) // Ctrl-C -> SIGINT
74-
return supervisor(*handleSleep, *handleCleanup, *handleTermAfter, sigCh)
75-
76-
case "version":
77-
versionCmd.Parse(args[1:])
78-
if len(versionCmd.Args()) > 0 {
79-
fmt.Fprintf(os.Stderr, "version: unexpected arguments: %v\n", versionCmd.Args())
80-
return 2
81-
}
82-
fmt.Printf("sleepit version %s\n", fullVersion)
83-
return 0
84-
85-
default:
86-
fmt.Fprintln(os.Stderr, usage)
87-
return 2
88-
}
89-
}
90-
91-
func supervisor(
92-
sleep time.Duration,
93-
cleanup time.Duration,
94-
termAfter int,
95-
sigCh <-chan os.Signal,
96-
) int {
97-
fmt.Printf("sleepit: ready\n")
98-
fmt.Printf("sleepit: PID=%d sleep=%v cleanup=%v\n",
99-
os.Getpid(), sleep, cleanup)
100-
101-
cancelWork := make(chan struct{})
102-
workerDone := worker(cancelWork, sleep, "work")
103-
104-
cancelCleaner := make(chan struct{})
105-
var cleanerDone <-chan struct{}
106-
107-
sigCount := 0
108-
for {
109-
select {
110-
case sig := <-sigCh:
111-
sigCount++
112-
fmt.Printf("sleepit: got signal=%s count=%d\n", sig, sigCount)
113-
if sigCount == 1 {
114-
// since `cancelWork` is unbuffered, sending will be synchronous:
115-
// we are ensured that the worker has terminated before starting cleanup.
116-
// This is important in some real-life situations.
117-
cancelWork <- struct{}{}
118-
cleanerDone = worker(cancelCleaner, cleanup, "cleanup")
119-
}
120-
if sigCount == termAfter {
121-
cancelCleaner <- struct{}{}
122-
return 4
123-
}
124-
case <-workerDone:
125-
return 0
126-
case <-cleanerDone:
127-
return 3
128-
}
129-
}
130-
}
131-
132-
// Start a worker goroutine and return immediately a `workerDone` channel.
133-
// The goroutine will prepend its prints with the prefix `name`.
134-
// The goroutine will simulate some work and will terminate when one of the following
135-
// conditions happens:
136-
// 1. When `howlong` is elapsed. This case will be signaled on the `workerDone` channel.
137-
// 2. When something happens on channel `canceled`. Note that this simulates real-life,
138-
// so cancellation is not instantaneous: if the caller wants a synchronous cancel,
139-
// it should send a message; if instead it wants an asynchronous cancel, it should
140-
// close the channel.
141-
func worker(
142-
canceled <-chan struct{},
143-
howlong time.Duration,
144-
name string,
145-
) <-chan struct{} {
146-
workerDone := make(chan struct{})
147-
deadline := time.Now().Add(howlong)
148-
go func() {
149-
fmt.Printf("sleepit: %s started\n", name)
150-
for {
151-
select {
152-
case <-canceled:
153-
fmt.Printf("sleepit: %s canceled\n", name)
154-
return
155-
default:
156-
if doSomeWork(deadline) {
157-
fmt.Printf("sleepit: %s done\n", name) // <== NOTE THIS LINE
158-
workerDone <- struct{}{}
159-
return
160-
}
161-
}
162-
}
163-
}()
164-
return workerDone
165-
}
166-
167-
// Do some work and then return, so that the caller can decide wether to continue or not.
168-
// Return true when all work is done.
169-
func doSomeWork(deadline time.Time) bool {
170-
if time.Now().After(deadline) {
171-
return true
172-
}
173-
timeout := 100 * time.Millisecond
174-
time.Sleep(timeout)
175-
return false
13+
os.Exit(sleepit.Main())
17614
}

cmd/sleepit/main_test.go

Lines changed: 0 additions & 91 deletions
This file was deleted.

0 commit comments

Comments
 (0)