Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Proposal: add support for mocking generic functions and methods #12

Closed
xhd2015 opened this issue Mar 29, 2024 · 1 comment
Closed

Proposal: add support for mocking generic functions and methods #12

xhd2015 opened this issue Mar 29, 2024 · 1 comment
Labels
done proposal Proposal new ideas

Comments

@xhd2015
Copy link
Owner

xhd2015 commented Mar 29, 2024

Generic functions and methods are currently skipped because their generic definition is just a template, there is no function instrumentation emitted until concrete types have been decided.

But since we use source code rewritting instead of IR rewritting for stdlib functions(see #6), it should be easy to also use source code rewritting for intercepting generic method and functions.

Generic function example:

package mock_generic_func

import (
	"context"
	"net/http"
	"testing"

	"github.com/xhd2015/xgo/runtime/core"
	"github.com/xhd2015/xgo/runtime/mock"
)

func ToString[T any](v T) string {
    return fmt.Sprint(v)
}

func TestMockGenericFunc(t *testing.T) {
	mock.Mock(ToString[int], func(ctx context.Context, fn *core.FuncInfo, args, results core.Object) error {
		results.GetFieldIndex(0).Set("hello world")
		return nil
	})
        expect := "hello world"
        output := ToString(0)
	if expect != output {
		t.Fatalf("expect ToString[int](0) to be %s, actual: %s", expect, output)
	}
}

Generic method example:

package mock_generic_method

import (
	"context"
	"net/http"
	"testing"

	"github.com/xhd2015/xgo/runtime/core"
	"github.com/xhd2015/xgo/runtime/mock"
)

type Formatter[T any] struct {
    prefix T
}

func (c *Formatter[T]) Format(v T) string {
    return fmt.Sprintf("%v: %v", c.prefix, v)
}

func TestMockGenericMethod(t *testing.T) {
       formatter := &Formatter[int]{ prefix: 1 }
	mock.Mock(formatter.Format, func(ctx context.Context, fn *core.FuncInfo, args, results core.Object) error {
		results.GetFieldIndex(0).Set("hello world")
		return nil
	})
        expect := "hello world"
        output := formatter.Format(0)
	if expect != output {
		t.Fatalf("expect ToString[int](0) to be %s, actual: %s", expect, output)
	}
}

What about MockByName and MockMethodByName ? Still works, the name can still be referenced without any type parameter.

Are we going to support per-type parameter mock? Seems no, but we can provide type parameter infos to the user to decide the concrete type. Or, user can just type assert the receiver or arguments or results to check what actual type parameter is.

@xhd2015 xhd2015 added the proposal Proposal new ideas label Mar 29, 2024
@xhd2015
Copy link
Owner Author

xhd2015 commented Mar 29, 2024

This has been done, see commit 0f3164c.

Side note: for go1.20~go1.22, adding generic support is easy.
However, for go1.18 and go1.19, there is much workaround because the implementation of generic instantiation in these two versions is based on closure.

One workaround can be found at here https://github.com/xhd2015/xgo/blob/master/patch/adapter_go1.18_19.go#:~:text=isClosureWrapperForGeneric

@xhd2015 xhd2015 added the done label Mar 29, 2024
@xhd2015 xhd2015 closed this as completed Apr 4, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
done proposal Proposal new ideas
Projects
None yet
Development

No branches or pull requests

1 participant