In pursuit of 100% unit test coverage, we have several lines we're trying to test in one of our functions. The relevant function calls out to the runtime package:
// functionName returns a string representing the function name of the function n stack frames above the caller.
// if n = 0, the name of the function calling functionName() will be returned.
func functionName(n int) string {
pc, _, _, ok := runtime.Caller(n + 1)
if !ok {
return "unknown function"
}
me := runtime.FuncForPC(pc)
if me == nil {
return "unknown function"
}
split := strings.Split(me.Name(), ".")
if len(split) == 0 {
return "unknown function"
}
return split[len(split)-1]
}
Specifically, the 3 if statements and their return values are currently untested, because the runtime functions don't appear to be easily manipulated to return the values we want. Our standard response in these cases is to mock out the items in question, but these calls are to package-level functions (rather than methods of an interface) within the runtime package itself.
My first thought was to mock out the runtime token itself by using a structure with Caller() and FuncForPC() methods, assigned to a variable named "runtime" in the test files (so it wouldn't affect the production code flow, since test files are omitted during normal builds). However, this triggers a build error about "runtime" being redeclared within the (global) block.
I know this would be possible if the "runtime" variable were declare in a non-global scope (example masking fmt), but I can't find an elegant way to do so such that it gets masked within the tests, but not within the production code itself. The only way I've thought of is by altering the source of the production code to declare such a variable and replacing it's value in the tests, but this is far from ideal, since it complicates the production code purely for the purposes of testing.
Any ideas?
One solution is to declare variables of those functions you want to mock.
var runtimeCaller = runtime.Caller
var runtimeFuncForPC = runtime.FuncForPC
func functionName(n int) string {
pc, _, _, ok := runtimeCaller(n + 1)
if !ok {
return "unknown function"
}
me := runtimeFuncForPC(pc)
if me == nil {
return "unknown function"
}
split := strings.Split(me.Name(), ".")
if len(split) == 0 {
return "unknown function"
}
return split[len(split)-1]
}
Or if you prefer the dot notation...
var _runtime = struct{
Caller func(skip int) (pc uintptr, file string, line int, ok bool)
FuncForPC func(pc uintptr) *runtime.Func
}{runtime.Caller, runtime.FuncForPC}
func functionName(n int) string {
pc, _, _, ok := _runtime.Caller(n + 1)
if !ok {
return "unknown function"
}
me := _runtime.FuncForPC(pc)
if me == nil {
return "unknown function"
}
split := strings.Split(me.Name(), ".")
if len(split) == 0 {
return "unknown function"
}
return split[len(split)-1]
}
And in your tests, before running functionName, you can set the variables/fields to mock implementations. And if other tests may cause the functionName to be called beware of concurrent access... I don't think there is much else you can do without changing the existing code significantly.
On the reliability of programs. Edsger W. Dijkstra
The first moral of the story is that program testing can be used very
effectively to show the presence of bugs but never to show their
absence.
Let's read your code. Go type int is a 32- or 64-bit signed integer. Therefore, consider,
funcname.go:
package main
import (
"fmt"
"runtime"
"strings"
)
// functionName returns a string representing the function name of the function n stack frames above the caller.
// if n = 0, the name of the function calling functionName() will be returned.
func functionName(n int) string {
pc, _, _, ok := runtime.Caller(n + 1)
if !ok {
return "unknown function"
}
me := runtime.FuncForPC(pc)
if me == nil {
return "unknown function"
}
split := strings.Split(me.Name(), ".")
if len(split) == 0 {
return "unknown function"
}
return split[len(split)-1]
}
func main() {
for skip := -4; skip <= 4; skip++ {
fn := functionName(skip)
fmt.Println(functionName(0), skip, fn)
}
const (
sizeInt = 32 << (^uint(0) >> 63)
maxInt = 1<<(sizeInt-1) - 1
minInt = -1 << (sizeInt - 1)
)
for _, skip := range []int{minInt, maxInt} {
fn := functionName(skip)
fmt.Println(functionName(0), skip, fn)
}
}
Output:
$ go run funcname.go
main -4 skipPleaseUseCallersFrames
main -3 skipPleaseUseCallersFrames
main -2 skipPleaseUseCallersFrames
main -1 functionName
main 0 main
main 1 main
main 2 goexit
main 3 unknown function
main 4 unknown function
main -9223372036854775808 skipPleaseUseCallersFrames
main 9223372036854775807 skipPleaseUseCallersFrames
$
It looks like a bug in functionName to me. What did your coverage testing say about this?
Reading your code there appears to be no reliable way to detect the return of an error value. One way would be to return an empty string. If you want to use a special value such as "unknown function" then provide a value to check against. For example,
const functionUnknown = "unknown function"
func functionName(n int) string {
pc, file, line, ok := runtime.Caller(n + 1)
if !ok {
return functionUnknown
}
// . . .
}
func main() {
fn := functionName(0)
if fn == functionUnknown {
// handle error
}
}
What did your coverage testing say about this?
Related
New in Go, couldn't find any intuitive way of doing that.
I have such piece of code
tx = getTx()
for _, record := range tx.a {
// do a lot with record.Important
}
for _, record := range tx.b {
// do a lot with record.Important
}
for _, record := range tx.c {
// do a lot with record.Important
}
And the following structs:
type Record1 struct {
// fields of Record1
Important string
}
type Record2 struct {
// fields of Record1
Important string
}
type TX struct {
a []Record1
b []Record1
c []Record2
}
Now the logical is to extract every for logic into the function:
func helper(records) { // Here is the problem
// do a lot with record.Important
}
Problem:
records is a []Record1 or []Record2 type. But it looks like Union types doesn't exists in Golang. So I thought I could pass []string into the helper, but cannot even find an elegant way to get something equivalent to map(lambda r: r.Important, tx.a). There is no high order map function, no list comprehesion. I am not convinced to use raw for loop to solve that.
One approach to do the loop across multiple types is to use interfaces together with generics. Have each Record type implement a getter method for the important field. Then declare an interface that includes that getter method in its method set. Then you can make your helper generic by declaring the interface as its type parameter.
func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }
type ImportantGetter interface {
GetImporant() string
}
func helper[T ImportantGetter](s []T) {
for _, v := range s {
_ = v.GetImportant()
}
}
Unless I'm misunderstanding your question, it seems like you want to extract all the values in column X from a set of records and then pass those values in as a slice to some function - I'm basing my assumption on your wish that go had something like map().
If what you're after is type-agnosticism, you could certainly use an interface approach like that suggested by mkopriva, but you aren't going to get out of using a for loop - iteration over list types is core to idiomatic go. If you need a mapping function, you're going to have to write one that performs the mapping you want.
I'd note that you do not need generics to do what mkopriva suggests, you can just use an interface without muddying the waters with generics go playground:
package main
import "fmt"
type Record1 struct {
Important string
}
type Record2 struct {
Important string
}
func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }
type ImportantGetter interface {
GetImportant() string
}
func helper(s []ImportantGetter) {
for _, v := range s {
fmt.Println(v.GetImportant())
}
}
func main() {
records := []ImportantGetter{Record1{Important: "foo"}, Record2{Important: "bar"}}
helper(records)
}
Another approach to the type-agnosticism, and one that's a bit more (IMHO) idiomatic for "I expect all of these types to have a common property," is to use struct embedding and type assertions to build your own Map() function up go playground:
type CommonFields struct {
Important string
}
type Record1 struct {
CommonFields
FieldSpecificToRecord1 string
}
type Record2 struct {
CommonFields
FieldSpecificToRecord2 int
}
func main() {
r1 := Record1{
CommonFields{Important: "I'm r1!"},
"foo",
}
r2 := Record2{
CommonFields{Important: "I'm r2!"},
5,
}
records := []interface{}{r1, r2, "this is not a valid record type"}
fmt.Println(Map(records))
}
func Map(source []interface{}) []string {
destination := make([]string, len(source))
for i, sourceRecord := range source {
if rr, ok := sourceRecord.(Record1); ok {
destination[i] = rr.Important
} else if rr, ok := sourceRecord.(Record2); ok {
destination[i] = rr.Important
} else {
destination[i] = "undefined"
}
}
return destination
}
You'd likely want to make your implementation of Map() accept an argument specifying the field to extract to conform to what you have in other languages, or possibly even just pass in a helper function which does most of the type-specific value extraction.
I have currently the following function in one file:
func pinExported(pin int) bool {
pinPath := fmt.Sprintf("/sys/class/gpio/gpio%d", pin)
if file, err := os.Stat(pinPath); err == nil && len(file.Name()) > 0 {
return true
}
return false
}
and another code part in the same file which uses the above function which looks like this:
func isGpioPinExported(gpioPin int) bool {
exported := pinExported(gpioPin)
for !exported && (timeOut < timeOutForPinExportInMilliseconds) {
timeOut++
time.Sleep(1 * time.Millisecond)
exported = pinExported(gpioPin)
}
...
So now I'm searching for an elegant way to mock/replace somehow the above pinExported function within my unit tests to test the logic inside isGpioPinExported because the function pinExported is hardware dependent (Raspberry PI).
One solution could be to make the pinExported function a parameter of isGpioPinExported
So defining a function type like this:
type pinExported func(int) int
which means I have to define isGpioPinExported like this:
isGpioPinExported(pinExported pinExported, gpioPin int) bool {
exported := pinExported(gpioPin)
for !exported && (timeOut < timeOutForPinExportInMilliseconds) {
...
}
..
}
Now I can write my unit test and define a mock/fake pinExported without a problem. So far so good. But I have about five or six of such functions which means it would result in putting five or six supplemental parameters to a function like isGpioPinExported which is simply wrong. Apart from that the question is where can I define the default implementation which are used if this is not running under test?
So based on the suggestion of mkopriva I have created an interface which looks like this (now with three functions to see how this really works):
type Raspberry interface {
isPinExported(gpioPin int) bool
valueExist(gpioPin int) bool
directionExist(gpioPin int) bool
}
Furthermore defined a structure to make the implementation for real hardware (Raspberry):
type Rasberry3Plus struct {
}
func (raspberry Rasberry3Plus) valueExist(gpioPin int) bool {
pinPath := fmt.Sprintf("%s%d/value", sysClassGPIOPin, gpioPin)
if file, err := os.Stat(pinPath); err == nil && len(file.Name()) > 0 {
return true
}
return false
}
func (raspberry Rasberry3Plus) directionExist(gpioPin int) bool {
pinPath := fmt.Sprintf("%s%d/direction", sysClassGPIOPin, gpioPin)
if file, err := os.Stat(pinPath); err == nil && len(file.Name()) > 0 {
return true
}
return false
}
func (raspberry Rasberry3Plus) isPinExported(gpioPin int) bool {
pinPath := fmt.Sprintf("%s%d", sysClassGPIOPin, gpioPin)
if file, err := os.Stat(pinPath); err == nil && len(file.Name()) > 0 {
return true
}
return false
}
and the function IsGpioPinExported which uses the above functions looks now like this (This is just an example implementation to see how the mocking testing works):
func IsGpioPinExported(raspberry Raspberry, gpioPin int) bool {
pinExported := raspberry.isPinExported(gpioPin)
valueExist := raspberry.valueExist(gpioPin)
directionExist := raspberry.directionExist(gpioPin)
return valueExist && directionExist && pinExported
}
So now the tests look like this. First I have to define a type (btw: I have decided to go with Mock):
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"testing"
)
type mockRaspberry struct {
mock.Mock
}
func (raspMock mockRaspberry) isPinExported(gpioPin int) bool {
args := raspMock.Called(gpioPin)
return args.Bool(0)
}
func (raspMock mockRaspberry) valueExist(gpioPin int) bool {
args := raspMock.Called(gpioPin)
return args.Bool(0)
}
func (raspMock mockRaspberry) directionExist(gpioPin int) bool {
args := raspMock.Called(gpioPin)
return args.Bool(0)
}
func Test_ValueTrue_DirectionExistTrue(t *testing.T) {
testObj := new(mockRaspberry)
testObj.On("isPinExported", 5).Return(false)
testObj.On("valueExist", 5).Return(true)
testObj.On("directionExist", 5).Return(true)
exported := IsGpioPinExported(testObj, 5)
assert.Equal(t, false, exported)
}
And now it is simple to test the loigic in function IsGpioPinExported with the appropriate mocked functions with wished result. And finally the main program looks like this:
func main() {
rasberry3Plus := gpio.Rasberry3Plus{}
gpio.IsGpioPinExported(rasberry3Plus, 23)
}
I'm trying to make a "if" statement in goroutine.
Question: how to make 10 from 10?
var jr = make(chan int, 10)
var clients = 10 // The number of clients varies with time.
func rpcMethod(num int) {
time.Sleep(time.Duration(rand.Intn(int(time.Second))))
jr <- num
}
func postHandler(num int) {
// wait RPC data
for {
select {
case msg := <-jr:
{
if msg == num {
fmt.Println(num, "hello from", msg)
return
}
}
}
}
}
func main() {
for i := 0; i < clients; i++ {
go postHandler(i)
go rpcMethod(i)
}
fmt.Scanln()
}
Result 2/10
5 hello from 5
2 hello from 2
Ok, there are multiple problems.
First and foremost, it does not work because when something is read from a channel, it disappears (it is not a broadcast, only one thread can read the message).
So in order for your code to pseudo-work, you could do this:
if msg == num {
fmt.Println(num, "hello from", msg)
return
}else {
// not my number, put it back in the channel
jr <- num
}
You will ge the expected result, but there is still a problem: your program won't shutdown properly. I guess this is only for experiment/learning purposes, but in a real program you would use a completely different code. Tell me if another version would interest you.
After postHandler receives msg from channel jr, that value is not in the channel anymore for another postHandler to find. Channels do not broadcast.
Either send the value back into the channel if it's not equal to num or restructure your code entirely.
This example is taken from http://blog.golang.org/pipelines. It runs and gives correct answer but it shows following runtime error: "fatal error: all goroutines are asleep - deadlock!". Could anyone help me understand why this is happening?
package main
import (
"fmt"
)
func gen(nums ...int) <- chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
}()
return out
}
func sq(in <- chan int) <- chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func main() {
for n := range sq(gen(2,3)) {
fmt.Println(n)
}
}
However the following modification doesn't.
func main() {
// Set up the pipeline.
c := gen(2, 3)
out := sq(c)
// Consume the output.
fmt.Println(<-out) // 4
fmt.Println(<-out) // 9
}
The for n := range in of the sq() function never exits, and start blocking (after reading 2 values), because gen() never closed its channel.
Adding close(out) to the go func of gen() would make it work: see playground.
With channel, the receiver blocks until receiving a value.
The range keyword, when used with a channel, will wait on the channel until it is closed.
sq() is blocked, which means close(out) is never called, and in turn main() blocks on range sq() (since the channel sq isn't closed).
In your second example, main() itself exits, which means even though sq() is blocked, everything still stops.
What is the idiomatic way to cast multiple return values in Go?
Can you do it in a single line, or do you need to use temporary variables such as I've done in my example below?
package main
import "fmt"
func oneRet() interface{} {
return "Hello"
}
func twoRet() (interface{}, error) {
return "Hejsan", nil
}
func main() {
// With one return value, you can simply do this
str1 := oneRet().(string)
fmt.Println("String 1: " + str1)
// It is not as easy with two return values
//str2, err := twoRet().(string) // Not possible
// Do I really have to use a temp variable instead?
temp, err := twoRet()
str2 := temp.(string)
fmt.Println("String 2: " + str2 )
if err != nil {
panic("unreachable")
}
}
By the way, is it called casting when it comes to interfaces?
i := interface.(int)
You can't do it in a single line.
Your temporary variable approach is the way to go.
By the way, is it called casting when it comes to interfaces?
It is actually called a type assertion.
A type cast conversion is different:
var a int
var b int64
a = 5
b = int64(a)
func silly() (interface{}, error) {
return "silly", nil
}
v, err := silly()
if err != nil {
// handle error
}
s, ok := v.(string)
if !ok {
// the assertion failed.
}
but more likely what you actually want is to use a type switch, like-a-this:
switch t := v.(type) {
case string:
// t is a string
case int :
// t is an int
default:
// t is some other type that we didn't name.
}
Go is really more about correctness than it is about terseness.
Or just in a single if:
if v, ok := value.(migrater); ok {
v.migrate()
}
Go will take care of the cast inside the if clause and let you access the properties of the casted type.
template.Must is the standard library's approach for returning only the first return value in one statement. Could be done similarly for your case:
func must(v interface{}, err error) interface{} {
if err != nil {
panic(err)
}
return v
}
// Usage:
str2 := must(twoRet()).(string)
By using must you basically say that there should never be an error, and if there is, then the program can't (or at least shouldn't) keep operating, and will panic instead.