monkey

package
v2.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2023 License: MIT Imports: 5 Imported by: 0

README

monkey

Deprecation warning (2023/8/10):
This package is not maintained, it will be removed in a future release.
Users are recommended to use bytedance/mockey, which is actively maintained.


As you may expect, this package is unsafe and fragile and probably crash you program, it is only recommended for testing usage.

Notes

  1. Monkey sometimes fails to patch a function if inlining is enabled. Try running your tests with inlining disabled, for example: -gcflags="all=-l -N" (go1.10 and above). The same command line argument can also be used for build.
  2. Monkey won't work on some security-oriented operating system that don't allow memory pages to be both write and execute at the same time. With the current approach there's not really a reliable fix for this.
  3. Monkey is super unsafe, be sure you know what you are doing.
  4. Monkey may not work as expected on arm64 platform, tests failed on MacBook Pro M2, I haven't figured out the problem and solution.

References

  1. https://github.com/bouk/monkey
  2. https://github.com/bytedance/mockey
  3. https://github.com/brahma-adshonor/gohook
  4. https://www.cnblogs.com/catch/p/10973611.html
  5. https://onedrive.live.com/View.aspx?resid=7804A3BDAEB13A9F!58083&authkey=!AKVlLS9s9KYh07s

Documentation

Overview

Package monkey provides a method to do testing using the monkey-patch pattern.

As you may expect, this package is unsafe and fragile and probably crash you program, it is only recommended for testing usage.

