feat(errorsx): cover more syscall errors and add tests (#430)

* feat(errorsx): cover more syscall errors and add tests

Part of https://github.com/ooni/probe/issues/1505

* fix tests
This commit is contained in:
Simone Basso 2021-07-08 12:23:37 +02:00 committed by GitHub
parent 3747598b4a
commit 14c1640f7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 293 additions and 48 deletions

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-07-02 15:15:17.997258 +0200 CEST m=+0.110031584
// Generated: 2021-07-02 17:54:06.182341 +0200 CEST m=+0.120124584
package errorsx
@ -10,6 +10,42 @@ import (
"syscall"
)
// This enumeration lists the syscall-derived failures defined at
// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md
//
// See also the enumeration at failures.go for the failures that
// DO NOT derive from system call errors.
const (
FailureOperationCanceled = "operation_canceled"
FailureConnectionRefused = "connection_refused"
FailureConnectionReset = "connection_reset"
FailureHostUnreachable = "host_unreachable"
FailureTimedOut = "timed_out"
FailureAddressFamilyNotSupported = "address_family_not_supported"
FailureAddressInUse = "address_in_use"
FailureAddressNotAvailable = "address_not_available"
FailureAlreadyConnected = "already_connected"
FailureBadAddress = "bad_address"
FailureBadFileDescriptor = "bad_file_descriptor"
FailureConnectionAborted = "connection_aborted"
FailureConnectionAlreadyInProgress = "connection_already_in_progress"
FailureDestinationAddressRequired = "destination_address_required"
FailureInterrupted = "interrupted"
FailureInvalidArgument = "invalid_argument"
FailureMessageSize = "message_size"
FailureNetworkDown = "network_down"
FailureNetworkReset = "network_reset"
FailureNetworkUnreachable = "network_unreachable"
FailureNoBufferSpace = "no_buffer_space"
FailureNoProtocolOption = "no_protocol_option"
FailureNotASocket = "not_a_socket"
FailureNotConnected = "not_connected"
FailureOperationWouldBlock = "operation_would_block"
FailurePermissionDenied = "permission_denied"
FailureProtocolNotSupported = "protocol_not_supported"
FailureWrongProtocolType = "wrong_protocol_type"
)
// toSyscallErr converts a syscall error to the
// proper OONI error. Returns the OONI error string
// on success, an empty string otherwise.
@ -23,7 +59,7 @@ func toSyscallErr(err error) string {
}
switch errno {
case ECANCELED:
return FailureInterrupted
return FailureOperationCanceled
case ECONNREFUSED:
return FailureConnectionRefused
case ECONNRESET:
@ -31,7 +67,53 @@ func toSyscallErr(err error) string {
case EHOSTUNREACH:
return FailureHostUnreachable
case ETIMEDOUT:
return FailureGenericTimeoutError
return FailureTimedOut
case EAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case EADDRINUSE:
return FailureAddressInUse
case EADDRNOTAVAIL:
return FailureAddressNotAvailable
case EISCONN:
return FailureAlreadyConnected
case EFAULT:
return FailureBadAddress
case EBADF:
return FailureBadFileDescriptor
case ECONNABORTED:
return FailureConnectionAborted
case EALREADY:
return FailureConnectionAlreadyInProgress
case EDESTADDRREQ:
return FailureDestinationAddressRequired
case EINTR:
return FailureInterrupted
case EINVAL:
return FailureInvalidArgument
case EMSGSIZE:
return FailureMessageSize
case ENETDOWN:
return FailureNetworkDown
case ENETRESET:
return FailureNetworkReset
case ENETUNREACH:
return FailureNetworkUnreachable
case ENOBUFS:
return FailureNoBufferSpace
case ENOPROTOOPT:
return FailureNoProtocolOption
case ENOTSOCK:
return FailureNotASocket
case ENOTCONN:
return FailureNotConnected
case EWOULDBLOCK:
return FailureOperationWouldBlock
case EACCES:
return FailurePermissionDenied
case EPROTONOSUPPORT:
return FailureProtocolNotSupported
case EPROTOTYPE:
return FailureWrongProtocolType
}
return ""
}

View File

@ -0,0 +1,103 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-07-02 17:54:06.226224 +0200 CEST m=+0.164008293
package errorsx
import (
"io"
"syscall"
"testing"
)
func TestToSyscallErr(t *testing.T) {
if v := toSyscallErr(io.EOF); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
if v := toSyscallErr(ECANCELED); v != FailureOperationCanceled {
t.Fatalf("expected '%s', got '%s'", FailureOperationCanceled, v)
}
if v := toSyscallErr(ECONNREFUSED); v != FailureConnectionRefused {
t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v)
}
if v := toSyscallErr(ECONNRESET); v != FailureConnectionReset {
t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v)
}
if v := toSyscallErr(EHOSTUNREACH); v != FailureHostUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v)
}
if v := toSyscallErr(ETIMEDOUT); v != FailureTimedOut {
t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v)
}
if v := toSyscallErr(EAFNOSUPPORT); v != FailureAddressFamilyNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v)
}
if v := toSyscallErr(EADDRINUSE); v != FailureAddressInUse {
t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v)
}
if v := toSyscallErr(EADDRNOTAVAIL); v != FailureAddressNotAvailable {
t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v)
}
if v := toSyscallErr(EISCONN); v != FailureAlreadyConnected {
t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v)
}
if v := toSyscallErr(EFAULT); v != FailureBadAddress {
t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v)
}
if v := toSyscallErr(EBADF); v != FailureBadFileDescriptor {
t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v)
}
if v := toSyscallErr(ECONNABORTED); v != FailureConnectionAborted {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v)
}
if v := toSyscallErr(EALREADY); v != FailureConnectionAlreadyInProgress {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v)
}
if v := toSyscallErr(EDESTADDRREQ); v != FailureDestinationAddressRequired {
t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v)
}
if v := toSyscallErr(EINTR); v != FailureInterrupted {
t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v)
}
if v := toSyscallErr(EINVAL); v != FailureInvalidArgument {
t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v)
}
if v := toSyscallErr(EMSGSIZE); v != FailureMessageSize {
t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v)
}
if v := toSyscallErr(ENETDOWN); v != FailureNetworkDown {
t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v)
}
if v := toSyscallErr(ENETRESET); v != FailureNetworkReset {
t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v)
}
if v := toSyscallErr(ENETUNREACH); v != FailureNetworkUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v)
}
if v := toSyscallErr(ENOBUFS); v != FailureNoBufferSpace {
t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v)
}
if v := toSyscallErr(ENOPROTOOPT); v != FailureNoProtocolOption {
t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v)
}
if v := toSyscallErr(ENOTSOCK); v != FailureNotASocket {
t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v)
}
if v := toSyscallErr(ENOTCONN); v != FailureNotConnected {
t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v)
}
if v := toSyscallErr(EWOULDBLOCK); v != FailureOperationWouldBlock {
t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v)
}
if v := toSyscallErr(EACCES); v != FailurePermissionDenied {
t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v)
}
if v := toSyscallErr(EPROTONOSUPPORT); v != FailureProtocolNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v)
}
if v := toSyscallErr(EPROTOTYPE); v != FailureWrongProtocolType {
t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v)
}
if v := toSyscallErr(syscall.Errno(0)); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
}

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-07-02 15:15:17.887594 +0200 CEST m=+0.000365001
// Generated: 2021-07-02 17:54:06.062944 +0200 CEST m=+0.000724793
package errorsx

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-07-02 15:15:17.971155 +0200 CEST m=+0.083928418
// Generated: 2021-07-02 17:54:06.155868 +0200 CEST m=+0.093650376
package errorsx

View File

@ -54,7 +54,7 @@ func TestToFailureString(t *testing.T) {
}
})
t.Run("for canceled", func(t *testing.T) {
if toFailureString(syscall.ECANCELED) != FailureInterrupted {
if toFailureString(syscall.ECANCELED) != FailureOperationCanceled {
t.Fatal("unexpected results")
}
})
@ -74,7 +74,7 @@ func TestToFailureString(t *testing.T) {
}
})
t.Run("for system timeout", func(t *testing.T) {
if toFailureString(syscall.ETIMEDOUT) != FailureGenericTimeoutError {
if toFailureString(syscall.ETIMEDOUT) != FailureTimedOut {
t.Fatal("unexpected results")
}
})

View File

@ -1,14 +1,11 @@
package errorsx
// This enumeration lists all the failures defined at
// This enumeration lists the non-syscall-derived failures defined at
// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md
//
// See also the enumeration at errno.go for the failures that
// derive directly from system call errors.
const (
// FailureConnectionRefused means ECONNREFUSED.
FailureConnectionRefused = "connection_refused"
// FailureConnectionReset means ECONNRESET.
FailureConnectionReset = "connection_reset"
// FailureDNSBogonError means we detected bogon in DNS reply.
FailureDNSBogonError = "dns_bogon_error"
@ -21,12 +18,6 @@ const (
// FailureGenericTimeoutError means we got some timer has expired.
FailureGenericTimeoutError = "generic_timeout_error"
// FailureHostUnreachable means that there is "no route to host".
FailureHostUnreachable = "host_unreachable"
// FailureInterrupted means that the user interrupted us.
FailureInterrupted = "interrupted"
// FailureNoCompatibleQUICVersion means that the server does not support the proposed QUIC version
FailureNoCompatibleQUICVersion = "quic_incompatible_version"

View File

@ -6,6 +6,7 @@ import (
"os"
"time"
"github.com/iancoleman/strcase"
"golang.org/x/sys/execabs"
)
@ -18,13 +19,10 @@ type ErrorSpec struct {
Failure string
}
// TODO(kelmenhorst): find out if we need more system errors here. Currently
// the code does not generate any mapping if Failure is empty.
// Specs contains all the error specs.
var Specs = []ErrorSpec{{
Errno: "ECANCELED",
Failure: "Interrupted",
Failure: "OperationCanceled",
}, {
Errno: "ECONNREFUSED",
Failure: "ConnectionRefused",
@ -36,53 +34,76 @@ var Specs = []ErrorSpec{{
Failure: "HostUnreachable",
}, {
Errno: "ETIMEDOUT",
Failure: "GenericTimeoutError",
Failure: "TimedOut",
}, {
Errno: "EAFNOSUPPORT",
Errno: "EAFNOSUPPORT",
Failure: "AddressFamilyNotSupported",
}, {
Errno: "EADDRINUSE",
Errno: "EADDRINUSE",
Failure: "AddressInUse",
}, {
Errno: "EADDRNOTAVAIL",
Errno: "EADDRNOTAVAIL",
Failure: "AddressNotAvailable",
}, {
Errno: "EISCONN",
Errno: "EISCONN",
Failure: "AlreadyConnected",
}, {
Errno: "EFAULT",
Errno: "EFAULT",
Failure: "BadAddress",
}, {
Errno: "EBADF",
Errno: "EBADF",
Failure: "BadFileDescriptor",
}, {
Errno: "ECONNABORTED",
Errno: "ECONNABORTED",
Failure: "ConnectionAborted",
}, {
Errno: "EALREADY",
Errno: "EALREADY",
Failure: "ConnectionAlreadyInProgress",
}, {
Errno: "EDESTADDRREQ",
Errno: "EDESTADDRREQ",
Failure: "DestinationAddressRequired",
}, {
Errno: "EINTR",
Errno: "EINTR",
Failure: "Interrupted",
}, {
Errno: "EINVAL",
Errno: "EINVAL",
Failure: "InvalidArgument",
}, {
Errno: "EMSGSIZE",
Errno: "EMSGSIZE",
Failure: "MessageSize",
}, {
Errno: "ENETDOWN",
Errno: "ENETDOWN",
Failure: "NetworkDown",
}, {
Errno: "ENETRESET",
Errno: "ENETRESET",
Failure: "NetworkReset",
}, {
Errno: "ENETUNREACH",
Errno: "ENETUNREACH",
Failure: "NetworkUnreachable",
}, {
Errno: "ENOBUFS",
Errno: "ENOBUFS",
Failure: "NoBufferSpace",
}, {
Errno: "ENOPROTOOPT",
Errno: "ENOPROTOOPT",
Failure: "NoProtocolOption",
}, {
Errno: "ENOTSOCK",
Errno: "ENOTSOCK",
Failure: "NotASocket",
}, {
Errno: "ENOTCONN",
Errno: "ENOTCONN",
Failure: "NotConnected",
}, {
Errno: "EWOULDBLOCK",
Errno: "EWOULDBLOCK",
Failure: "OperationWouldBlock",
}, {
Errno: "EACCES",
Errno: "EACCES",
Failure: "PermissionDenied",
}, {
Errno: "EPROTONOSUPPORT",
Errno: "EPROTONOSUPPORT",
Failure: "ProtocolNotSupported",
}, {
Errno: "EPROTOTYPE",
Errno: "EPROTOTYPE",
Failure: "WrongProtocolType",
}}
func fileCreate(filename string) *os.File {
@ -143,6 +164,19 @@ func writeGenericFile() {
fileWrite(filep, "\t\"errors\"\n")
fileWrite(filep, "\t\"syscall\"\n")
fileWrite(filep, ")\n\n")
fileWrite(filep, "// This enumeration lists the syscall-derived failures defined at\n")
fileWrite(filep, "// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md\n")
fileWrite(filep, "//\n")
fileWrite(filep, "// See also the enumeration at failures.go for the failures that\n")
fileWrite(filep, "// DO NOT derive from system call errors.\n")
fileWrite(filep, "const (\n")
for _, spec := range Specs {
filePrintf(filep, "\tFailure%s = \"%s\"\n", spec.Failure,
strcase.ToSnake(spec.Failure))
}
fileWrite(filep, ")\n\n")
fileWrite(filep, "// toSyscallErr converts a syscall error to the\n")
fileWrite(filep, "// proper OONI error. Returns the OONI error string\n")
fileWrite(filep, "// on success, an empty string otherwise.\n")
@ -164,6 +198,40 @@ func writeGenericFile() {
fileWrite(filep, "\t}\n")
fileWrite(filep, "\treturn \"\"\n")
fileWrite(filep, "}\n\n")
fileClose(filep)
gofmt(filename)
}
func writeGenericTestFile() {
filename := "errno_test.go"
filep := fileCreate(filename)
fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n")
filePrintf(filep, "// Generated: %+v\n\n", time.Now())
fileWrite(filep, "package errorsx\n\n")
fileWrite(filep, "import (\n")
fileWrite(filep, "\t\"io\"\n")
fileWrite(filep, "\t\"syscall\"\n")
fileWrite(filep, "\t\"testing\"\n")
fileWrite(filep, ")\n\n")
fileWrite(filep, "func TestToSyscallErr(t *testing.T) {\n")
fileWrite(filep, "\tif v := toSyscallErr(io.EOF); v != \"\" {\n")
fileWrite(filep, "\t\tt.Fatalf(\"expected empty string, got '%s'\", v)\n")
fileWrite(filep, "\t}\n")
for _, spec := range Specs {
filePrintf(filep, "\tif v := toSyscallErr(%s); v != Failure%s {\n", spec.Errno, spec.Failure)
filePrintf(filep, "\t\tt.Fatalf(\"expected '%%s', got '%%s'\", Failure%s, v)\n", spec.Failure)
fileWrite(filep, "\t}\n")
}
fileWrite(filep, "\tif v := toSyscallErr(syscall.Errno(0)); v != \"\" {\n")
fileWrite(filep, "\t\tt.Fatalf(\"expected empty string, got '%s'\", v)\n")
fileWrite(filep, "\t}\n")
fileWrite(filep, "}\n")
fileClose(filep)
gofmt(filename)
}
@ -172,4 +240,5 @@ func main() {
writeSystemSpecificFile("unix")
writeSystemSpecificFile("windows")
writeGenericFile()
writeGenericTestFile()
}