Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Kirill Smelkov
neo
Commits
f3effa6c
Commit
f3effa6c
authored
Jan 03, 2021
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
X move tracetest to go123
go123@c8d9907e
parent
9d07a6b6
Changes
6
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
2 additions
and
1082 deletions
+2
-1082
go/internal/xtracing/tracetest/chan.go
go/internal/xtracing/tracetest/chan.go
+0
-156
go/internal/xtracing/tracetest/example_test.go
go/internal/xtracing/tracetest/example_test.go
+0
-341
go/internal/xtracing/tracetest/tracetest.go
go/internal/xtracing/tracetest/tracetest.go
+0
-516
go/internal/xtracing/tracetest/ztrace_x_test.go
go/internal/xtracing/tracetest/ztrace_x_test.go
+0
-66
go/neo/neo_test.go
go/neo/neo_test.go
+1
-2
go/neo/t_cluster_test.go
go/neo/t_cluster_test.go
+1
-1
No files found.
go/internal/xtracing/tracetest/chan.go
deleted
100644 → 0
View file @
9d07a6b6
// Copyright (C) 2017-2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package
tracetest
// synchronous channels.
import
(
"errors"
"flag"
"fmt"
"reflect"
"time"
)
var
(
chatty
=
flag
.
Bool
(
"tracetest.v"
,
false
,
"verbose: print events as they are sent on trace channels"
)
deadTime
=
flag
.
Duration
(
"tracetest.deadtime"
,
3
*
time
.
Second
,
"time after which no events activity is considered to be a deadlock"
)
)
// _Msg represents message with 1 event sent over _chan.
//
// The goroutine which sent the message will wait for Ack before continue.
type
_Msg
struct
{
Event
interface
{}
ack
chan
<-
error
// nil on Ack; !nil on nak
}
// _chan provides synchronous channel associated with a stream.
//
// It comes with additional property that send blocks until receiving side
// explicitly acknowledges message was received and processed.
//
// New channels must be created via T.newChan.
//
// It is safe to use _chan from multiple goroutines simultaneously.
type
_chan
struct
{
t
*
T
// created for stream <.name> under <.t>
name
string
// name of the channel/stream
msgq
chan
*
_Msg
down
chan
struct
{}
// becomes ready when closed
}
// Send sends event to a consumer and waits for ack.
// if main testing goroutine detects any problem Send panics.
func
(
ch
*
_chan
)
Send
(
event
interface
{})
{
if
*
chatty
{
fmt
.
Printf
(
"%s <- %T %v
\n
"
,
ch
.
name
,
event
,
event
)
}
ack
:=
make
(
chan
error
)
select
{
case
<-
ch
.
down
:
ch
.
t
.
fatalfInNonMain
(
"%s: send: channel was closed"
,
ch
.
name
)
case
ch
.
msgq
<-
&
_Msg
{
event
,
ack
}
:
err
:=
<-
ack
if
err
!=
nil
{
ch
.
t
.
fatalfInNonMain
(
"%s: send: %s"
,
ch
.
name
,
err
)
}
}
}
// Close closes the sending side of the channel.
func
(
ch
*
_chan
)
Close
()
{
close
(
ch
.
down
)
// note - not .msgq
}
// Recv receives message from a producer.
//
// The consumer, after dealing with the message, must send back an ack.
// Must be called from main testing thread.
func
(
ch
*
_chan
)
Recv
()
*
_Msg
{
t
:=
ch
.
t
;
t
.
Helper
()
msg
:=
ch
.
recv
()
if
msg
==
nil
{
t
.
Fatalf
(
"%s: recv: deadlock
\n
"
,
ch
.
name
)
}
return
msg
}
// RecvInto receives message from a producer, verifies that event type is the
// same as type of *event, and saves received event there.
//
// Must be called from main testing thread.
func
(
ch
*
_chan
)
RecvInto
(
eventp
interface
{})
*
_Msg
{
t
:=
ch
.
t
;
t
.
Helper
()
msg
:=
ch
.
recv
()
if
msg
==
nil
{
t
.
Fatalf
(
"%s: recv: deadlock waiting for %T
\n
"
,
ch
.
name
,
eventp
)
}
reventp
:=
reflect
.
ValueOf
(
eventp
)
if
reventp
.
Type
()
.
Elem
()
!=
reflect
.
TypeOf
(
msg
.
Event
)
{
t
.
queuenak
(
msg
,
"unexpected event type"
)
t
.
Fatalf
(
"%s: expect: %s: got %T %v"
,
ch
.
name
,
reventp
.
Elem
()
.
Type
(),
msg
.
Event
,
msg
.
Event
)
}
// *eventp = msg.Event
reventp
.
Elem
()
.
Set
(
reflect
.
ValueOf
(
msg
.
Event
))
return
msg
}
func
(
ch
*
_chan
)
recv
()
*
_Msg
{
select
{
case
msg
:=
<-
ch
.
msgq
:
return
msg
// ok
case
<-
time
.
After
(
*
deadTime
)
:
return
nil
// deadlock
}
}
// Ack acknowledges the event was processed and unblocks producer goroutine.
func
(
m
*
_Msg
)
Ack
()
{
m
.
ack
<-
nil
}
// nak tells sender that event verification failed and why.
// it is called only by tracetest internals.
func
(
m
*
_Msg
)
nak
(
why
string
)
{
m
.
ack
<-
errors
.
New
(
why
)
}
// nak represents scheduled call to `msg.nak(why)`.
type
nak
struct
{
msg
*
_Msg
why
string
}
// queuenak schedules call to `msg.nak(why)`.
func
(
t
*
T
)
queuenak
(
msg
*
_Msg
,
why
string
)
{
t
.
nakq
=
append
(
t
.
nakq
,
nak
{
msg
,
why
})
}
// newChan creates new _chan channel.
func
(
t
*
T
)
newChan
(
name
string
)
*
_chan
{
// NOTE T ensures not to create channels with duplicate names.
return
&
_chan
{
t
:
t
,
name
:
name
,
msgq
:
make
(
chan
*
_Msg
),
down
:
make
(
chan
struct
{})}
}
go/internal/xtracing/tracetest/example_test.go
deleted
100644 → 0
View file @
9d07a6b6
// Copyright (C) 2018-2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package tracetest_test demonstrates how to use package tracetest.
//
// It also serves as set of testcases for tracetest itself.
package
tracetest_test
//go:generate gotrace gen .
import
(
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"sync"
"testing"
"time"
"lab.nexedi.com/kirr/go123/tracing"
"lab.nexedi.com/kirr/neo/go/internal/xtracing/tracetest"
)
// hi and hello are functions that emit "(Hi|Hello), <who>" and can be traced.
//trace:event traceHi(who string)
//trace:event traceHello(who string)
func
hi
(
who
string
)
{
traceHi
(
who
)
fmt
.
Println
(
"Hi,"
,
who
)
}
func
hello
(
who
string
)
{
traceHello
(
who
)
fmt
.
Println
(
"Hello,"
,
who
)
}
// we use tracing to attach probes to hi and hello, and emit corresponding
// eventHi and eventHello to tracetest.T from there.
type
eventHi
string
type
eventHello
string
func
setupTracing
(
t
*
tracetest
.
T
)
*
tracing
.
ProbeGroup
{
pg
:=
&
tracing
.
ProbeGroup
{}
tracing
.
Lock
()
traceHi_Attach
(
pg
,
func
(
who
string
)
{
t
.
RxEvent
(
eventHi
(
who
))
})
traceHello_Attach
(
pg
,
func
(
who
string
)
{
t
.
RxEvent
(
eventHello
(
who
))
})
tracing
.
Unlock
()
// NOTE pg.Done must be invoked by caller when setup tracing is no longer needed.
return
pg
}
// routeEvent tells to which stream an event should go.
// Here, in example, we use the convention that who comes as "<threadID>·..."
// and we route the event to stream that corresponds to threadID.
func
routeEvent
(
event
interface
{})
(
stream
string
)
{
who
:=
""
switch
ev
:=
event
.
(
type
)
{
default
:
panic
(
fmt
.
Sprintf
(
"unexpected event type %T"
,
event
))
case
eventHi
:
who
=
string
(
ev
)
case
eventHello
:
who
=
string
(
ev
)
}
i
:=
strings
.
Index
(
who
,
"·"
)
if
i
==
-
1
{
panic
(
fmt
.
Sprintf
(
"who does not have threadID: %q"
,
who
))
}
return
strings
.
ToLower
(
who
[
:
i
])
}
// verify calls tracetest.Verify on f with first preparing tracing setup and events delivery.
// It also verifies that tracetest detects errors as expected.
func
verify
(
t
*
testing
.
T
,
f
func
(
t
*
tracetest
.
T
),
targvExtra
...
string
)
{
t
.
Helper
()
verifyInSubprocess
(
t
,
func
(
t
*
testing
.
T
)
{
tracetest
.
Verify
(
t
,
func
(
t
*
tracetest
.
T
)
{
// setup tracing to deliver trace events to t.
pg
:=
setupTracing
(
t
)
defer
pg
.
Done
()
// tell t to which stream an event should go.
t
.
SetEventRouter
(
routeEvent
)
// run test code
f
(
t
)
})
},
targvExtra
...
)
}
// Test2ThreadsOK demonstrates verifying 2 threads that execute independently.
// There is no concurrency problem here.
func
Test2ThreadsOK
(
t
*
testing
.
T
)
{
verify
(
t
,
func
(
t
*
tracetest
.
T
)
{
var
wg
sync
.
WaitGroup
defer
wg
.
Wait
()
wg
.
Add
(
2
)
go
func
()
{
// thread1
defer
wg
.
Done
()
hi
(
"T1·A"
)
hello
(
"T1·B"
)
}()
go
func
()
{
// thread2
defer
wg
.
Done
()
hello
(
"T2·C"
)
hi
(
"T2·D"
)
}()
// assert that events come as expected
// NOTE in checks t2 vs t1 order does not matter
t
.
Expect
(
"t2"
,
eventHello
(
"T2·C"
))
t
.
Expect
(
"t2"
,
eventHi
(
"T2·D"
))
t
.
Expect
(
"t1"
,
eventHi
(
"T1·A"
))
t
.
Expect
(
"t1"
,
eventHello
(
"T1·B"
))
})
}
// TestDeadlock demonstrates deadlock detection.
// XXX also test for wrong decomposition XXX or is it also covered by this test as well?
func
TestDeadlock
(
t
*
testing
.
T
)
{
verify
(
t
,
func
(
t
*
tracetest
.
T
)
{
var
wg
sync
.
WaitGroup
defer
wg
.
Wait
()
wg
.
Add
(
1
)
go
func
()
{
// thread1
defer
wg
.
Done
()
hi
(
"T1·A"
)
}()
// the checker expects something on stream "t2", but there is
// no event sent there -> deadlock.
t
.
Expect
(
"t2"
,
eventHi
(
"zzz"
))
},
"-tracetest.deadtime=0.5s"
)
}
// TestRace demonstrates detection of logical race.
func
TestRace
(
t
*
testing
.
T
)
{
verify
(
t
,
func
(
t
*
tracetest
.
T
)
{
var
wg
sync
.
WaitGroup
defer
wg
.
Wait
()
wg
.
Add
(
2
)
// 2 threads should synchronize with each other and do step A before B.
// They do not properly synchronize though, and just happen to
// usually emit events in expected order due to sleep in T2.
// Tracetest detects that.
go
func
()
{
// thread1
defer
wg
.
Done
()
hi
(
"x·A"
)
}()
go
func
()
{
// thread2
defer
wg
.
Done
()
time
.
Sleep
(
100
*
time
.
Millisecond
)
hi
(
"x·B"
)
}()
t
.
Expect
(
"x"
,
eventHi
(
"x·A"
))
t
.
Expect
(
"x"
,
eventHi
(
"x·B"
))
})
}
// other tests (mainly to verify tracetest itself)
// TestExpectType demonstrates Expect asserting with "unexpected event type".
func
TestExpectType
(
t
*
testing
.
T
)
{
verify
(
t
,
func
(
t
*
tracetest
.
T
)
{
var
wg
sync
.
WaitGroup
defer
wg
.
Wait
()
wg
.
Add
(
1
)
go
func
()
{
// thread 1
defer
wg
.
Done
()
hi
(
"T1·A"
)
}()
t
.
Expect
(
"t1"
,
eventHello
(
"T1·A"
))
})
}
// TestExpectValue demonstrates Expect asserting with "unexpected event value".
func
TestExpectValue
(
t
*
testing
.
T
)
{
verify
(
t
,
func
(
t
*
tracetest
.
T
)
{
var
wg
sync
.
WaitGroup
defer
wg
.
Wait
()
wg
.
Add
(
1
)
go
func
()
{
// thread 1
defer
wg
.
Done
()
hi
(
"T1·A"
)
}()
t
.
Expect
(
"t1"
,
eventHi
(
"T1·B"
))
})
}
// ----------------------------------------
// verifyInSubprocess runs f in subprocess and verifies that its output matches testExpectMap[t.Name].
func
verifyInSubprocess
(
t
*
testing
.
T
,
f
func
(
t
*
testing
.
T
),
targvExtra
...
string
)
{
t
.
Helper
()
if
os
.
Getenv
(
"TRACETEST_EX_VERIFY_IN_SUBPROCESS"
)
==
"1"
{
f
(
t
)
return
}
// spawn the test in subprocess and verify its output
expectOK
,
ok
:=
testExpectMap
[
t
.
Name
()]
if
!
ok
{
panic
(
fmt
.
Sprintf
(
"testExpectMap[%q] not defined"
,
t
.
Name
()))
}
outputOK
:=
regexp
.
QuoteMeta
(
expectOK
.
output
)
// empty line -> kind of "<BLANKLINE>"
for
{
__
:=
strings
.
ReplaceAll
(
outputOK
,
"
\n\n
"
,
"
\n\\
s*
\n
"
)
if
__
==
outputOK
{
break
}
outputOK
=
__
}
outputOK
=
strings
.
ReplaceAll
(
outputOK
,
"<TIME>"
,
".+s"
)
outputOK
=
strings
.
ReplaceAll
(
outputOK
,
"<LINE>"
,
"[0-9]+"
)
outputRe
:=
regexp
.
MustCompile
(
outputOK
)
argv
:=
[]
string
{
"-test.run="
+
t
.
Name
()}
argv
=
append
(
argv
,
targvExtra
...
)
cmd
:=
exec
.
Command
(
os
.
Args
[
0
],
argv
...
)
cmd
.
Env
=
append
(
os
.
Environ
(),
"TRACETEST_EX_VERIFY_IN_SUBPROCESS=1"
)
bout
,
err
:=
cmd
.
CombinedOutput
()
// NOTE `go test` itself combines everything to stdout only
out
:=
string
(
bout
)
ecode
:=
0
if
testing
.
Verbose
()
{
t
.
Logf
(
"stdout:
\n
%s
\n
"
,
out
)
}
if
err
!=
nil
{
e
,
ok
:=
err
.
(
*
exec
.
ExitError
)
if
!
ok
{
// e.g. could not respawn at all
t
.
Fatal
(
err
)
}
ecode
=
e
.
ExitCode
()
}
bad
:=
""
badf
:=
func
(
format
string
,
argv
...
interface
{})
{
bad
+=
fmt
.
Sprintf
(
format
+
"
\n
"
,
argv
...
)
}
if
ecode
!=
expectOK
.
exitCode
{
badf
(
"exit code: %d ; expected: %d"
,
ecode
,
expectOK
.
exitCode
)
}
if
!
outputRe
.
MatchString
(
out
)
{
badf
(
"unexpected output:
\n
%s
\n
want: ~
\n
%s
\n
"
,
out
,
expectOK
.
output
)
}
if
bad
!=
""
{
t
.
Fatal
(
bad
)
}
}
// testExpect describes what result to expect from a test.
type
testExpect
struct
{
exitCode
int
output
string
}
// testExpectMap maps <test name> -> testExpect.
var
testExpectMap
=
map
[
string
]
testExpect
{
"Test2ThreadsOK"
:
{
0
,
""
},
"TestDeadlock"
:
{
1
,
`--- FAIL: TestDeadlock (<TIME>)
example_test.go:157: t2: recv: deadlock waiting for *tracetest_test.eventHi
example_test.go:157: test shutdown: #streams: 2, #(pending events): 1
t1 <- tracetest_test.eventHi T1·A
# t2
chan.go:<LINE>: t1: send: canceled (test failed)
`
},
"TestRace"
:
{
1
,
` --- FAIL: TestRace/delay@0(=x:0) (<TIME>)
example_test.go:183: x: expect: tracetest_test.eventHi:
want: x·A
have: x·B
diff:
-"x·A"
+"x·B"
`
},
"TestExpectType"
:
{
1
,
`--- FAIL: TestExpectType (<TIME>)
example_test.go:203: t1: expect: tracetest_test.eventHello: got tracetest_test.eventHi T1·A
example_test.go:203: test shutdown: #streams: 1, #(pending events): 0
# t1
chan.go:<LINE>: t1: send: unexpected event type
`
},
"TestExpectValue"
:
{
1
,
`--- FAIL: TestExpectValue (<TIME>)
example_test.go:219: t1: expect: tracetest_test.eventHi:
want: T1·B
have: T1·A
diff:
-"T1·B"
+"T1·A"
example_test.go:219: test shutdown: #streams: 1, #(pending events): 0
# t1
chan.go:<LINE>: t1: send: unexpected event data
`
},
}
go/internal/xtracing/tracetest/tracetest.go
deleted
100644 → 0
View file @
9d07a6b6
This diff is collapsed.
Click to expand it.
go/internal/xtracing/tracetest/ztrace_x_test.go
deleted
100644 → 0
View file @
9d07a6b6
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
package
tracetest_test
// code generated for tracepoints
import
(
"lab.nexedi.com/kirr/go123/tracing"
"unsafe"
)
// traceevent: traceHello(who string)
type
_t_traceHello
struct
{
tracing
.
Probe
probefunc
func
(
who
string
)
}
var
_traceHello
*
_t_traceHello
func
traceHello
(
who
string
)
{
if
_traceHello
!=
nil
{
_traceHello_run
(
who
)
}
}
func
_traceHello_run
(
who
string
)
{
for
p
:=
_traceHello
;
p
!=
nil
;
p
=
(
*
_t_traceHello
)(
unsafe
.
Pointer
(
p
.
Next
()))
{
p
.
probefunc
(
who
)
}
}
func
traceHello_Attach
(
pg
*
tracing
.
ProbeGroup
,
probe
func
(
who
string
))
*
tracing
.
Probe
{
p
:=
_t_traceHello
{
probefunc
:
probe
}
tracing
.
AttachProbe
(
pg
,
(
**
tracing
.
Probe
)(
unsafe
.
Pointer
(
&
_traceHello
)),
&
p
.
Probe
)
return
&
p
.
Probe
}
// traceevent: traceHi(who string)
type
_t_traceHi
struct
{
tracing
.
Probe
probefunc
func
(
who
string
)
}
var
_traceHi
*
_t_traceHi
func
traceHi
(
who
string
)
{
if
_traceHi
!=
nil
{
_traceHi_run
(
who
)
}
}
func
_traceHi_run
(
who
string
)
{
for
p
:=
_traceHi
;
p
!=
nil
;
p
=
(
*
_t_traceHi
)(
unsafe
.
Pointer
(
p
.
Next
()))
{
p
.
probefunc
(
who
)
}
}
func
traceHi_Attach
(
pg
*
tracing
.
ProbeGroup
,
probe
func
(
who
string
))
*
tracing
.
Probe
{
p
:=
_t_traceHi
{
probefunc
:
probe
}
tracing
.
AttachProbe
(
pg
,
(
**
tracing
.
Probe
)(
unsafe
.
Pointer
(
&
_traceHi
)),
&
p
.
Probe
)
return
&
p
.
Probe
}
// trace export signature
func
_trace_exporthash_51bb0086e9435e499919853bae2dbb429f70d833
()
{}
go/neo/neo_test.go
View file @
f3effa6c
...
...
@@ -33,9 +33,8 @@ import (
"lab.nexedi.com/kirr/neo/go/neo/proto"
"lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/internal/xtracing/tracetest"
"lab.nexedi.com/kirr/go123/exc"
"lab.nexedi.com/kirr/go123/tracing/tracetest"
"lab.nexedi.com/kirr/go123/xsync"
)
...
...
go/neo/t_cluster_test.go
View file @
f3effa6c
...
...
@@ -26,6 +26,7 @@ import (
"sync"
"testing"
"lab.nexedi.com/kirr/go123/tracing/tracetest"
"lab.nexedi.com/kirr/go123/xnet"
"lab.nexedi.com/kirr/go123/xnet/lonet"
"lab.nexedi.com/kirr/go123/xnet/pipenet"
...
...
@@ -33,7 +34,6 @@ import (
"lab.nexedi.com/kirr/go123/xsync"
"lab.nexedi.com/kirr/neo/go/internal/xcontext"
"lab.nexedi.com/kirr/neo/go/internal/xtracing/tracetest"
"lab.nexedi.com/kirr/neo/go/neo/proto"
"lab.nexedi.com/kirr/neo/go/neo/storage"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment