提交 5a9c4e60 编写于 作者: J Jeet Parekh

added neo4j and esv7 dependencies

上级 f63b06b9
Apache License
Version 2.0, January 2004
http://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
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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
http://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.
// Copyright 2010 Google Inc.
//
// 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
//
// http://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.
package gomock
import (
"fmt"
"reflect"
"strconv"
"strings"
)
// Call represents an expected call to a mock.
type Call struct {
t TestHelper // for triggering test failures on invalid call setup
receiver interface{} // the receiver of the method call
method string // the name of the method
methodType reflect.Type // the type of the method
args []Matcher // the args
origin string // file and line number of call setup
preReqs []*Call // prerequisite calls
// Expectations
minCalls, maxCalls int
numCalls int // actual number made
// actions are called when this Call is called. Each action gets the args and
// can set the return values by returning a non-nil slice. Actions run in the
// order they are created.
actions []func([]interface{}) []interface{}
}
// newCall creates a *Call. It requires the method type in order to support
// unexported methods.
func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
t.Helper()
// TODO: check arity, types.
margs := make([]Matcher, len(args))
for i, arg := range args {
if m, ok := arg.(Matcher); ok {
margs[i] = m
} else if arg == nil {
// Handle nil specially so that passing a nil interface value
// will match the typed nils of concrete args.
margs[i] = Nil()
} else {
margs[i] = Eq(arg)
}
}
origin := callerInfo(3)
actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} {
// Synthesize the zero value for each of the return args' types.
rets := make([]interface{}, methodType.NumOut())
for i := 0; i < methodType.NumOut(); i++ {
rets[i] = reflect.Zero(methodType.Out(i)).Interface()
}
return rets
}}
return &Call{t: t, receiver: receiver, method: method, methodType: methodType,
args: margs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions}
}
// AnyTimes allows the expectation to be called 0 or more times
func (c *Call) AnyTimes() *Call {
c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity
return c
}
// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called, MinTimes also
// sets the maximum number of calls to infinity.
func (c *Call) MinTimes(n int) *Call {
c.minCalls = n
if c.maxCalls == 1 {
c.maxCalls = 1e8
}
return c
}
// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called, MaxTimes also
// sets the minimum number of calls to 0.
func (c *Call) MaxTimes(n int) *Call {
c.maxCalls = n
if c.minCalls == 1 {
c.minCalls = 0
}
return c
}
// DoAndReturn declares the action to run when the call is matched.
// The return values from this function are returned by the mocked function.
// It takes an interface{} argument to support n-arity functions.
func (c *Call) DoAndReturn(f interface{}) *Call {
// TODO: Check arity and types here, rather than dying badly elsewhere.
v := reflect.ValueOf(f)
c.addAction(func(args []interface{}) []interface{} {
vargs := make([]reflect.Value, len(args))
ft := v.Type()
for i := 0; i < len(args); i++ {
if args[i] != nil {
vargs[i] = reflect.ValueOf(args[i])
} else {
// Use the zero value for the arg.
vargs[i] = reflect.Zero(ft.In(i))
}
}
vrets := v.Call(vargs)
rets := make([]interface{}, len(vrets))
for i, ret := range vrets {
rets[i] = ret.Interface()
}
return rets
})
return c
}
// Do declares the action to run when the call is matched. The function's
// return values are ignored to retain backward compatibility. To use the
// return values call DoAndReturn.
// It takes an interface{} argument to support n-arity functions.
func (c *Call) Do(f interface{}) *Call {
// TODO: Check arity and types here, rather than dying badly elsewhere.
v := reflect.ValueOf(f)
c.addAction(func(args []interface{}) []interface{} {
vargs := make([]reflect.Value, len(args))
ft := v.Type()
for i := 0; i < len(args); i++ {
if args[i] != nil {
vargs[i] = reflect.ValueOf(args[i])
} else {
// Use the zero value for the arg.
vargs[i] = reflect.Zero(ft.In(i))
}
}
v.Call(vargs)
return nil
})
return c
}
// Return declares the values to be returned by the mocked function call.
func (c *Call) Return(rets ...interface{}) *Call {
c.t.Helper()
mt := c.methodType
if len(rets) != mt.NumOut() {
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",
c.receiver, c.method, len(rets), mt.NumOut(), c.origin)
}
for i, ret := range rets {
if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {
// Identical types; nothing to do.
} else if got == nil {
// Nil needs special handling.
switch want.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
// ok
default:
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",
i, c.receiver, c.method, want, c.origin)
}
} else if got.AssignableTo(want) {
// Assignable type relation. Make the assignment now so that the generated code
// can return the values with a type assertion.
v := reflect.New(want).Elem()
v.Set(reflect.ValueOf(ret))
rets[i] = v.Interface()
} else {
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",
i, c.receiver, c.method, got, want, c.origin)
}
}
c.addAction(func([]interface{}) []interface{} {
return rets
})
return c
}
// Times declares the exact number of times a function call is expected to be executed.
func (c *Call) Times(n int) *Call {
c.minCalls, c.maxCalls = n, n
return c
}
// SetArg declares an action that will set the nth argument's value,
// indirected through a pointer. Or, in the case of a slice, SetArg
// will copy value's elements into the nth argument.
func (c *Call) SetArg(n int, value interface{}) *Call {
c.t.Helper()
mt := c.methodType
// TODO: This will break on variadic methods.
// We will need to check those at invocation time.
if n < 0 || n >= mt.NumIn() {
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]",
n, mt.NumIn(), c.origin)
}
// Permit setting argument through an interface.
// In the interface case, we don't (nay, can't) check the type here.
at := mt.In(n)
switch at.Kind() {
case reflect.Ptr:
dt := at.Elem()
if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) {
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]",
n, vt, dt, c.origin)
}
case reflect.Interface:
// nothing to do
case reflect.Slice:
// nothing to do
default:
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]",
n, at, c.origin)
}
c.addAction(func(args []interface{}) []interface{} {
v := reflect.ValueOf(value)
switch reflect.TypeOf(args[n]).Kind() {
case reflect.Slice:
setSlice(args[n], v)
default:
reflect.ValueOf(args[n]).Elem().Set(v)
}
return nil
})
return c
}
// isPreReq returns true if other is a direct or indirect prerequisite to c.
func (c *Call) isPreReq(other *Call) bool {
for _, preReq := range c.preReqs {
if other == preReq || preReq.isPreReq(other) {
return true
}
}
return false
}
// After declares that the call may only match after preReq has been exhausted.
func (c *Call) After(preReq *Call) *Call {
c.t.Helper()
if c == preReq {
c.t.Fatalf("A call isn't allowed to be its own prerequisite")
}
if preReq.isPreReq(c) {
c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq)
}
c.preReqs = append(c.preReqs, preReq)
return c
}
// Returns true if the minimum number of calls have been made.
func (c *Call) satisfied() bool {
return c.numCalls >= c.minCalls
}
// Returns true iff the maximum number of calls have been made.
func (c *Call) exhausted() bool {
return c.numCalls >= c.maxCalls
}
func (c *Call) String() string {
args := make([]string, len(c.args))
for i, arg := range c.args {
args[i] = arg.String()
}
arguments := strings.Join(args, ", ")
return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin)
}
// Tests if the given call matches the expected call.
// If yes, returns nil. If no, returns error with message explaining why it does not match.
func (c *Call) matches(args []interface{}) error {
if !c.methodType.IsVariadic() {
if len(args) != len(c.args) {
return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d",
c.origin, len(args), len(c.args))
}
for i, m := range c.args {
if !m.Matches(args[i]) {
return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
c.origin, strconv.Itoa(i), args[i], m)
}
}
} else {
if len(c.args) < c.methodType.NumIn()-1 {
return fmt.Errorf("Expected call at %s has the wrong number of matchers. Got: %d, want: %d",
c.origin, len(c.args), c.methodType.NumIn()-1)
}
if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) {
return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d",
c.origin, len(args), len(c.args))
}
if len(args) < len(c.args)-1 {
return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d",
c.origin, len(args), len(c.args)-1)
}
for i, m := range c.args {
if i < c.methodType.NumIn()-1 {
// Non-variadic args
if !m.Matches(args[i]) {
return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
c.origin, strconv.Itoa(i), args[i], m)
}
continue
}
// The last arg has a possibility of a variadic argument, so let it branch
// sample: Foo(a int, b int, c ...int)
if i < len(c.args) && i < len(args) {
if m.Matches(args[i]) {
// Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any())
// Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher)
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC)
// Got Foo(a, b) want Foo(matcherA, matcherB)
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD)
continue
}
}
// The number of actual args don't match the number of matchers,
// or the last matcher is a slice and the last arg is not.
// If this function still matches it is because the last matcher
// matches all the remaining arguments or the lack of any.
// Convert the remaining arguments, if any, into a slice of the
// expected type.
vargsType := c.methodType.In(c.methodType.NumIn() - 1)
vargs := reflect.MakeSlice(vargsType, 0, len(args)-i)
for _, arg := range args[i:] {
vargs = reflect.Append(vargs, reflect.ValueOf(arg))
}
if m.Matches(vargs.Interface()) {
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any())
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher)
// Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any())
// Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher)
break
}
// Wrong number of matchers or not match. Fail.
// Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD)
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD)
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE)
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD)
// Got Foo(a, b, c) want Foo(matcherA, matcherB)
return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
c.origin, strconv.Itoa(i), args[i:], c.args[i])
}
}
// Check that all prerequisite calls have been satisfied.
for _, preReqCall := range c.preReqs {
if !preReqCall.satisfied() {
return fmt.Errorf("Expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v",
c.origin, preReqCall, c)
}
}
// Check that the call is not exhausted.
if c.exhausted() {
return fmt.Errorf("Expected call at %s has already been called the max number of times.", c.origin)
}
return nil
}
// dropPrereqs tells the expected Call to not re-check prerequisite calls any
// longer, and to return its current set.
func (c *Call) dropPrereqs() (preReqs []*Call) {
preReqs = c.preReqs
c.preReqs = nil
return
}
func (c *Call) call(args []interface{}) []func([]interface{}) []interface{} {
c.numCalls++
return c.actions
}
// InOrder declares that the given calls should occur in order.
func InOrder(calls ...*Call) {
for i := 1; i < len(calls); i++ {
calls[i].After(calls[i-1])
}
}
func setSlice(arg interface{}, v reflect.Value) {
va := reflect.ValueOf(arg)
for i := 0; i < v.Len(); i++ {
va.Index(i).Set(v.Index(i))
}
}
func (c *Call) addAction(action func([]interface{}) []interface{}) {
c.actions = append(c.actions, action)
}
package gomock
import (
"testing"
)
type mockTestReporter struct {
errorCalls int
fatalCalls int
}
func (o *mockTestReporter) Errorf(format string, args ...interface{}) {
o.errorCalls++
}
func (o *mockTestReporter) Fatalf(format string, args ...interface{}) {
o.fatalCalls++
}
func (o *mockTestReporter) Helper() {}
func TestCall_After(t *testing.T) {
t.Run("SelfPrereqCallsFatalf", func(t *testing.T) {
tr1 := &mockTestReporter{}
c := &Call{t: tr1}
c.After(c)
if tr1.fatalCalls != 1 {
t.Errorf("number of fatal calls == %v, want 1", tr1.fatalCalls)
}
})
t.Run("LoopInCallOrderCallsFatalf", func(t *testing.T) {
tr1 := &mockTestReporter{}
tr2 := &mockTestReporter{}
c1 := &Call{t: tr1}
c2 := &Call{t: tr2}
c1.After(c2)
c2.After(c1)
if tr1.errorCalls != 0 || tr1.fatalCalls != 0 {
t.Error("unexpected errors")
}
if tr2.fatalCalls != 1 {
t.Errorf("number of fatal calls == %v, want 1", tr2.fatalCalls)
}
})
}
// Copyright 2011 Google Inc.
//
// 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
//
// http://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.
package gomock
import (
"bytes"
"fmt"
)
// callSet represents a set of expected calls, indexed by receiver and method
// name.
type callSet struct {
// Calls that are still expected.
expected map[callSetKey][]*Call
// Calls that have been exhausted.
exhausted map[callSetKey][]*Call
}
// callSetKey is the key in the maps in callSet
type callSetKey struct {
receiver interface{}
fname string
}
func newCallSet() *callSet {
return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)}
}
// Add adds a new expected call.
func (cs callSet) Add(call *Call) {
key := callSetKey{call.receiver, call.method}
m := cs.expected
if call.exhausted() {
m = cs.exhausted
}
m[key] = append(m[key], call)
}
// Remove removes an expected call.
func (cs callSet) Remove(call *Call) {
key := callSetKey{call.receiver, call.method}
calls := cs.expected[key]
for i, c := range calls {
if c == call {
// maintain order for remaining calls
cs.expected[key] = append(calls[:i], calls[i+1:]...)
cs.exhausted[key] = append(cs.exhausted[key], call)
break
}
}
}
// FindMatch searches for a matching call. Returns error with explanation message if no call matched.
func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) {
key := callSetKey{receiver, method}
// Search through the expected calls.
expected := cs.expected[key]
var callsErrors bytes.Buffer
for _, call := range expected {
err := call.matches(args)
if err != nil {
fmt.Fprintf(&callsErrors, "\n%v", err)
} else {
return call, nil
}
}
// If we haven't found a match then search through the exhausted calls so we
// get useful error messages.
exhausted := cs.exhausted[key]
for _, call := range exhausted {
if err := call.matches(args); err != nil {
fmt.Fprintf(&callsErrors, "\n%v", err)
}
}
if len(expected)+len(exhausted) == 0 {
fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method)
}
return nil, fmt.Errorf(callsErrors.String())
}
// Failures returns the calls that are not satisfied.
func (cs callSet) Failures() []*Call {
failures := make([]*Call, 0, len(cs.expected))
for _, calls := range cs.expected {
for _, call := range calls {
if !call.satisfied() {
failures = append(failures, call)
}
}
}
return failures
}
// Copyright 2011 Google Inc.
//
// 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
//
// http://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.
package gomock
import (
"reflect"
"testing"
)
type receiverType struct{}
func (receiverType) Func() {}
func TestCallSetAdd(t *testing.T) {
method := "TestMethod"
var receiver interface{} = "TestReceiver"
cs := newCallSet()
numCalls := 10
for i := 0; i < numCalls; i++ {
cs.Add(newCall(t, receiver, method, reflect.TypeOf(receiverType{}.Func)))
}
call, err := cs.FindMatch(receiver, method, []interface{}{})
if err != nil {
t.Fatalf("FindMatch: %v", err)
}
if call == nil {
t.Fatalf("FindMatch: Got nil, want non-nil *Call")
}
}
func TestCallSetRemove(t *testing.T) {
method := "TestMethod"
var receiver interface{} = "TestReceiver"
cs := newCallSet()
ourCalls := []*Call{}
numCalls := 10
for i := 0; i < numCalls; i++ {
// NOTE: abuse the `numCalls` value to convey initial ordering of mocked calls
generatedCall := &Call{receiver: receiver, method: method, numCalls: i}
cs.Add(generatedCall)
ourCalls = append(ourCalls, generatedCall)
}
// validateOrder validates that the calls in the array are ordered as they were added
validateOrder := func(calls []*Call) {
// lastNum tracks the last `numCalls` (call order) value seen
lastNum := -1
for _, c := range calls {
if lastNum >= c.numCalls {
t.Errorf("found call %d after call %d", c.numCalls, lastNum)
}
lastNum = c.numCalls
}
}
for _, c := range ourCalls {
validateOrder(cs.expected[callSetKey{receiver, method}])
cs.Remove(c)
}
}
// Copyright 2010 Google Inc.
//
// 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
//
// http://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.
// Package gomock is a mock framework for Go.
//
// Standard usage:
// (1) Define an interface that you wish to mock.
// type MyInterface interface {
// SomeMethod(x int64, y string)
// }
// (2) Use mockgen to generate a mock from the interface.
// (3) Use the mock in a test:
// func TestMyThing(t *testing.T) {
// mockCtrl := gomock.NewController(t)
// defer mockCtrl.Finish()
//
// mockObj := something.NewMockMyInterface(mockCtrl)
// mockObj.EXPECT().SomeMethod(4, "blah")
// // pass mockObj to a real object and play with it.
// }
//
// By default, expected calls are not enforced to run in any particular order.
// Call order dependency can be enforced by use of InOrder and/or Call.After.
// Call.After can create more varied call order dependencies, but InOrder is
// often more convenient.
//
// The following examples create equivalent call order dependencies.
//
// Example of using Call.After to chain expected call order:
//
// firstCall := mockObj.EXPECT().SomeMethod(1, "first")
// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
//
// Example of using InOrder to declare expected call order:
//
// gomock.InOrder(
// mockObj.EXPECT().SomeMethod(1, "first"),
// mockObj.EXPECT().SomeMethod(2, "second"),
// mockObj.EXPECT().SomeMethod(3, "third"),
// )
//
// TODO:
// - Handle different argument/return types (e.g. ..., chan, map, interface).
package gomock
import (
"context"
"fmt"
"reflect"
"runtime"
"sync"
)
// A TestReporter is something that can be used to report test failures. It
// is satisfied by the standard library's *testing.T.
type TestReporter interface {
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
}
// TestHelper is a TestReporter that has the Helper method. It is satisfied
// by the standard library's *testing.T.
type TestHelper interface {
TestReporter
Helper()
}
// A Controller represents the top-level control of a mock ecosystem. It
// defines the scope and lifetime of mock objects, as well as their
// expectations. It is safe to call Controller's methods from multiple
// goroutines. Each test should create a new Controller and invoke Finish via
// defer.
//
// func TestFoo(t *testing.T) {
// ctrl := gomock.NewController(t)
// defer ctrl.Finish()
// // ..
// }
//
// func TestBar(t *testing.T) {
// t.Run("Sub-Test-1", st) {
// ctrl := gomock.NewController(st)
// defer ctrl.Finish()
// // ..
// })
// t.Run("Sub-Test-2", st) {
// ctrl := gomock.NewController(st)
// defer ctrl.Finish()
// // ..
// })
// })
type Controller struct {
// T should only be called within a generated mock. It is not intended to
// be used in user code and may be changed in future versions. T is the
// TestReporter passed in when creating the Controller via NewController.
// If the TestReporter does not implement a TestHelper it will be wrapped
// with a nopTestHelper.
T TestHelper
mu sync.Mutex
expectedCalls *callSet
finished bool
}
// NewController returns a new Controller. It is the preferred way to create a
// Controller.
func NewController(t TestReporter) *Controller {
h, ok := t.(TestHelper)
if !ok {
h = nopTestHelper{t}
}
return &Controller{
T: h,
expectedCalls: newCallSet(),
}
}
type cancelReporter struct {
TestHelper
cancel func()
}
func (r *cancelReporter) Errorf(format string, args ...interface{}) {
r.TestHelper.Errorf(format, args...)
}
func (r *cancelReporter) Fatalf(format string, args ...interface{}) {
defer r.cancel()
r.TestHelper.Fatalf(format, args...)
}
// WithContext returns a new Controller and a Context, which is cancelled on any
// fatal failure.
func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) {
h, ok := t.(TestHelper)
if !ok {
h = nopTestHelper{t}
}
ctx, cancel := context.WithCancel(ctx)
return NewController(&cancelReporter{h, cancel}), ctx
}
type nopTestHelper struct {
TestReporter
}
func (h nopTestHelper) Helper() {}
// RecordCall is called by a mock. It should not be called by user code.
func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call {
ctrl.T.Helper()
recv := reflect.ValueOf(receiver)
for i := 0; i < recv.Type().NumMethod(); i++ {
if recv.Type().Method(i).Name == method {
return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)
}
}
ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver)
panic("unreachable")
}
// RecordCallWithMethodType is called by a mock. It should not be called by user code.
func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
ctrl.T.Helper()
call := newCall(ctrl.T, receiver, method, methodType, args...)
ctrl.mu.Lock()
defer ctrl.mu.Unlock()
ctrl.expectedCalls.Add(call)
return call
}
// Call is called by a mock. It should not be called by user code.
func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} {
ctrl.T.Helper()
// Nest this code so we can use defer to make sure the lock is released.
actions := func() []func([]interface{}) []interface{} {
ctrl.T.Helper()
ctrl.mu.Lock()
defer ctrl.mu.Unlock()
expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)
if err != nil {
origin := callerInfo(2)
ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err)
}
// Two things happen here:
// * the matching call no longer needs to check prerequite calls,
// * and the prerequite calls are no longer expected, so remove them.
preReqCalls := expected.dropPrereqs()
for _, preReqCall := range preReqCalls {
ctrl.expectedCalls.Remove(preReqCall)
}
actions := expected.call(args)
if expected.exhausted() {
ctrl.expectedCalls.Remove(expected)
}
return actions
}()
var rets []interface{}
for _, action := range actions {
if r := action(args); r != nil {
rets = r
}
}
return rets
}
// Finish checks to see if all the methods that were expected to be called
// were called. It should be invoked for each Controller. It is not idempotent
// and therefore can only be invoked once.
func (ctrl *Controller) Finish() {
ctrl.T.Helper()
ctrl.mu.Lock()
defer ctrl.mu.Unlock()
if ctrl.finished {
ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
}
ctrl.finished = true
// If we're currently panicking, probably because this is a deferred call,
// pass through the panic.
if err := recover(); err != nil {
panic(err)
}
// Check that all remaining expected calls are satisfied.
failures := ctrl.expectedCalls.Failures()
for _, call := range failures {
ctrl.T.Errorf("missing call(s) to %v", call)
}
if len(failures) != 0 {
ctrl.T.Fatalf("aborting test due to missing call(s)")
}
}
func callerInfo(skip int) string {
if _, file, line, ok := runtime.Caller(skip + 1); ok {
return fmt.Sprintf("%s:%d", file, line)
}
return "unknown file"
}
此差异已折叠。
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/golang/mock/gomock (interfaces: Matcher)
// Package mock_gomock is a generated GoMock package.
package mock_gomock
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockMatcher is a mock of Matcher interface
type MockMatcher struct {
ctrl *gomock.Controller
recorder *MockMatcherMockRecorder
}
// MockMatcherMockRecorder is the mock recorder for MockMatcher
type MockMatcherMockRecorder struct {
mock *MockMatcher
}
// NewMockMatcher creates a new mock instance
func NewMockMatcher(ctrl *gomock.Controller) *MockMatcher {
mock := &MockMatcher{ctrl: ctrl}
mock.recorder = &MockMatcherMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockMatcher) EXPECT() *MockMatcherMockRecorder {
return m.recorder
}
// Matches mocks base method
func (m *MockMatcher) Matches(arg0 interface{}) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Matches", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// Matches indicates an expected call of Matches
func (mr *MockMatcherMockRecorder) Matches(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Matches", reflect.TypeOf((*MockMatcher)(nil).Matches), arg0)
}
// String mocks base method
func (m *MockMatcher) String() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "String")
ret0, _ := ret[0].(string)
return ret0
}
// String indicates an expected call of String
func (mr *MockMatcherMockRecorder) String() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockMatcher)(nil).String))
}
// Copyright 2010 Google Inc.
//
// 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
//
// http://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.
package gomock
import (
"fmt"
"reflect"
)
// A Matcher is a representation of a class of values.
// It is used to represent the valid or expected arguments to a mocked method.
type Matcher interface {
// Matches returns whether x is a match.
Matches(x interface{}) bool
// String describes what the matcher matches.
String() string
}
type anyMatcher struct{}
func (anyMatcher) Matches(x interface{}) bool {
return true
}
func (anyMatcher) String() string {
return "is anything"
}
type eqMatcher struct {
x interface{}
}
func (e eqMatcher) Matches(x interface{}) bool {
return reflect.DeepEqual(e.x, x)
}
func (e eqMatcher) String() string {
return fmt.Sprintf("is equal to %v", e.x)
}
type nilMatcher struct{}
func (nilMatcher) Matches(x interface{}) bool {
if x == nil {
return true
}
v := reflect.ValueOf(x)
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Slice:
return v.IsNil()
}
return false
}
func (nilMatcher) String() string {
return "is nil"
}
type notMatcher struct {
m Matcher
}
func (n notMatcher) Matches(x interface{}) bool {
return !n.m.Matches(x)
}
func (n notMatcher) String() string {
// TODO: Improve this if we add a NotString method to the Matcher interface.
return "not(" + n.m.String() + ")"
}
type assignableToTypeOfMatcher struct {
targetType reflect.Type
}
func (m assignableToTypeOfMatcher) Matches(x interface{}) bool {
return reflect.TypeOf(x).AssignableTo(m.targetType)
}
func (m assignableToTypeOfMatcher) String() string {
return "is assignable to " + m.targetType.Name()
}
// Constructors
// Any returns a matcher that always matches.
func Any() Matcher { return anyMatcher{} }
// Eq returns a matcher that matches on equality.
//
// Example usage:
// Eq(5).Matches(5) // returns true
// Eq(5).Matches(4) // returns false
func Eq(x interface{}) Matcher { return eqMatcher{x} }
// Nil returns a matcher that matches if the received value is nil.
//
// Example usage:
// var x *bytes.Buffer
// Nil().Matches(x) // returns true
// x = &bytes.Buffer{}
// Nil().Matches(x) // returns false
func Nil() Matcher { return nilMatcher{} }
// Not reverses the results of its given child matcher.
//
// Example usage:
// Not(Eq(5)).Matches(4) // returns true
// Not(Eq(5)).Matches(5) // returns false
func Not(x interface{}) Matcher {
if m, ok := x.(Matcher); ok {
return notMatcher{m}
}
return notMatcher{Eq(x)}
}
// AssignableToTypeOf is a Matcher that matches if the parameter to the mock
// function is assignable to the type of the parameter to this function.
//
// Example usage:
// var s fmt.Stringer = &bytes.Buffer{}
// AssignableToTypeOf(s).Matches(time.Second) // returns true
// AssignableToTypeOf(s).Matches(99) // returns false
func AssignableToTypeOf(x interface{}) Matcher {
return assignableToTypeOfMatcher{reflect.TypeOf(x)}
}
// Copyright 2010 Google Inc.
//
// 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
//
// http://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.
//go:generate mockgen -destination internal/mock_gomock/mock_matcher.go github.com/golang/mock/gomock Matcher
package gomock_test
import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"github.com/golang/mock/gomock/internal/mock_gomock"
)
func TestMatchers(t *testing.T) {
type e interface{}
type testCase struct {
matcher gomock.Matcher
yes, no []e
}
tests := []testCase{
{gomock.Any(), []e{3, nil, "foo"}, nil},
{gomock.Eq(4), []e{4}, []e{3, "blah", nil, int64(4)}},
{gomock.Nil(),
[]e{nil, (error)(nil), (chan bool)(nil), (*int)(nil)},
[]e{"", 0, make(chan bool), errors.New("err"), new(int)}},
{gomock.Not(gomock.Eq(4)), []e{3, "blah", nil, int64(4)}, []e{4}},
}
for i, test := range tests {
for _, x := range test.yes {
if !test.matcher.Matches(x) {
t.Errorf(`test %d: "%v %s" should be true.`, i, x, test.matcher)
}
}
for _, x := range test.no {
if test.matcher.Matches(x) {
t.Errorf(`test %d: "%v %s" should be false.`, i, x, test.matcher)
}
}
}
}
// A more thorough test of notMatcher
func TestNotMatcher(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockMatcher := mock_gomock.NewMockMatcher(ctrl)
notMatcher := gomock.Not(mockMatcher)
mockMatcher.EXPECT().Matches(4).Return(true)
if match := notMatcher.Matches(4); match {
t.Errorf("notMatcher should not match 4")
}
mockMatcher.EXPECT().Matches(5).Return(false)
if match := notMatcher.Matches(5); !match {
t.Errorf("notMatcher should match 5")
}
}
type Dog struct {
Breed, Name string
}
// A thorough test of assignableToTypeOfMatcher
func TestAssignableToTypeOfMatcher(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
aStr := "def"
anotherStr := "ghi"
if match := gomock.AssignableToTypeOf("abc").Matches(4); match {
t.Errorf(`AssignableToTypeOf("abc") should not match 4`)
}
if match := gomock.AssignableToTypeOf("abc").Matches(&aStr); match {
t.Errorf(`AssignableToTypeOf("abc") should not match &aStr (*string)`)
}
if match := gomock.AssignableToTypeOf("abc").Matches("def"); !match {
t.Errorf(`AssignableToTypeOf("abc") should match "def"`)
}
if match := gomock.AssignableToTypeOf(&aStr).Matches("abc"); match {
t.Errorf(`AssignableToTypeOf(&aStr) should not match "abc"`)
}
if match := gomock.AssignableToTypeOf(&aStr).Matches(&anotherStr); !match {
t.Errorf(`AssignableToTypeOf(&aStr) should match &anotherStr`)
}
if match := gomock.AssignableToTypeOf(0).Matches(4); !match {
t.Errorf(`AssignableToTypeOf(0) should match 4`)
}
if match := gomock.AssignableToTypeOf(0).Matches("def"); match {
t.Errorf(`AssignableToTypeOf(0) should not match "def"`)
}
if match := gomock.AssignableToTypeOf(Dog{}).Matches(&Dog{}); match {
t.Errorf(`AssignableToTypeOf(Dog{}) should not match &Dog{}`)
}
if match := gomock.AssignableToTypeOf(Dog{}).Matches(Dog{Breed: "pug", Name: "Fido"}); !match {
t.Errorf(`AssignableToTypeOf(Dog{}) should match Dog{Breed: "pug", Name: "Fido"}`)
}
if match := gomock.AssignableToTypeOf(&Dog{}).Matches(Dog{}); match {
t.Errorf(`AssignableToTypeOf(&Dog{}) should not match Dog{}`)
}
if match := gomock.AssignableToTypeOf(&Dog{}).Matches(&Dog{Breed: "pug", Name: "Fido"}); !match {
t.Errorf(`AssignableToTypeOf(&Dog{}) should match &Dog{Breed: "pug", Name: "Fido"}`)
}
}
Apache License
Version 2.0, January 2004
http://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
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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
http://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.
# neo4j-go-connector
This package is an internal dependency being used by [Neo4j Go Driver](https://github.com/neo4j/neo4j-go-driver).
\ No newline at end of file
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
/*
#include <stdlib.h>
#include "bolt/bolt.h"
*/
import "C"
import (
"bytes"
"crypto/x509"
"encoding/pem"
"net/url"
"time"
"unsafe"
)
// Config holds the available configurations options applicable to the connector
type Config struct {
Encryption bool
TLSCertificates []*x509.Certificate
TLSSkipVerify bool
TLSSkipVerifyHostname bool
MaxPoolSize int
MaxConnLifetime time.Duration
ConnAcquisitionTimeout time.Duration
SockConnectTimeout time.Duration
SockKeepalive bool
ConnectorErrorFactory func(state, code int, codeText, context, description string) ConnectorError
DatabaseErrorFactory func(classification, code, message string) DatabaseError
GenericErrorFactory func(format string, args ...interface{}) GenericError
Log Logging
AddressResolver URLAddressResolver
ValueHandlers []ValueHandler
}
func pemEncodeCerts(certs []*x509.Certificate) (*bytes.Buffer, error) {
if len(certs) == 0 {
return nil, nil
}
var buf = &bytes.Buffer{}
for _, cert := range certs {
if err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
return nil, err
}
}
return buf, nil
}
func createConfig(key int, uri *url.URL, config *Config, valueSystem *boltValueSystem) (*C.struct_BoltConfig, error) {
var err error
var cTrust *C.struct_BoltTrust
var cSocketOpts *C.struct_BoltSocketOptions
var cRoutingContext *C.struct_BoltValue
var cUserAgent *C.char
var cConfig *C.struct_BoltConfig
if cTrust, err = createTrust(config); err != nil {
return nil, valueSystem.genericErrorFactory("unable to create trust settings: %v", err)
}
defer C.BoltTrust_destroy(cTrust)
if cRoutingContext, err = createRoutingContext(uri, valueSystem); err != nil {
return nil, valueSystem.genericErrorFactory("unable to extract routing context: %v", err)
}
defer C.BoltValue_destroy(cRoutingContext)
cSocketOpts = createSocketOptions(config)
defer C.BoltSocketOptions_destroy(cSocketOpts)
cUserAgent = C.CString("Go Driver/1.7")
defer C.free(unsafe.Pointer(cUserAgent))
var cLogger = registerLogging(key, config.Log)
defer C.BoltLog_destroy(cLogger)
var cResolver = registerResolver(key, config.AddressResolver)
defer C.BoltAddressResolver_destroy(cResolver)
cConfig = C.BoltConfig_create()
C.BoltConfig_set_scheme(cConfig, scheme(uri))
C.BoltConfig_set_transport(cConfig, transport(config))
C.BoltConfig_set_trust(cConfig, cTrust)
C.BoltConfig_set_user_agent(cConfig, cUserAgent)
C.BoltConfig_set_routing_context(cConfig, cRoutingContext)
C.BoltConfig_set_address_resolver(cConfig, cResolver)
C.BoltConfig_set_log(cConfig, cLogger)
C.BoltConfig_set_max_pool_size(cConfig, C.int32_t(config.MaxPoolSize))
C.BoltConfig_set_max_connection_life_time(cConfig, C.int32_t(config.MaxConnLifetime/time.Millisecond))
C.BoltConfig_set_max_connection_acquisition_time(cConfig, C.int32_t(config.ConnAcquisitionTimeout/time.Millisecond))
C.BoltConfig_set_socket_options(cConfig, cSocketOpts)
return cConfig, nil
}
func scheme(uri *url.URL) C.BoltScheme {
var mode C.BoltScheme = C.BOLT_SCHEME_DIRECT
if uri.Scheme == "bolt+routing" {
mode = C.BOLT_SCHEME_ROUTING
}
if uri.Scheme == "neo4j" {
mode = C.BOLT_SCHEME_NEO4J
}
return mode
}
func transport(config *Config) C.BoltTransport {
var transport C.BoltTransport = C.BOLT_TRANSPORT_PLAINTEXT
if config.Encryption {
transport = C.BOLT_TRANSPORT_ENCRYPTED
}
return transport
}
func createSocketOptions(config *Config) *C.struct_BoltSocketOptions {
var cSocketOpts = C.BoltSocketOptions_create()
C.BoltSocketOptions_set_connect_timeout(cSocketOpts, C.int32_t(config.SockConnectTimeout/time.Millisecond))
C.BoltSocketOptions_set_keep_alive(cSocketOpts, 1)
if !config.SockKeepalive {
C.BoltSocketOptions_set_keep_alive(cSocketOpts, 0)
}
return cSocketOpts
}
func createTrust(config *Config) (*C.struct_BoltTrust, error) {
var cTrust = C.BoltTrust_create()
C.BoltTrust_set_certs(cTrust, nil, 0)
C.BoltTrust_set_skip_verify(cTrust, 0)
C.BoltTrust_set_skip_verify_hostname(cTrust, 0)
certsBuf, err := pemEncodeCerts(config.TLSCertificates)
if err != nil {
C.BoltTrust_destroy(cTrust)
return nil, err
}
if certsBuf != nil {
certsBytes := certsBuf.String()
C.BoltTrust_set_certs(cTrust, C.CString(certsBytes), C.uint64_t(certsBuf.Len()))
}
if config.TLSSkipVerify {
C.BoltTrust_set_skip_verify(cTrust, 1)
}
if config.TLSSkipVerifyHostname {
C.BoltTrust_set_skip_verify_hostname(cTrust, 1)
}
return cTrust, nil
}
func createRoutingContext(source *url.URL, valueSystem *boltValueSystem) (*C.struct_BoltValue, error) {
var err error
var values url.Values
var result map[string]string
if values, err = url.ParseQuery(source.RawQuery); err != nil {
return nil, valueSystem.genericErrorFactory("unable to parse routing context '%s'", source.RawQuery)
}
if len(values) == 0 {
return nil, nil
}
result = make(map[string]string, len(values))
for key, value := range values {
if len(value) > 1 {
return nil, valueSystem.genericErrorFactory("duplicate value specified for '%s' as routing context", key)
}
result[key] = value[0]
}
return valueSystem.valueToConnector(result)
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
import (
"time"
)
// RequestHandle identifies an individual request sent to server
type RequestHandle int64
// FetchType identifies the type of the result fetched via Fetch() call
type FetchType int
const (
// FetchTypeRecord tells that fetched data is record
FetchTypeRecord FetchType = 1
// FetchTypeMetadata tells that fetched data is metadata
FetchTypeMetadata FetchType = 0
// FetchTypeError tells that fetch was not successful
FetchTypeError FetchType = -1
)
// Connection represents an active seabolt connection
type Connection interface {
Id() (string, error)
RemoteAddress() (string, error)
Server() (string, error)
Begin(bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error)
Commit() (RequestHandle, error)
Rollback() (RequestHandle, error)
Run(cypher string, parameters map[string]interface{}, bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error)
PullAll() (RequestHandle, error)
DiscardAll() (RequestHandle, error)
Reset() (RequestHandle, error)
Flush() error
Fetch(request RequestHandle) (FetchType, error) // return type ?
FetchSummary(request RequestHandle) (int, error) // return type ?
LastBookmark() (string, error)
Fields() ([]string, error)
Metadata() (map[string]interface{}, error)
Data() ([]interface{}, error)
Close() error
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
import (
"time"
"github.com/stretchr/testify/mock"
)
type mockConnection struct {
mock.Mock
}
func (m *mockConnection) Id() (string, error) {
args := m.Called()
return args.String(0), args.Error(1)
}
func (m *mockConnection) RemoteAddress() (string, error) {
args := m.Called()
return args.String(0), args.Error(1)
}
func (m *mockConnection) Server() (string, error) {
args := m.Called()
return args.String(0), args.Error(1)
}
func (m *mockConnection) Begin(bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error) {
args := m.Called(bookmarks, txTimeout, txMetadata)
return args.Get(0).(RequestHandle), args.Error(1)
}
func (m *mockConnection) Commit() (RequestHandle, error) {
args := m.Called()
return args.Get(0).(RequestHandle), args.Error(1)
}
func (m *mockConnection) Rollback() (RequestHandle, error) {
args := m.Called()
return args.Get(0).(RequestHandle), args.Error(1)
}
func (m *mockConnection) Run(cypher string, parameters map[string]interface{}, bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error) {
args := m.Called(cypher, parameters, bookmarks, txTimeout, txMetadata)
return args.Get(0).(RequestHandle), args.Error(1)
}
func (m *mockConnection) PullAll() (RequestHandle, error) {
args := m.Called()
return args.Get(0).(RequestHandle), args.Error(1)
}
func (m *mockConnection) DiscardAll() (RequestHandle, error) {
args := m.Called()
return args.Get(0).(RequestHandle), args.Error(1)
}
func (m *mockConnection) Reset() (RequestHandle, error) {
args := m.Called()
return args.Get(0).(RequestHandle), args.Error(1)
}
func (m *mockConnection) Flush() error {
args := m.Called()
return args.Error(0)
}
func (m *mockConnection) Fetch(request RequestHandle) (FetchType, error) {
args := m.Called(request)
return args.Get(0).(FetchType), args.Error(1)
}
func (m *mockConnection) FetchSummary(request RequestHandle) (int, error) {
args := m.Called(request)
return args.Int(0), args.Error(1)
}
func (m *mockConnection) LastBookmark() (string, error) {
args := m.Called()
return args.String(0), args.Error(1)
}
func (m *mockConnection) Fields() ([]string, error) {
args := m.Called()
return args.Get(0).([]string), args.Error(1)
}
func (m *mockConnection) Metadata() (map[string]interface{}, error) {
args := m.Called()
return args.Get(0).(map[string]interface{}), args.Error(1)
}
func (m *mockConnection) Data() ([]interface{}, error) {
args := m.Called()
return args.Get(0).([]interface{}), args.Error(1)
}
func (m *mockConnection) Close() error {
args := m.Called()
return args.Error(0)
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
/*
#include <stdlib.h>
#include "bolt/bolt.h"
*/
import "C"
import (
"fmt"
"time"
"unsafe"
)
type seaboltConnection struct {
connector *seaboltConnector
cInstance *C.struct_BoltConnection
valueSystem *boltValueSystem
}
var newSeaboltConnection = func(connector *seaboltConnector, mode AccessMode) (*seaboltConnection, error) {
var cMode uint32 = C.BOLT_ACCESS_MODE_WRITE
if mode == AccessModeRead {
cMode = C.BOLT_ACCESS_MODE_READ
}
cStatus := C.BoltStatus_create()
defer C.BoltStatus_destroy(cStatus)
cConnection := C.BoltConnector_acquire(connector.cInstance, C.BoltAccessMode(cMode), cStatus)
if cConnection == nil {
state := C.BoltStatus_get_state(cStatus)
code := C.BoltStatus_get_error(cStatus)
codeText := C.GoString(C.BoltError_get_string(code))
context := C.GoString(C.BoltStatus_get_error_context(cStatus))
return nil, newConnectorError(int(state), int(code), codeText, context, "unable to acquire connection from connector")
}
return &seaboltConnection{connector: connector, cInstance: cConnection, valueSystem: connector.valueSystem}, nil
}
func (connection *seaboltConnection) Id() (string, error) {
return C.GoString(C.BoltConnection_id(connection.cInstance)), nil
}
func (connection *seaboltConnection) RemoteAddress() (string, error) {
connectedAddress := C.BoltConnection_remote_endpoint(connection.cInstance)
if connectedAddress == nil {
return "UNKNOWN", nil
}
return fmt.Sprintf("%s:%s", C.GoString(C.BoltAddress_host(connectedAddress)), C.GoString(C.BoltAddress_port(connectedAddress))), nil
}
func (connection *seaboltConnection) Server() (string, error) {
server := C.BoltConnection_server(connection.cInstance)
if server == nil {
return "UNKNOWN", nil
}
return C.GoString(server), nil
}
func (connection *seaboltConnection) Begin(bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error) {
var res C.int32_t
res = C.BoltConnection_clear_begin(connection.cInstance)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to clear begin message")
}
if len(bookmarks) > 0 {
bookmarksValue, err := connection.valueSystem.valueToConnector(bookmarks)
if err != nil {
return -1, connection.valueSystem.genericErrorFactory("unable to convert bookmarks to connector value for begin message: %v", err)
}
res := C.BoltConnection_set_begin_bookmarks(connection.cInstance, bookmarksValue)
C.BoltValue_destroy(bookmarksValue)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to set bookmarks for begin message")
}
}
if txTimeout > 0 {
timeOut := C.int64_t(txTimeout / time.Millisecond)
res := C.BoltConnection_set_begin_tx_timeout(connection.cInstance, timeOut)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to set tx timeout for begin message")
}
}
if len(txMetadata) > 0 {
metadataValue, err := connection.valueSystem.valueToConnector(txMetadata)
if err != nil {
return -1, connection.valueSystem.genericErrorFactory("unable to convert tx metadata to connector value for begin message: %v", err)
}
res := C.BoltConnection_set_begin_tx_metadata(connection.cInstance, metadataValue)
C.BoltValue_destroy(metadataValue)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to set tx metadata for begin message")
}
}
res = C.BoltConnection_load_begin_request(connection.cInstance)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to generate begin message")
}
return RequestHandle(C.BoltConnection_last_request(connection.cInstance)), nil
}
func (connection *seaboltConnection) Commit() (RequestHandle, error) {
res := C.BoltConnection_load_commit_request(connection.cInstance)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to generate commit message")
}
return RequestHandle(C.BoltConnection_last_request(connection.cInstance)), nil
}
func (connection *seaboltConnection) Rollback() (RequestHandle, error) {
res := C.BoltConnection_load_rollback_request(connection.cInstance)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to generate rollback message")
}
return RequestHandle(C.BoltConnection_last_request(connection.cInstance)), nil
}
func (connection *seaboltConnection) Run(cypher string, params map[string]interface{}, bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error) {
var res C.int32_t
res = C.BoltConnection_clear_run(connection.cInstance)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to clear run message")
}
cypherStr := C.CString(cypher)
res = C.BoltConnection_set_run_cypher(connection.cInstance, cypherStr, C.uint64_t(len(cypher)), C.int32_t(len(params)))
C.free(unsafe.Pointer(cypherStr))
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to set cypher statement")
}
var index C.int32_t
for paramName, paramValue := range params {
paramNameLen := C.uint64_t(len(paramName))
paramNameStr := C.CString(paramName)
boltValue := C.BoltConnection_set_run_cypher_parameter(connection.cInstance, index, paramNameStr, paramNameLen)
C.free(unsafe.Pointer(paramNameStr))
if boltValue == nil {
return -1, newError(connection, "unable to retrieve reference to cypher statement parameter value")
}
if err := connection.valueSystem.valueAsConnector(boltValue, paramValue); err != nil {
return -1, connection.valueSystem.genericErrorFactory("unable to convert parameter %q to connector value for run message: %v", paramName, err)
}
index++
}
if len(bookmarks) > 0 {
bookmarksValue, err := connection.valueSystem.valueToConnector(bookmarks)
if err != nil {
return -1, connection.valueSystem.genericErrorFactory("unable to convert bookmarks to connector value for run message: %v", err)
}
res := C.BoltConnection_set_run_bookmarks(connection.cInstance, bookmarksValue)
C.BoltValue_destroy(bookmarksValue)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to set bookmarks for run message")
}
}
if txTimeout > 0 {
timeOut := C.int64_t(txTimeout / time.Millisecond)
res := C.BoltConnection_set_run_tx_timeout(connection.cInstance, timeOut)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to set tx timeout for run message")
}
}
if len(txMetadata) > 0 {
metadataValue, err := connection.valueSystem.valueToConnector(txMetadata)
if err != nil {
return -1, connection.valueSystem.genericErrorFactory("unable to convert tx metadata to connector value for run message: %v", err)
}
res := C.BoltConnection_set_run_tx_metadata(connection.cInstance, metadataValue)
C.BoltValue_destroy(metadataValue)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to set tx metadata for run message")
}
}
res = C.BoltConnection_load_run_request(connection.cInstance)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to generate run message")
}
return RequestHandle(C.BoltConnection_last_request(connection.cInstance)), nil
}
func (connection *seaboltConnection) PullAll() (RequestHandle, error) {
res := C.BoltConnection_load_pull_request(connection.cInstance, -1)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to generate pullall message")
}
return RequestHandle(C.BoltConnection_last_request(connection.cInstance)), nil
}
func (connection *seaboltConnection) DiscardAll() (RequestHandle, error) {
res := C.BoltConnection_load_discard_request(connection.cInstance, -1)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to generate discardall message")
}
return RequestHandle(C.BoltConnection_last_request(connection.cInstance)), nil
}
func (connection *seaboltConnection) assertReadyState() error {
cStatus := C.BoltConnection_status(connection.cInstance)
if C.BoltStatus_get_state(cStatus) != C.BOLT_CONNECTION_STATE_READY {
return newError(connection, "unexpected connection state")
}
return nil
}
func (connection *seaboltConnection) Flush() error {
res := C.BoltConnection_send(connection.cInstance)
if res < 0 {
return newError(connection, "unable to flush")
}
return connection.assertReadyState()
}
func (connection *seaboltConnection) Fetch(request RequestHandle) (FetchType, error) {
res := C.BoltConnection_fetch(connection.cInstance, C.BoltRequest(request))
if err := connection.assertReadyState(); err != nil {
return FetchTypeError, err
}
return FetchType(res), nil
}
func (connection *seaboltConnection) FetchSummary(request RequestHandle) (int, error) {
res := C.BoltConnection_fetch_summary(connection.cInstance, C.BoltRequest(request))
if res < 0 {
return -1, newError(connection, "unable to fetch summary")
}
err := connection.assertReadyState()
if err != nil {
return -1, err
}
return int(res), nil
}
func (connection *seaboltConnection) LastBookmark() (string, error) {
bookmark := C.BoltConnection_last_bookmark(connection.cInstance)
if bookmark != nil {
return C.GoString(bookmark), nil
}
return "", nil
}
func (connection *seaboltConnection) Fields() ([]string, error) {
fields, err := connection.valueSystem.valueAsGo(C.BoltConnection_field_names(connection.cInstance))
if err != nil {
return nil, err
}
if fields != nil {
fieldsAsList := fields.([]interface{})
fieldsAsStr := make([]string, len(fieldsAsList))
for i := range fieldsAsList {
fieldsAsStr[i] = fieldsAsList[i].(string)
}
return fieldsAsStr, nil
}
return nil, connection.valueSystem.genericErrorFactory("field names not available")
}
func (connection *seaboltConnection) Metadata() (map[string]interface{}, error) {
metadata, err := connection.valueSystem.valueAsGo(C.BoltConnection_metadata(connection.cInstance))
if err != nil {
return nil, err
}
if metadataAsGenericMap, ok := metadata.(map[string]interface{}); ok {
return metadataAsGenericMap, nil
}
return nil, connection.valueSystem.genericErrorFactory("metadata is not of expected type")
}
func (connection *seaboltConnection) Data() ([]interface{}, error) {
fields, err := connection.valueSystem.valueAsGo(C.BoltConnection_field_values(connection.cInstance))
if err != nil {
return nil, err
}
return fields.([]interface{}), nil
}
func (connection *seaboltConnection) Reset() (RequestHandle, error) {
res := C.BoltConnection_load_reset_request(connection.cInstance)
if res != C.BOLT_SUCCESS {
return -1, newError(connection, "unable to generate reset message")
}
return RequestHandle(C.BoltConnection_last_request(connection.cInstance)), nil
}
func (connection *seaboltConnection) Close() error {
C.BoltConnector_release(connection.connector.cInstance, connection.cInstance)
return nil
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
import (
"sync/atomic"
"time"
)
type job func()
type workerConnection struct {
connector *workerConnector
pool *workerPool
delegate Connection
active int32
}
var newWorkerConnection = func(connector *workerConnector, mode AccessMode) (*workerConnection, error) {
var err error
var startTime time.Time
var delegate *seaboltConnection
var connection *workerConnection
connection = &workerConnection{
connector: connector,
pool: connector.pool,
delegate: nil,
active: 0,
}
for connection.delegate == nil {
if startTime.IsZero() {
startTime = time.Now()
} else if time.Since(startTime) > connector.config.ConnAcquisitionTimeout {
return nil, newConnectionAcquisitionTimedOutError(connector.delegate.valueSystem)
}
if poolError := connection.queueJob(func() {
delegate, err = newSeaboltConnection(connector.delegate, mode)
}); poolError != nil {
err = poolError
}
if err != nil {
if isPoolFullError(err) && connector.config.ConnAcquisitionTimeout > 0 {
if waitClosed(connection, connector.config.ConnAcquisitionTimeout-time.Since(startTime)) {
continue
} else {
return nil, newConnectionAcquisitionTimedOutError(connector.delegate.valueSystem)
}
}
return nil, err
}
connection.delegate = delegate
}
return connection, nil
}
var waitClosed = func(w *workerConnection, timeout time.Duration) bool {
if w.connector != nil {
select {
case <-w.connector.closeSignal:
return true
case <-time.After(timeout):
}
}
return false
}
var signalClosed = func(w *workerConnection) {
if w.connector != nil {
select {
case w.connector.closeSignal <- signal{}:
default:
}
}
}
func (w *workerConnection) queueJob(item job) error {
if atomic.CompareAndSwapInt32(&w.active, 0, 1) {
defer atomic.StoreInt32(&w.active, 0)
var done = make(chan bool, 1)
defer close(done)
if err := w.pool.submit(func(stopper <-chan signal) {
item()
done <- true
}); err != nil {
return err
}
<-done
return nil
}
return newGenericError("a connection is not thread-safe and thus should not be used concurrently")
}
func (w *workerConnection) Id() (string, error) {
var id string
var err error
if otherErr := w.queueJob(func() {
id, err = w.delegate.Id()
}); otherErr != nil {
err = otherErr
}
return id, err
}
func (w *workerConnection) RemoteAddress() (string, error) {
var remoteAddress string
var err error
if otherErr := w.queueJob(func() {
remoteAddress, err = w.delegate.RemoteAddress()
}); otherErr != nil {
err = otherErr
}
return remoteAddress, err
}
func (w *workerConnection) Server() (string, error) {
var server string
var err error
if otherErr := w.queueJob(func() {
server, err = w.delegate.Server()
}); otherErr != nil {
err = otherErr
}
return server, err
}
func (w *workerConnection) Begin(bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error) {
var handle RequestHandle
var err error
if otherErr := w.queueJob(func() {
handle, err = w.delegate.Begin(bookmarks, txTimeout, txMetadata)
}); otherErr != nil {
err = otherErr
}
return handle, err
}
func (w *workerConnection) Commit() (RequestHandle, error) {
var handle RequestHandle
var err error
if otherErr := w.queueJob(func() {
handle, err = w.delegate.Commit()
}); otherErr != nil {
err = otherErr
}
return handle, err
}
func (w *workerConnection) Rollback() (RequestHandle, error) {
var handle RequestHandle
var err error
if otherErr := w.queueJob(func() {
handle, err = w.delegate.Rollback()
}); otherErr != nil {
err = otherErr
}
return handle, err
}
func (w *workerConnection) Run(cypher string, args map[string]interface{}, bookmarks []string, txTimeout time.Duration, txMetadata map[string]interface{}) (RequestHandle, error) {
var handle RequestHandle
var err error
if otherErr := w.queueJob(func() {
handle, err = w.delegate.Run(cypher, args, bookmarks, txTimeout, txMetadata)
}); otherErr != nil {
err = otherErr
}
return handle, err
}
func (w *workerConnection) PullAll() (RequestHandle, error) {
var handle RequestHandle
var err error
if otherErr := w.queueJob(func() {
handle, err = w.delegate.PullAll()
}); otherErr != nil {
err = otherErr
}
return handle, err
}
func (w *workerConnection) DiscardAll() (RequestHandle, error) {
var handle RequestHandle
var err error
if otherErr := w.queueJob(func() {
handle, err = w.delegate.DiscardAll()
}); otherErr != nil {
err = otherErr
}
return handle, err
}
func (w *workerConnection) Reset() (RequestHandle, error) {
var handle RequestHandle
var err error
if otherErr := w.queueJob(func() {
handle, err = w.delegate.Reset()
}); otherErr != nil {
err = otherErr
}
return handle, err
}
func (w *workerConnection) Flush() error {
var err error
if otherErr := w.queueJob(func() {
err = w.delegate.Flush()
}); otherErr != nil {
err = otherErr
}
return err
}
func (w *workerConnection) Fetch(request RequestHandle) (FetchType, error) {
var fetched FetchType
var err error
if otherErr := w.queueJob(func() {
fetched, err = w.delegate.Fetch(request)
}); otherErr != nil {
err = otherErr
}
return fetched, err
}
func (w *workerConnection) FetchSummary(request RequestHandle) (int, error) {
var fetched int
var err error
if otherErr := w.queueJob(func() {
fetched, err = w.delegate.FetchSummary(request)
}); otherErr != nil {
err = otherErr
}
return fetched, err
}
func (w *workerConnection) LastBookmark() (string, error) {
var bookmark string
var err error
if otherErr := w.queueJob(func() {
bookmark, err = w.delegate.LastBookmark()
}); otherErr != nil {
err = otherErr
}
return bookmark, err
}
func (w *workerConnection) Fields() ([]string, error) {
var fields []string
var err error
if otherErr := w.queueJob(func() {
fields, err = w.delegate.Fields()
}); otherErr != nil {
err = otherErr
}
return fields, err
}
func (w *workerConnection) Metadata() (map[string]interface{}, error) {
var metadata map[string]interface{}
var err error
if otherErr := w.queueJob(func() {
metadata, err = w.delegate.Metadata()
}); otherErr != nil {
err = otherErr
}
return metadata, err
}
func (w *workerConnection) Data() ([]interface{}, error) {
var data []interface{}
var err error
if otherErr := w.queueJob(func() {
data, err = w.delegate.Data()
}); otherErr != nil {
err = otherErr
}
return data, err
}
func (w *workerConnection) Close() error {
var err error
if otherErr := w.queueJob(func() {
err = w.delegate.Close()
}); otherErr != nil {
err = otherErr
}
signalClosed(w)
return err
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func Test_WorkerConnection(t *testing.T) {
newMockedConnection := func() (*workerConnection, *mockConnection, func()) {
pool := newWorkerPool(1, 1, 1*time.Minute)
delegate := new(mockConnection)
connection := &workerConnection{
pool: pool,
delegate: delegate,
active: 0,
}
return connection, delegate, func() {
pool.close()
}
}
newMockedConnectionInUse := func() (*workerConnection, *mockConnection, func()) {
conn, mocked, cleanup := newMockedConnection()
conn.active = 1
return conn, mocked, cleanup
}
t.Run("newWorkerConnection", func(t *testing.T) {
var valueSystem = createValueSystem(&Config{})
var someOtherFailure = fmt.Errorf("some other failure")
var poolFullError = newPoolFullError(valueSystem)
var connectionAcquisitionTimedOutError = newConnectionAcquisitionTimedOutError(valueSystem)
t.Run("shouldInvokeNewSeaboltConnection", func(t *testing.T) {
var cases = []struct {
name string
timeout time.Duration
}{
{"AcquisitionTimeout=0", 0},
{"AcquisitionTimeout=5s", 5 * time.Second},
}
for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
var originalNewSeaboltConnection = newSeaboltConnection
defer func() {
newSeaboltConnection = originalNewSeaboltConnection
}()
var newSeaboltConnectionCount = 0
newSeaboltConnection = func(connector *seaboltConnector, mode AccessMode) (*seaboltConnection, error) {
newSeaboltConnectionCount++
return &seaboltConnection{}, nil
}
var connector = &workerConnector{
config: Config{ConnAcquisitionTimeout: testCase.timeout},
pool: newWorkerPool(1, 1, 1*time.Minute),
}
defer connector.pool.close()
connection, err := newWorkerConnection(connector, AccessModeRead)
assert.NoError(t, err)
assert.NotNil(t, connection)
assert.Equal(t, 1, newSeaboltConnectionCount)
})
}
})
t.Run("shouldReturnErrorFromNewSeaboltConnection", func(t *testing.T) {
var originalNewSeaboltConnection = newSeaboltConnection
defer func() {
newSeaboltConnection = originalNewSeaboltConnection
}()
var newSeaboltConnectionCount = 0
newSeaboltConnection = func(connector *seaboltConnector, mode AccessMode) (*seaboltConnection, error) {
newSeaboltConnectionCount++
return nil, someOtherFailure
}
var connector = &workerConnector{
config: Config{ConnAcquisitionTimeout: 0},
pool: newWorkerPool(1, 1, 1*time.Minute),
}
defer connector.pool.close()
connection, err := newWorkerConnection(connector, AccessModeRead)
assert.EqualError(t, err, "some other failure")
assert.Nil(t, connection)
assert.Equal(t, 1, newSeaboltConnectionCount)
})
t.Run("shouldReturnPoolFullErrorWhenAcquisitionTimeoutIsZero", func(t *testing.T) {
var originalNewSeaboltConnection = newSeaboltConnection
defer func() {
newSeaboltConnection = originalNewSeaboltConnection
}()
var newSeaboltConnectionCount = 0
newSeaboltConnection = func(connector *seaboltConnector, mode AccessMode) (*seaboltConnection, error) {
newSeaboltConnectionCount++
return nil, poolFullError
}
var connector = &workerConnector{
config: Config{ConnAcquisitionTimeout: 0},
pool: newWorkerPool(1, 1, 1*time.Minute),
}
defer connector.pool.close()
connection, err := newWorkerConnection(connector, AccessModeRead)
assert.EqualError(t, err, poolFullError.Error())
assert.Nil(t, connection)
assert.Equal(t, 1, newSeaboltConnectionCount)
})
t.Run("shouldInvokeWaitClosedWhenPoolIsFullAndSucceedWhenWaitSucceeds", func(t *testing.T) {
var originalNewSeaboltConnection = newSeaboltConnection
var originalWaitClosed = waitClosed
defer func() {
newSeaboltConnection = originalNewSeaboltConnection
waitClosed = originalWaitClosed
}()
var newSeaboltConnectionCount = 0
newSeaboltConnection = func(connector *seaboltConnector, mode AccessMode) (*seaboltConnection, error) {
newSeaboltConnectionCount++
if newSeaboltConnectionCount > 1 {
return &seaboltConnection{}, nil
}
return nil, poolFullError
}
var waitClosedCount = 0
waitClosed = func(w *workerConnection, timeout time.Duration) bool {
waitClosedCount++
return true
}
var connector = &workerConnector{
config: Config{ConnAcquisitionTimeout: 5 * time.Second},
pool: newWorkerPool(1, 1, 1*time.Minute),
}
defer connector.pool.close()
connection, err := newWorkerConnection(connector, AccessModeRead)
assert.NoError(t, err)
assert.NotNil(t, connection)
assert.Equal(t, 1, waitClosedCount)
assert.Equal(t, 2, newSeaboltConnectionCount)
})
t.Run("shouldInvokeWaitClosedWhenPoolIsFullAndFailWhenWaitFails", func(t *testing.T) {
var originalNewSeaboltConnection = newSeaboltConnection
var originalWaitClosed = waitClosed
defer func() {
newSeaboltConnection = originalNewSeaboltConnection
waitClosed = originalWaitClosed
}()
var newSeaboltConnectionCount = 0
newSeaboltConnection = func(connector *seaboltConnector, mode AccessMode) (*seaboltConnection, error) {
newSeaboltConnectionCount++
return nil, poolFullError
}
var waitClosedCount = 0
waitClosed = func(w *workerConnection, timeout time.Duration) bool {
waitClosedCount++
return false
}
var connector = &workerConnector{
config: Config{ConnAcquisitionTimeout: 5 * time.Second},
pool: newWorkerPool(1, 1, 1*time.Minute),
delegate: &seaboltConnector{
valueSystem: valueSystem,
},
}
defer connector.pool.close()
connection, err := newWorkerConnection(connector, AccessModeRead)
assert.EqualError(t, err, connectionAcquisitionTimedOutError.Error())
assert.Nil(t, connection)
assert.Equal(t, 1, waitClosedCount)
assert.Equal(t, 1, newSeaboltConnectionCount)
})
})
t.Run("shouldInvokeSignalClosedOnClose", func(t *testing.T) {
var originalSignalClosed = signalClosed
defer func() {
signalClosed = originalSignalClosed
}()
var signalClosedCount = 0
signalClosed = func(w *workerConnection) {
signalClosedCount++
}
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Close").Return(nil)
assert.NoError(t, conn.Close())
assert.Equal(t, 1, signalClosedCount)
})
t.Run("shouldSurfacePoolFullErrorWhenAcquisitionTimeoutIsZero", func(t *testing.T) {
})
t.Run("shouldInterceptPoolFullErrorWhenAcquisitionTimeoutIsNotZero", func(t *testing.T) {
})
t.Run("shouldInvokeDelegate", func(t *testing.T) {
failure := fmt.Errorf("some error")
handle := RequestHandle(500)
t.Run("Id", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Id").Return("123", failure)
id, err := conn.Id()
assert.Equal(t, "123", id)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("RemoteAddress", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("RemoteAddress").Return("localhost:7687", failure)
remoteAddress, err := conn.RemoteAddress()
assert.Equal(t, "localhost:7687", remoteAddress)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Server", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Server").Return("Neo4j/3.5.0", failure)
server, err := conn.Server()
assert.Equal(t, "Neo4j/3.5.0", server)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Begin", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
bookmarks := []string{"1", "2", "3"}
txTimeout := 5 * time.Minute
txMetadata := map[string]interface{}{"a": 1, "b": true, "c": "yes"}
delegate.On("Begin", bookmarks, txTimeout, txMetadata).Return(handle, failure)
beginHandle, err := conn.Begin(bookmarks, txTimeout, txMetadata)
assert.Equal(t, handle, beginHandle)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Commit", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Commit").Return(handle, failure)
commitHandle, err := conn.Commit()
assert.Equal(t, handle, commitHandle)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Rollback", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Rollback").Return(handle, failure)
rollbackHandle, err := conn.Rollback()
assert.Equal(t, handle, rollbackHandle)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Run", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
cypher := "CREATE (n {id: $x})"
parameters := map[string]interface{}{"id": 5000}
bookmarks := []string{"1", "2", "3"}
txTimeout := 5 * time.Minute
txMetadata := map[string]interface{}{"a": 1, "b": true, "c": "yes"}
delegate.On("Run", cypher, parameters, bookmarks, txTimeout, txMetadata).Return(handle, failure)
runHandle, err := conn.Run(cypher, parameters, bookmarks, txTimeout, txMetadata)
assert.Equal(t, handle, runHandle)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("PullAll", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("PullAll").Return(handle, failure)
pullAllHandle, err := conn.PullAll()
assert.Equal(t, handle, pullAllHandle)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("DiscardAll", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("DiscardAll").Return(handle, failure)
discardAllHandle, err := conn.DiscardAll()
assert.Equal(t, handle, discardAllHandle)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Reset", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Reset").Return(handle, failure)
resetHandle, err := conn.Reset()
assert.Equal(t, handle, resetHandle)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Flush", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Flush").Return(failure)
err := conn.Flush()
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Fetch", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Fetch", handle).Return(FetchTypeRecord, failure)
fetched, err := conn.Fetch(handle)
assert.Equal(t, FetchTypeRecord, fetched)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("FetchSummary", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("FetchSummary", handle).Return(50, failure)
records, err := conn.FetchSummary(handle)
assert.Equal(t, 50, records)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("LastBookmark", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("LastBookmark").Return("bookmark:1234", failure)
bookmark, err := conn.LastBookmark()
assert.Equal(t, "bookmark:1234", bookmark)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Fields", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
fields := []string{"x", "y", "z"}
delegate.On("Fields").Return(fields, failure)
fieldsReturned, err := conn.Fields()
assert.Equal(t, fields, fieldsReturned)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Metadata", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
metadata := map[string]interface{}{"x": 1, "y": "a", "z": false}
delegate.On("Metadata").Return(metadata, failure)
metadataReturned, err := conn.Metadata()
assert.Equal(t, metadata, metadataReturned)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Data", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
data := []interface{}{"1", 2, false}
delegate.On("Data").Return(data, failure)
dataReturned, err := conn.Data()
assert.Equal(t, data, dataReturned)
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
t.Run("Close", func(t *testing.T) {
conn, delegate, cleanup := newMockedConnection()
defer cleanup()
delegate.On("Close").Return(failure)
err := conn.Close()
assert.Equal(t, failure, err)
delegate.AssertExpectations(t)
})
})
t.Run("shouldPropagateWorkerError", func(t *testing.T) {
errText := "a connection is not thread-safe and thus should not be used concurrently"
t.Run("Id", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Id()
assert.EqualError(t, err, errText)
})
t.Run("RemoteAddress", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.RemoteAddress()
assert.EqualError(t, err, errText)
})
t.Run("Server", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Server()
assert.EqualError(t, err, errText)
})
t.Run("Begin", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Begin([]string{}, 1*time.Minute, nil)
assert.EqualError(t, err, errText)
})
t.Run("Commit", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Commit()
assert.EqualError(t, err, errText)
})
t.Run("Rollback", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Rollback()
assert.EqualError(t, err, errText)
})
t.Run("Run", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Run("RETURN 1", nil, nil, 1*time.Second, nil)
assert.EqualError(t, err, errText)
})
t.Run("PullAll", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.PullAll()
assert.EqualError(t, err, errText)
})
t.Run("DiscardAll", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.DiscardAll()
assert.EqualError(t, err, errText)
})
t.Run("Reset", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Reset()
assert.EqualError(t, err, errText)
})
t.Run("Flush", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
err := conn.Flush()
assert.EqualError(t, err, errText)
})
t.Run("Fetch", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Fetch(RequestHandle(1))
assert.EqualError(t, err, errText)
})
t.Run("FetchSummary", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.FetchSummary(RequestHandle(1))
assert.EqualError(t, err, errText)
})
t.Run("LastBookmark", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.LastBookmark()
assert.EqualError(t, err, errText)
})
t.Run("Fields", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Fields()
assert.EqualError(t, err, errText)
})
t.Run("Metadata", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Metadata()
assert.EqualError(t, err, errText)
})
t.Run("Data", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
_, err := conn.Data()
assert.EqualError(t, err, errText)
})
t.Run("Close", func(t *testing.T) {
conn, _, cleanup := newMockedConnectionInUse()
defer cleanup()
err := conn.Close()
assert.EqualError(t, err, errText)
})
})
t.Run("queueJob", func(t *testing.T) {
t.Run("shouldSetReceivingToOneWhenExecuting", func(t *testing.T) {
var startEvent = make(chan bool, 1)
var waitEvent = make(chan bool, 1)
var blockingJob = func() {
startEvent <- true
<-waitEvent
}
conn, _, cleanup := newMockedConnection()
defer cleanup()
defer close(waitEvent)
defer close(startEvent)
go conn.queueJob(blockingJob)
<-startEvent
assert.Equal(t, int32(1), conn.active)
})
t.Run("shouldSetReceivingToZeroWhenExecutionIsComplete", func(t *testing.T) {
conn, _, cleanup := newMockedConnection()
defer cleanup()
conn.queueJob(func() {})
assert.Equal(t, int32(0), conn.active)
})
t.Run("shouldCheckForConcurrentAccess", func(t *testing.T) {
var startEvent = make(chan bool, 1)
var waitEvent = make(chan bool, 1)
var blockingJob = func() {
startEvent <- true
<-waitEvent
}
conn, _, cleanup := newMockedConnection()
defer cleanup()
defer close(waitEvent)
defer close(startEvent)
go conn.queueJob(blockingJob)
<-startEvent
err := conn.queueJob(blockingJob)
assert.EqualError(t, err, "a connection is not thread-safe and thus should not be used concurrently")
})
})
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
import (
"net/url"
)
// AccessMode is used by the routing driver to decide if a transaction should be routed to a write server
// or a read server in a cluster. When running a transaction, a write transaction requires a server that
// supports writes. A read transaction, on the other hand, requires a server that supports read operations.
// This classification is key for routing driver to route transactions to a cluster correctly.
type AccessMode int
const (
// AccessModeWrite makes the driver return a session towards a write server
AccessModeWrite AccessMode = 0
// AccessModeRead makes the driver return a session towards a follower or a read-replica
AccessModeRead AccessMode = 1
)
// Connector represents an initialised seabolt connector
type Connector interface {
Acquire(mode AccessMode) (Connection, error)
Close() error
}
func NewConnector(uri *url.URL, authToken map[string]interface{}, config *Config) (Connector, error) {
if workersEnabled() {
return newWorkerConnector(uri, authToken, config)
} else {
return newSeaboltConnector(uri, authToken, config)
}
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
/*
#include <stdlib.h>
#include "bolt/bolt.h"
*/
import "C"
import (
"errors"
"net/url"
"reflect"
"unsafe"
)
type seaboltConnector struct {
key int
uri *url.URL
authToken map[string]interface{}
config Config
cAddress *C.BoltAddress
cInstance *C.BoltConnector
valueSystem *boltValueSystem
}
func (conn *seaboltConnector) Close() error {
if conn.cInstance != nil {
C.BoltConnector_destroy(conn.cInstance)
conn.cInstance = nil
}
if conn.cAddress != nil {
C.BoltAddress_destroy(conn.cAddress)
conn.cAddress = nil
}
unregisterLogging(conn.key)
unregisterResolver(conn.key)
shutdownLibrary()
return nil
}
func (conn *seaboltConnector) Acquire(mode AccessMode) (Connection, error) {
return newSeaboltConnection(conn, mode)
}
// NewConnector returns a new connector instance with given parameters
func newSeaboltConnector(uri *url.URL, authToken map[string]interface{}, config *Config) (*seaboltConnector, error) {
var err error
var key int
var cAddress *C.struct_BoltAddress
var valueSystem *boltValueSystem
var cAuthToken *C.struct_BoltValue
var cConfig *C.struct_BoltConfig
if uri == nil {
return nil, errors.New("provided uri should not be nil")
}
if config == nil {
config = &Config{
Encryption: true,
MaxPoolSize: 100,
}
}
valueSystem = createValueSystem(config)
cAddress = createAddress(uri)
key = startupLibrary()
if cAuthToken, err = valueSystem.valueToConnector(authToken); err != nil {
return nil, valueSystem.genericErrorFactory("unable to convert authentication token: %v", err)
}
defer C.BoltValue_destroy(cAuthToken)
if cConfig, err = createConfig(key, uri, config, valueSystem); err != nil {
return nil, err
}
defer C.BoltConfig_destroy(cConfig)
cInstance := C.BoltConnector_create(cAddress, cAuthToken, cConfig)
conn := &seaboltConnector{
key: key,
uri: uri,
authToken: authToken,
config: *config,
cAddress: cAddress,
valueSystem: valueSystem,
cInstance: cInstance,
}
return conn, nil
}
func createValueSystem(config *Config) *boltValueSystem {
valueHandlersBySignature := make(map[int16]ValueHandler, len(config.ValueHandlers))
valueHandlersByType := make(map[reflect.Type]ValueHandler, len(config.ValueHandlers))
for _, handler := range config.ValueHandlers {
for _, readSignature := range handler.ReadableStructs() {
valueHandlersBySignature[readSignature] = handler
}
for _, writeType := range handler.WritableTypes() {
valueHandlersByType[writeType] = handler
}
}
databaseErrorFactory := newDatabaseError
connectorErrorFactory := newConnectorError
genericErrorFactory := newGenericError
if config.DatabaseErrorFactory != nil {
databaseErrorFactory = config.DatabaseErrorFactory
}
if config.ConnectorErrorFactory != nil {
connectorErrorFactory = config.ConnectorErrorFactory
}
if config.GenericErrorFactory != nil {
genericErrorFactory = config.GenericErrorFactory
}
return &boltValueSystem{
valueHandlers: config.ValueHandlers,
valueHandlersBySignature: valueHandlersBySignature,
valueHandlersByType: valueHandlersByType,
connectorErrorFactory: connectorErrorFactory,
databaseErrorFactory: databaseErrorFactory,
genericErrorFactory: genericErrorFactory,
}
}
func createAddress(uri *url.URL) *C.struct_BoltAddress {
var hostname = C.CString(uri.Hostname())
var port = C.CString(uri.Port())
defer C.free(unsafe.Pointer(hostname))
defer C.free(unsafe.Pointer(port))
return C.BoltAddress_create(hostname, port)
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
import (
"net/url"
"os"
"strconv"
"time"
)
type workerConnector struct {
config Config
delegate *seaboltConnector
pool *workerPool
closeSignal chan signal
}
func (w *workerConnector) Acquire(mode AccessMode) (Connection, error) {
return newWorkerConnection(w, mode)
}
func (w *workerConnector) Close() error {
var err error
var done = make(chan bool, 1)
if poolErr := w.pool.submit(func(stopper <-chan signal) {
err = w.delegate.Close()
done <- true
}); poolErr != nil {
err = poolErr
done <- true
}
<-done
if err != nil {
return err
}
w.pool.close()
close(w.closeSignal)
return nil
}
func newWorkerConnector(url *url.URL, authToken map[string]interface{}, config *Config) (Connector, error) {
var err error
var connector *seaboltConnector
var pool = newWorkerPool(minWorkers(config), maxWorkers(config), keepAlive(config))
var configOverride = *config
configOverride.ConnAcquisitionTimeout = 0
var done = make(chan bool, 1)
if poolErr := pool.submit(func(stopper <-chan signal) {
connector, err = newSeaboltConnector(url, authToken, &configOverride)
done <- true
}); poolErr != nil {
err = poolErr
done <- true
}
// wait for connector creation to complete
<-done
if err != nil {
defer pool.close()
return nil, err
}
return &workerConnector{
config: *config,
delegate: connector,
pool: pool,
closeSignal: make(chan signal, config.MaxPoolSize),
}, nil
}
func workersEnabled() bool {
var workersEnabled = true
if val, ok := os.LookupEnv("BOLTWORKERS"); ok {
if parsed, err := strconv.ParseBool(val); err == nil {
workersEnabled = parsed
}
}
return workersEnabled
}
func maxWorkers(config *Config) int {
var workersMax = int(float64(config.MaxPoolSize) * float64(1.2))
if val, ok := os.LookupEnv("BOLTWORKERSMAX"); ok {
if parsed, err := strconv.ParseInt(val, 10, 32); err == nil {
workersMax = int(parsed)
}
}
return workersMax
}
func minWorkers(config *Config) int {
var workersMin = 0
if val, ok := os.LookupEnv("BOLTWORKERSMIN"); ok {
if parsed, err := strconv.ParseInt(val, 10, 32); err == nil {
workersMin = int(parsed)
}
}
return workersMin
}
func keepAlive(config *Config) time.Duration {
var workersKeepAlive = 5 * time.Minute
if val, ok := os.LookupEnv("BOLTWORKERSKEEPALIVE"); ok {
if parsed, err := time.ParseDuration(val); err == nil {
workersKeepAlive = parsed
}
}
return workersKeepAlive
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
/*
#include "bolt/bolt.h"
*/
import "C"
import (
"fmt"
"strings"
)
// BoltError is a marker interface to identify neo4j errors
type BoltError interface {
BoltError() bool
}
// DatabaseError represents errors returned from the server a FAILURE messages
type DatabaseError interface {
// Classification returns classification of the error returned from the database
Classification() string
// Code returns code of the error returned from the database
Code() string
// Message returns message of the error returned from the database
Message() string
// Error returns textual representation of the error returned from the database
Error() string
}
// ConnectorError represents errors that occur on the connector/client side, like network errors, etc.
type ConnectorError interface {
// State returns the state of the related connection
State() int
// Code returns the error code set on the related connection
Code() int
// Context returns the error context set by the connector
Context() string
// Description returns any additional description set
Description() string
// Error returns textual representation of the connector level error
Error() string
}
// GenericError represents errors which originates from the connector wrapper itself
type GenericError interface {
// Message returns the underlying error message
Message() string
// Error returns textual representation of the generic error
Error() string
}
type defaultDatabaseError struct {
classification string
code string
message string
}
type defaultConnectorError struct {
state int
code int
codeText string
context string
description string
}
type defaultGenericError struct {
message string
}
func (failure *defaultDatabaseError) BoltError() bool {
return true
}
func (failure *defaultDatabaseError) Classification() string {
return failure.classification
}
func (failure *defaultDatabaseError) Code() string {
return failure.code
}
func (failure *defaultDatabaseError) Message() string {
return failure.message
}
func (failure *defaultDatabaseError) Error() string {
return fmt.Sprintf("database returned error [%s]: %s", failure.code, failure.message)
}
func (failure *defaultConnectorError) BoltError() bool {
return true
}
func (failure *defaultConnectorError) State() int {
return failure.state
}
func (failure *defaultConnectorError) Code() int {
return failure.code
}
func (failure *defaultConnectorError) Context() string {
return failure.context
}
func (failure *defaultConnectorError) Description() string {
return failure.description
}
func (failure *defaultConnectorError) Error() string {
if failure.description != "" {
return fmt.Sprintf("%s: error: [%d] %s, state: %d, context: %s", failure.description, failure.code, failure.codeText, failure.state, failure.context)
}
return fmt.Sprintf("error: [%d] %s, state: %d, context: %s", failure.code, failure.codeText, failure.state, failure.context)
}
func (failure *defaultGenericError) BoltError() bool {
return true
}
func (failure *defaultGenericError) Message() string {
return failure.message
}
func (failure *defaultGenericError) Error() string {
return failure.message
}
func newError(connection *seaboltConnection, description string) error {
cStatus := C.BoltConnection_status(connection.cInstance)
errorCode := C.BoltStatus_get_error(cStatus)
if errorCode == C.BOLT_SERVER_FAILURE {
failure, err := connection.valueSystem.valueAsDictionary(C.BoltConnection_failure(connection.cInstance))
if err != nil {
return connection.valueSystem.genericErrorFactory("unable to construct database error: %s", err.Error())
}
var ok bool
var codeInt, messageInt interface{}
var code, message string
if codeInt, ok = failure["code"]; !ok {
return connection.valueSystem.genericErrorFactory("expected 'code' key to be present in map '%v'", failure)
}
if code, ok = codeInt.(string); !ok {
return connection.valueSystem.genericErrorFactory("expected 'code' value to be of type 'string': '%v'", codeInt)
}
if messageInt, ok = failure["message"]; !ok {
return connection.valueSystem.genericErrorFactory("expected 'message' key to be present in map '%v'", failure)
}
if message, ok = messageInt.(string); !ok {
return connection.valueSystem.genericErrorFactory("expected 'message' value to be of type 'string': '%v'", messageInt)
}
classification := ""
if codeParts := strings.Split(code, "."); len(codeParts) >= 2 {
classification = codeParts[1]
}
return connection.valueSystem.databaseErrorFactory(classification, code, message)
}
state := C.BoltStatus_get_state(cStatus)
errorText := C.GoString(C.BoltError_get_string(errorCode))
context := C.GoString(C.BoltStatus_get_error_context(cStatus))
return connection.valueSystem.connectorErrorFactory(int(state), int(errorCode), errorText, context, description)
}
func newGenericError(format string, args ...interface{}) GenericError {
return &defaultGenericError{message: fmt.Sprintf(format, args...)}
}
func newDatabaseError(classification, code, message string) DatabaseError {
return &defaultDatabaseError{code: code, message: message, classification: classification}
}
func newConnectorError(state int, code int, codeText, context, description string) ConnectorError {
return &defaultConnectorError{state: state, code: code, codeText: codeText, context: context, description: description}
}
// IsDatabaseError checkes whether given err is a DatabaseError
func IsDatabaseError(err error) bool {
if _, ok := err.(DatabaseError); !ok {
return false
}
if _, ok := err.(BoltError); !ok {
return false
}
return true
}
// IsConnectorError checkes whether given err is a ConnectorError
func IsConnectorError(err error) bool {
if _, ok := err.(ConnectorError); !ok {
return false
}
if _, ok := err.(BoltError); !ok {
return false
}
return true
}
// IsGenericError checkes whether given err is a GenericError
func IsGenericError(err error) bool {
if _, ok := err.(GenericError); !ok {
return false
}
if _, ok := err.(BoltError); !ok {
return false
}
return true
}
// IsTransientError checks whether given err is a transient error
func IsTransientError(err error) bool {
if _, ok := err.(BoltError); !ok {
return false
}
if dbErr, ok := err.(DatabaseError); ok {
if dbErr.Classification() == "TransientError" {
switch dbErr.Code() {
case "Neo.TransientError.Transaction.Terminated":
fallthrough
case "Neo.TransientError.Transaction.LockClientStopped":
return false
}
return true
}
}
return false
}
// IsWriteError checks whether given err can be classified as a write error
func IsWriteError(err error) bool {
if _, ok := err.(BoltError); !ok {
return false
}
if dbErr, ok := err.(DatabaseError); ok {
switch dbErr.Code() {
case "Neo.ClientError.Cluster.NotALeader":
fallthrough
case "Neo.ClientError.General.ForbiddenOnReadOnlyDatabase":
return true
}
}
return false
}
// IsServiceUnavailable checkes whether given err represents a service unavailable status
func IsServiceUnavailable(err error) bool {
if _, ok := err.(BoltError); !ok {
return false
}
if connErr, ok := err.(ConnectorError); ok {
switch connErr.Code() {
case C.BOLT_INTERRUPTED:
fallthrough
case C.BOLT_CONNECTION_RESET:
fallthrough
case C.BOLT_NO_VALID_ADDRESS:
fallthrough
case C.BOLT_TIMED_OUT:
fallthrough
case C.BOLT_CONNECTION_REFUSED:
fallthrough
case C.BOLT_NETWORK_UNREACHABLE:
fallthrough
case C.BOLT_TLS_ERROR:
fallthrough
case C.BOLT_END_OF_TRANSMISSION:
fallthrough
case C.BOLT_POOL_FULL:
fallthrough
case C.BOLT_ADDRESS_NOT_RESOLVED:
fallthrough
case C.BOLT_ROUTING_UNABLE_TO_RETRIEVE_ROUTING_TABLE:
fallthrough
case C.BOLT_ROUTING_UNABLE_TO_REFRESH_ROUTING_TABLE:
fallthrough
case C.BOLT_ROUTING_NO_SERVERS_TO_SELECT:
return true
}
}
return false
}
func IsSecurityError(err error) bool {
if _, ok := err.(BoltError); !ok {
return false
}
if connErr, ok := err.(ConnectorError); ok {
return connErr.Code() == C.BOLT_TLS_ERROR
}
return IsAuthenticationError(err)
}
func IsAuthenticationError(err error) bool {
if _, ok := err.(BoltError); !ok {
return false
}
if connErr, ok := err.(ConnectorError); ok {
return connErr.Code() == C.BOLT_PERMISSION_DENIED
}
if dbErr, ok := err.(DatabaseError); ok {
return dbErr.Code() == "Neo.ClientError.Security.Unauthorized"
}
return false
}
func IsClientError(err error) bool {
if _, ok := err.(BoltError); !ok {
return false
}
if dbErr, ok := err.(DatabaseError); ok {
if dbErr.Classification() == "ClientError" {
return dbErr.Code() != "Neo.ClientError.Security.Unauthorized"
}
return false
}
return IsGenericError(err)
}
func IsSessionExpired(err error) bool {
if _, ok := err.(BoltError); !ok {
return false
}
if connErr, ok := err.(ConnectorError); ok {
return connErr.Code() == C.BOLT_ROUTING_NO_SERVERS_TO_SELECT
}
return false
}
func isPoolFullError(err error) bool {
if connectorError, ok := err.(ConnectorError); ok {
return connectorError.Code() == C.BOLT_POOL_FULL
}
return false
}
func newConnectionAcquisitionTimedOutError(valueSystem *boltValueSystem) error {
return valueSystem.connectorErrorFactory(C.BOLT_CONNECTION_STATE_DISCONNECTED, C.BOLT_POOL_ACQUISITION_TIMED_OUT, C.GoString(C.BoltError_get_string(C.BOLT_POOL_ACQUISITION_TIMED_OUT)), "", "")
}
func newPoolFullError(valueSystem *boltValueSystem) error {
return valueSystem.connectorErrorFactory(C.BOLT_CONNECTION_STATE_DISCONNECTED, C.BOLT_POOL_FULL, C.GoString(C.BoltError_get_string(C.BOLT_POOL_FULL)), "", "")
}
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
/*
#include <stdlib.h>
#include "bolt/bolt.h"
*/
import "C"
import "sync/atomic"
var initCounter int32
func startupLibrary() int {
counter := atomic.AddInt32(&initCounter, 1)
if counter == 1 {
C.Bolt_startup()
}
return int(counter)
}
func shutdownLibrary() {
if atomic.AddInt32(&initCounter, -1) == 0 {
C.Bolt_shutdown()
}
}
此差异已折叠。
此差异已折叠。
// +build seabolt_static
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
// #cgo pkg-config: seabolt17-static
import "C"
// +build !seabolt_static
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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.
*/
package gobolt
// #cgo pkg-config: seabolt17
import "C"
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册