Deprecated: this package is not maintained, it will be removed in a future release. Users are recommended to use bytedance/mockey(https://github.com/bytedance/mockey), which is actively maintained.

Example
beforePatch := MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
fmt.Printf("original: %v\n", beforePatch)

patch := PatchFunc(MyTimeFunc, func(year, month, day, hour, min, sec, nsec int) time.Time {
	return time.Date(2001, 1, 1, 1, 1, 1, 1, time.UTC)
})
duringPatch := MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
fmt.Printf("patched: %v\n", duringPatch)

patch.Delete()
unpatched := MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
fmt.Printf("unpatched: %v\n", unpatched)

fmt.Printf("before AutoUnpatch\n")

AutoUnpatch(func() {
	tmp := MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
	fmt.Printf("with AutoUnpatch, before mock: %v\n", tmp)

	// We can use `Return` to return specified values.
	Mock().Target(MyTimeFunc).
		Return(time.Date(2001, time.January, 1, 1, 1, 1, 1, time.UTC)).
		Build()
	tmp = MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
	fmt.Printf("with AutoUnpatch, after mock: %v\n", tmp)

	var innerFunc = func(year, month, day, hour, min, sec, nsec int) time.Time {
		return time.Date(2002, time.February, 2, 2, 2, 2, 2, time.UTC)
	}

	AutoUnpatch(func() {
		tmp := MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
		fmt.Printf("inner AutoUnpatch, before mock: %v\n", tmp)

		// We can also use `To` to specify a replacement function.
		Mock().Target(MyTimeFunc).To(innerFunc).Build()
		tmp = MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
		fmt.Printf("inner AutoUnpatch, after mock: %v\n", tmp)
	})

	tmp = MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
	fmt.Printf("with AutoUnpatch, after inner: %v\n", tmp)

	// In AutoUnpatch, we can also use the Patch* functions.

	// Static function works.
	PatchFunc(MyTimeFunc, mock333)
	tmp = MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
	fmt.Printf("with AutoUnpatch, after inner mock333: %v\n", tmp)

	// Closure also works.
	var fakeTime = time.Date(2004, time.April, 4, 4, 4, 4, 4, time.UTC)
	var mock444 = func(year, month, day, hour, min, sec, nsec int) time.Time {
		return fakeTime
	}
	PatchFunc(MyTimeFunc, mock444)
	tmp = MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
	fmt.Printf("with AutoUnpatch, after inner mock444: %v\n", tmp)

	// We can also patch a function by name.
	Mock().ByName("github.com/jxskiss/gopkg/v2/unsafe/monkey.MyTimeFunc",
		(func(year, month, day, hour, min, sec, nsec int) time.Time)(nil)).
		Return(time.Date(2005, time.May, 5, 5, 5, 5, 5, time.UTC)).
		Build()
	tmp = MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
	fmt.Printf("with AutoUnpatch, ByName: %v\n", tmp)
})

afterAutoUnpatch := MyTimeFunc(2021, 2, 3, 4, 5, 6, 7)
fmt.Printf("after AutoUnpatch: %v\n", afterAutoUnpatch)
Output:

original: 2021-02-03 04:05:06.000000007 +0000 UTC
patched: 2001-01-01 01:01:01.000000001 +0000 UTC
unpatched: 2021-02-03 04:05:06.000000007 +0000 UTC
before AutoUnpatch
with AutoUnpatch, before mock: 2021-02-03 04:05:06.000000007 +0000 UTC
with AutoUnpatch, after mock: 2001-01-01 01:01:01.000000001 +0000 UTC
inner AutoUnpatch, before mock: 2001-01-01 01:01:01.000000001 +0000 UTC
inner AutoUnpatch, after mock: 2002-02-02 02:02:02.000000002 +0000 UTC
with AutoUnpatch, after inner: 2001-01-01 01:01:01.000000001 +0000 UTC
with AutoUnpatch, after inner mock333: 2003-03-03 03:03:03.000000003 +0000 UTC
with AutoUnpatch, after inner mock444: 2004-04-04 04:04:04.000000004 +0000 UTC
with AutoUnpatch, ByName: 2005-05-05 05:05:05.000000005 +0000 UTC
after AutoUnpatch: 2021-02-03 04:05:06.000000007 +0000 UTC

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AutoUnpatch

func AutoUnpatch(f func())

AutoUnpatch encapsulates a function with a context, which automatically unpatch all patches applied within function f.

Types

type Mocker added in v2.1.0

type Mocker struct {
	// contains filtered or unexported fields
}

func Mock

func Mock(target ...any) *Mocker

Mock returns a Mocker object which helps to do mocking.

func (*Mocker) Build added in v2.1.0

func (m *Mocker) Build() *Patch

Build applies the patch, it returns the final Patch object.

func (*Mocker) ByName added in v2.1.0

func (m *Mocker) ByName(name string, signature any) *Mocker

ByName sets the mocking target by name. Private method is supported by specifying the full name.

func (*Mocker) Method added in v2.1.0

func (m *Mocker) Method(target any, method string) *Mocker

Method sets a method of a type as the mocking target.

func (*Mocker) Return added in v2.1.0

func (m *Mocker) Return(rets ...any) *Mocker

Return sets the patch to build a function as replacement which returns rets.

func (*Mocker) Target added in v2.1.0

func (m *Mocker) Target(target any) *Mocker

Target sets the target to mock.

func (*Mocker) To added in v2.1.0

func (m *Mocker) To(repl any) *Mocker

To sets the replacement to mock with.

type Patch

type Patch struct {
	// contains filtered or unexported fields
}

Patch holds the patch data of a patch target and its replacement.

func PatchByName

func PatchByName(name string, repl any) *Patch

PatchByName replaces a function with replacement by it's name. TargetName should be the fully-qualified name of the target function or method. If the target cannot be found or the replacement type does not match, it panics.

func PatchFunc

func PatchFunc(target, repl any) *Patch

PatchFunc replaces a function with replacement. If target or replacement is not a function or their types do not match, it panics.

func PatchMethod

func PatchMethod(target any, method string, repl any) *Patch

PatchMethod replaces a target's method with replacement. Replacement should expect the receiver (of type target) as the first argument. If the method cannot be found or the replacement type does not match, it panics.

func PatchVar

func PatchVar(targetAddr, repl any) *Patch

PatchVar replaces target's value with replacement. If type of target and repl does not match, it panics.

Example
var someVar = 1234
fmt.Printf("original: %v\n", someVar)

patch := PatchVar(&someVar, 2345)
fmt.Printf("patched: %v\n", someVar)

patch.Delete()
fmt.Printf("unpatched: %v\n", someVar)

fmt.Printf("before AutoUnpatch\n")

AutoUnpatch(func() {
	fmt.Printf("with AutoUnpatch, before patch: %v\n", someVar)

	PatchVar(&someVar, 3456)
	fmt.Printf("with AutoUnpatch, after patch: %v\n", someVar)

	AutoUnpatch(func() {
		fmt.Printf("inner AutoUnpatch, before patch: %v\n", someVar)

		PatchVar(&someVar, 4567)
		fmt.Printf("inner AutoUnpatch, after patch: %v\n", someVar)
	})

	fmt.Printf("with AutoUnpatch, after inner: %v\n", someVar)

	// Patch again.
	PatchVar(&someVar, 5678)
	fmt.Printf("with AutoUnpatch, patch again: %v\n", someVar)
})

fmt.Printf("after AutoUnpatch: %v\n", someVar)
Output:

original: 1234
patched: 2345
unpatched: 1234
before AutoUnpatch
with AutoUnpatch, before patch: 1234
with AutoUnpatch, after patch: 3456
inner AutoUnpatch, before patch: 3456
inner AutoUnpatch, after patch: 4567
with AutoUnpatch, after inner: 3456
with AutoUnpatch, patch again: 5678
after AutoUnpatch: 1234

func (*Patch) Delete

func (p *Patch) Delete()

Delete resets target to the state before applying the patch and destroys the patch. After calling Delete, the patch MUST NOT be used anymore.

func (*Patch) Patch

func (p *Patch) Patch()

Patch applies the patch.

Directories

Path Synopsis
fn
mem

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL