github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/tests/imports.go (about) 1 package tests 2 3 import ( 4 "bufio" 5 "bytes" 6 "compress/flate" 7 "compress/gzip" 8 "context" 9 "crypto/md5" //nolint:gosec 10 crand "crypto/rand" 11 "crypto/sha1" //nolint:gosec 12 "encoding/base64" 13 "encoding/binary" 14 "encoding/json" 15 "encoding/xml" 16 "errors" 17 "flag" 18 "fmt" 19 "hash/fnv" 20 "image" 21 "image/color" 22 "io" 23 "log" 24 "math" 25 "math/big" 26 "math/rand" 27 "net" 28 "net/url" 29 "os" 30 "path/filepath" 31 "reflect" 32 "sort" 33 "strconv" 34 "strings" 35 "sync" 36 "sync/atomic" 37 "text/template" 38 "time" 39 "unicode/utf8" 40 41 gno "github.com/gnolang/gno/gnovm/pkg/gnolang" 42 "github.com/gnolang/gno/gnovm/stdlibs" 43 teststdlibs "github.com/gnolang/gno/gnovm/tests/stdlibs" 44 "github.com/gnolang/gno/tm2/pkg/db/memdb" 45 osm "github.com/gnolang/gno/tm2/pkg/os" 46 "github.com/gnolang/gno/tm2/pkg/std" 47 "github.com/gnolang/gno/tm2/pkg/store/dbadapter" 48 "github.com/gnolang/gno/tm2/pkg/store/iavl" 49 stypes "github.com/gnolang/gno/tm2/pkg/store/types" 50 ) 51 52 type importMode uint64 53 54 // Import modes to control the import behaviour of TestStore. 55 const ( 56 // use stdlibs/* only (except a few exceptions). for stdlibs/* and examples/* testing. 57 ImportModeStdlibsOnly importMode = iota 58 // use stdlibs/* if present, otherwise use native. used in files/tests, excluded for *_native.go 59 ImportModeStdlibsPreferred 60 // do not use stdlibs/* if native registered. used in files/tests, excluded for *_stdlibs.go 61 ImportModeNativePreferred 62 ) 63 64 // NOTE: this isn't safe, should only be used for testing. 65 func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Writer, mode importMode) (store gno.Store) { 66 getPackage := func(pkgPath string, newStore gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) { 67 if pkgPath == "" { 68 panic(fmt.Sprintf("invalid zero package path in testStore().pkgGetter")) 69 } 70 if mode != ImportModeStdlibsOnly && 71 mode != ImportModeStdlibsPreferred && 72 mode != ImportModeNativePreferred { 73 panic(fmt.Sprintf("unrecognized import mode")) 74 } 75 76 if filesPath != "" { 77 // if _test package... 78 const testPath = "github.com/gnolang/gno/_test/" 79 if strings.HasPrefix(pkgPath, testPath) { 80 baseDir := filepath.Join(filesPath, "extern", pkgPath[len(testPath):]) 81 memPkg := gno.ReadMemPackage(baseDir, pkgPath) 82 send := std.Coins{} 83 ctx := testContext(pkgPath, send) 84 m2 := gno.NewMachineWithOptions(gno.MachineOptions{ 85 PkgPath: "test", 86 Output: stdout, 87 Store: newStore, 88 Context: ctx, 89 }) 90 // pkg := gno.NewPackageNode(gno.Name(memPkg.Name), memPkg.Path, nil) 91 // pv := pkg.NewPackage() 92 // m2.SetActivePackage(pv) 93 return m2.RunMemPackage(memPkg, false) 94 } 95 } 96 97 // if stdlibs package is preferred , try to load it first. 98 if mode == ImportModeStdlibsOnly || 99 mode == ImportModeStdlibsPreferred { 100 pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout) 101 if pn != nil { 102 return 103 } 104 } 105 106 // if native package is allowed, return it. 107 if pkgPath == "os" || // special cases even when StdlibsOnly (for tests). 108 pkgPath == "fmt" || // TODO: try to minimize these exceptions over time. 109 pkgPath == "log" || 110 pkgPath == "crypto/rand" || 111 pkgPath == "crypto/md5" || 112 pkgPath == "crypto/sha1" || 113 pkgPath == "encoding/binary" || 114 pkgPath == "encoding/json" || 115 pkgPath == "encoding/xml" || 116 pkgPath == "internal/os_test" || 117 pkgPath == "math/big" || 118 pkgPath == "math/rand" || 119 mode == ImportModeStdlibsPreferred || 120 mode == ImportModeNativePreferred { 121 switch pkgPath { 122 case "os": 123 pkg := gno.NewPackageNode("os", pkgPath, nil) 124 pkg.DefineGoNativeValue("Stdin", stdin) 125 pkg.DefineGoNativeValue("Stdout", stdout) 126 pkg.DefineGoNativeValue("Stderr", stderr) 127 return pkg, pkg.NewPackage() 128 case "fmt": 129 pkg := gno.NewPackageNode("fmt", pkgPath, nil) 130 pkg.DefineGoNativeType(reflect.TypeOf((*fmt.Stringer)(nil)).Elem()) 131 pkg.DefineGoNativeType(reflect.TypeOf((*fmt.Formatter)(nil)).Elem()) 132 pkg.DefineGoNativeValue("Println", func(a ...interface{}) (n int, err error) { 133 // NOTE: uncomment to debug long running tests 134 // fmt.Println(a...) 135 res := fmt.Sprintln(a...) 136 return stdout.Write([]byte(res)) 137 }) 138 pkg.DefineGoNativeValue("Printf", func(format string, a ...interface{}) (n int, err error) { 139 res := fmt.Sprintf(format, a...) 140 return stdout.Write([]byte(res)) 141 }) 142 pkg.DefineGoNativeValue("Print", func(a ...interface{}) (n int, err error) { 143 res := fmt.Sprint(a...) 144 return stdout.Write([]byte(res)) 145 }) 146 pkg.DefineGoNativeValue("Sprint", fmt.Sprint) 147 pkg.DefineGoNativeValue("Sprintf", fmt.Sprintf) 148 pkg.DefineGoNativeValue("Sprintln", fmt.Sprintln) 149 pkg.DefineGoNativeValue("Sscanf", fmt.Sscanf) 150 pkg.DefineGoNativeValue("Errorf", fmt.Errorf) 151 pkg.DefineGoNativeValue("Fprintln", fmt.Fprintln) 152 pkg.DefineGoNativeValue("Fprintf", fmt.Fprintf) 153 pkg.DefineGoNativeValue("Fprint", fmt.Fprint) 154 return pkg, pkg.NewPackage() 155 case "encoding/base64": 156 pkg := gno.NewPackageNode("base64", pkgPath, nil) 157 pkg.DefineGoNativeValue("RawStdEncoding", base64.RawStdEncoding) 158 pkg.DefineGoNativeValue("StdEncoding", base64.StdEncoding) 159 pkg.DefineGoNativeValue("NewDecoder", base64.NewDecoder) 160 return pkg, pkg.NewPackage() 161 case "encoding/binary": 162 pkg := gno.NewPackageNode("binary", pkgPath, nil) 163 pkg.DefineGoNativeValue("LittleEndian", binary.LittleEndian) 164 pkg.DefineGoNativeValue("BigEndian", binary.BigEndian) 165 pkg.DefineGoNativeValue("Write", binary.BigEndian) // warn: use reflection 166 return pkg, pkg.NewPackage() 167 case "encoding/json": 168 pkg := gno.NewPackageNode("json", pkgPath, nil) 169 pkg.DefineGoNativeValue("Unmarshal", json.Unmarshal) 170 pkg.DefineGoNativeValue("Marshal", json.Marshal) 171 return pkg, pkg.NewPackage() 172 case "encoding/xml": 173 pkg := gno.NewPackageNode("xml", pkgPath, nil) 174 pkg.DefineGoNativeValue("Unmarshal", xml.Unmarshal) 175 return pkg, pkg.NewPackage() 176 case "internal/os_test": 177 pkg := gno.NewPackageNode("os_test", pkgPath, nil) 178 pkg.DefineNative("Sleep", 179 gno.Flds( // params 180 "d", gno.AnyT(), // NOTE: should be time.Duration 181 ), 182 gno.Flds( // results 183 ), 184 func(m *gno.Machine) { 185 // For testing purposes here, nanoseconds are separately kept track. 186 arg0 := m.LastBlock().GetParams1().TV 187 d := arg0.GetInt64() 188 sec := d / int64(time.Second) 189 nano := d % int64(time.Second) 190 ctx := m.Context.(stdlibs.ExecContext) 191 ctx.Timestamp += sec 192 ctx.TimestampNano += nano 193 if ctx.TimestampNano >= int64(time.Second) { 194 ctx.Timestamp += 1 195 ctx.TimestampNano -= int64(time.Second) 196 } 197 m.Context = ctx 198 }, 199 ) 200 return pkg, pkg.NewPackage() 201 case "net": 202 pkg := gno.NewPackageNode("net", pkgPath, nil) 203 pkg.DefineGoNativeType(reflect.TypeOf(net.TCPAddr{})) 204 pkg.DefineGoNativeValue("IPv4", net.IPv4) 205 return pkg, pkg.NewPackage() 206 case "net/url": 207 pkg := gno.NewPackageNode("url", pkgPath, nil) 208 pkg.DefineGoNativeType(reflect.TypeOf(url.Values{})) 209 return pkg, pkg.NewPackage() 210 case "bufio": 211 pkg := gno.NewPackageNode("bufio", pkgPath, nil) 212 pkg.DefineGoNativeValue("NewScanner", bufio.NewScanner) 213 pkg.DefineGoNativeType(reflect.TypeOf(bufio.SplitFunc(nil))) 214 return pkg, pkg.NewPackage() 215 case "bytes": 216 pkg := gno.NewPackageNode("bytes", pkgPath, nil) 217 pkg.DefineGoNativeValue("Equal", bytes.Equal) 218 pkg.DefineGoNativeValue("Compare", bytes.Compare) 219 pkg.DefineGoNativeValue("NewReader", bytes.NewReader) 220 pkg.DefineGoNativeValue("NewBuffer", bytes.NewBuffer) 221 pkg.DefineGoNativeValue("Repeat", bytes.Repeat) 222 pkg.DefineGoNativeType(reflect.TypeOf(bytes.Buffer{})) 223 return pkg, pkg.NewPackage() 224 case "time": 225 pkg := gno.NewPackageNode("time", pkgPath, nil) 226 pkg.DefineGoNativeValue("Millisecond", time.Millisecond) 227 pkg.DefineGoNativeValue("Second", time.Second) 228 pkg.DefineGoNativeValue("Minute", time.Minute) 229 pkg.DefineGoNativeValue("Hour", time.Hour) 230 pkg.DefineGoNativeValue("Date", time.Date) 231 pkg.DefineGoNativeValue("Now", func() time.Time { return time.Unix(0, 0).UTC() }) // deterministic 232 pkg.DefineGoNativeValue("November", time.November) 233 pkg.DefineGoNativeValue("UTC", time.UTC) 234 pkg.DefineGoNativeValue("Unix", time.Unix) 235 pkg.DefineGoNativeType(reflect.TypeOf(time.Time{})) 236 pkg.DefineGoNativeType(reflect.TypeOf(time.Duration(0))) 237 pkg.DefineGoNativeType(reflect.TypeOf(time.Month(0))) 238 return pkg, pkg.NewPackage() 239 case "strings": 240 pkg := gno.NewPackageNode("strings", pkgPath, nil) 241 pkg.DefineGoNativeValue("Split", strings.Split) 242 pkg.DefineGoNativeValue("SplitN", strings.SplitN) 243 pkg.DefineGoNativeValue("Contains", strings.Contains) 244 pkg.DefineGoNativeValue("TrimSpace", strings.TrimSpace) 245 pkg.DefineGoNativeValue("HasPrefix", strings.HasPrefix) 246 pkg.DefineGoNativeValue("NewReader", strings.NewReader) 247 pkg.DefineGoNativeValue("Index", strings.Index) 248 pkg.DefineGoNativeValue("IndexRune", strings.IndexRune) 249 pkg.DefineGoNativeValue("Join", strings.Join) 250 pkg.DefineGoNativeType(reflect.TypeOf(strings.Builder{})) 251 return pkg, pkg.NewPackage() 252 case "math": 253 pkg := gno.NewPackageNode("math", pkgPath, nil) 254 pkg.DefineGoNativeValue("Abs", math.Abs) 255 pkg.DefineGoNativeValue("Cos", math.Cos) 256 pkg.DefineGoNativeValue("Pi", math.Pi) 257 pkg.DefineGoNativeValue("Float64bits", math.Float64bits) 258 pkg.DefineGoNativeValue("Pi", math.Pi) 259 pkg.DefineGoNativeValue("MaxFloat32", math.MaxFloat32) 260 pkg.DefineGoNativeValue("MaxFloat64", math.MaxFloat64) 261 pkg.DefineGoNativeValue("MaxUint32", math.MaxUint32) 262 pkg.DefineGoNativeValue("MaxUint64", uint64(math.MaxUint64)) 263 pkg.DefineGoNativeValue("MinInt8", math.MinInt8) 264 pkg.DefineGoNativeValue("MinInt16", math.MinInt16) 265 pkg.DefineGoNativeValue("MinInt32", math.MinInt32) 266 pkg.DefineGoNativeValue("MinInt64", math.MinInt64) 267 pkg.DefineGoNativeValue("MaxInt8", math.MaxInt8) 268 pkg.DefineGoNativeValue("MaxInt16", math.MaxInt16) 269 pkg.DefineGoNativeValue("MaxInt32", math.MaxInt32) 270 pkg.DefineGoNativeValue("MaxInt64", math.MaxInt64) 271 return pkg, pkg.NewPackage() 272 case "math/rand": 273 // XXX only expose for tests. 274 pkg := gno.NewPackageNode("rand", pkgPath, nil) 275 pkg.DefineGoNativeValue("Intn", rand.Intn) 276 pkg.DefineGoNativeValue("Uint32", rand.Uint32) 277 pkg.DefineGoNativeValue("Seed", rand.Seed) 278 pkg.DefineGoNativeValue("New", rand.New) 279 pkg.DefineGoNativeValue("NewSource", rand.NewSource) 280 pkg.DefineGoNativeType(reflect.TypeOf(rand.Rand{})) 281 return pkg, pkg.NewPackage() 282 case "crypto/rand": 283 pkg := gno.NewPackageNode("rand", pkgPath, nil) 284 pkg.DefineGoNativeValue("Prime", crand.Prime) 285 // for determinism: 286 // pkg.DefineGoNativeValue("Reader", crand.Reader) 287 pkg.DefineGoNativeValue("Reader", &dummyReader{}) 288 return pkg, pkg.NewPackage() 289 case "crypto/md5": 290 pkg := gno.NewPackageNode("md5", pkgPath, nil) 291 pkg.DefineGoNativeValue("New", md5.New) 292 return pkg, pkg.NewPackage() 293 case "crypto/sha1": 294 pkg := gno.NewPackageNode("sha1", pkgPath, nil) 295 pkg.DefineGoNativeValue("New", sha1.New) 296 return pkg, pkg.NewPackage() 297 case "image": 298 pkg := gno.NewPackageNode("image", pkgPath, nil) 299 pkg.DefineGoNativeType(reflect.TypeOf(image.Point{})) 300 return pkg, pkg.NewPackage() 301 case "image/color": 302 pkg := gno.NewPackageNode("color", pkgPath, nil) 303 pkg.DefineGoNativeType(reflect.TypeOf(color.NRGBA64{})) 304 return pkg, pkg.NewPackage() 305 case "compress/flate": 306 pkg := gno.NewPackageNode("flate", pkgPath, nil) 307 pkg.DefineGoNativeValue("BestSpeed", flate.BestSpeed) 308 return pkg, pkg.NewPackage() 309 case "compress/gzip": 310 pkg := gno.NewPackageNode("gzip", pkgPath, nil) 311 pkg.DefineGoNativeType(reflect.TypeOf(gzip.Writer{})) 312 pkg.DefineGoNativeValue("BestCompression", gzip.BestCompression) 313 pkg.DefineGoNativeValue("BestSpeed", gzip.BestSpeed) 314 return pkg, pkg.NewPackage() 315 case "context": 316 pkg := gno.NewPackageNode("context", pkgPath, nil) 317 pkg.DefineGoNativeType(reflect.TypeOf((*context.Context)(nil)).Elem()) 318 pkg.DefineGoNativeValue("WithValue", context.WithValue) 319 pkg.DefineGoNativeValue("Background", context.Background) 320 return pkg, pkg.NewPackage() 321 case "sync": 322 pkg := gno.NewPackageNode("sync", pkgPath, nil) 323 pkg.DefineGoNativeType(reflect.TypeOf(sync.Mutex{})) 324 pkg.DefineGoNativeType(reflect.TypeOf(sync.RWMutex{})) 325 pkg.DefineGoNativeType(reflect.TypeOf(sync.Pool{})) 326 return pkg, pkg.NewPackage() 327 case "sync/atomic": 328 pkg := gno.NewPackageNode("atomic", pkgPath, nil) 329 pkg.DefineGoNativeType(reflect.TypeOf(atomic.Value{})) 330 return pkg, pkg.NewPackage() 331 case "math/big": 332 pkg := gno.NewPackageNode("big", pkgPath, nil) 333 pkg.DefineGoNativeValue("NewInt", big.NewInt) 334 return pkg, pkg.NewPackage() 335 case "sort": 336 pkg := gno.NewPackageNode("sort", pkgPath, nil) 337 pkg.DefineGoNativeValue("Strings", sort.Strings) 338 // pkg.DefineGoNativeValue("Sort", sort.Sort) 339 return pkg, pkg.NewPackage() 340 case "flag": 341 pkg := gno.NewPackageNode("flag", pkgPath, nil) 342 pkg.DefineGoNativeType(reflect.TypeOf(flag.Flag{})) 343 return pkg, pkg.NewPackage() 344 case "io": 345 pkg := gno.NewPackageNode("io", pkgPath, nil) 346 pkg.DefineGoNativeValue("EOF", io.EOF) 347 pkg.DefineGoNativeValue("NopCloser", io.NopCloser) 348 pkg.DefineGoNativeValue("ReadFull", io.ReadFull) 349 pkg.DefineGoNativeValue("ReadAll", io.ReadAll) 350 pkg.DefineGoNativeType(reflect.TypeOf((*io.ReadCloser)(nil)).Elem()) 351 pkg.DefineGoNativeType(reflect.TypeOf((*io.Closer)(nil)).Elem()) 352 pkg.DefineGoNativeType(reflect.TypeOf((*io.Reader)(nil)).Elem()) 353 return pkg, pkg.NewPackage() 354 case "log": 355 pkg := gno.NewPackageNode("log", pkgPath, nil) 356 pkg.DefineGoNativeValue("Fatal", log.Fatal) 357 return pkg, pkg.NewPackage() 358 case "text/template": 359 pkg := gno.NewPackageNode("template", pkgPath, nil) 360 pkg.DefineGoNativeType(reflect.TypeOf(template.FuncMap{})) 361 return pkg, pkg.NewPackage() 362 case "unicode/utf8": 363 pkg := gno.NewPackageNode("utf8", pkgPath, nil) 364 pkg.DefineGoNativeValue("DecodeRuneInString", utf8.DecodeRuneInString) 365 tv := gno.TypedValue{T: gno.UntypedRuneType} // TODO dry 366 tv.SetInt32(utf8.RuneSelf) // .. 367 pkg.Define("RuneSelf", tv) // .. 368 return pkg, pkg.NewPackage() 369 case "errors": 370 pkg := gno.NewPackageNode("errors", pkgPath, nil) 371 pkg.DefineGoNativeValue("New", errors.New) 372 return pkg, pkg.NewPackage() 373 case "hash/fnv": 374 pkg := gno.NewPackageNode("fnv", pkgPath, nil) 375 pkg.DefineGoNativeValue("New32a", fnv.New32a) 376 return pkg, pkg.NewPackage() 377 default: 378 // continue on... 379 } 380 } 381 382 // if native package is preferred, try to load stdlibs/* as backup. 383 if mode == ImportModeNativePreferred { 384 pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout) 385 if pn != nil { 386 return 387 } 388 } 389 390 // if examples package... 391 examplePath := filepath.Join(rootDir, "examples", pkgPath) 392 if osm.DirExists(examplePath) { 393 memPkg := gno.ReadMemPackage(examplePath, pkgPath) 394 if memPkg.IsEmpty() { 395 panic(fmt.Sprintf("found an empty package %q", pkgPath)) 396 } 397 398 send := std.Coins{} 399 ctx := testContext(pkgPath, send) 400 m2 := gno.NewMachineWithOptions(gno.MachineOptions{ 401 PkgPath: "test", 402 Output: stdout, 403 Store: newStore, 404 Context: ctx, 405 }) 406 pn, pv = m2.RunMemPackage(memPkg, true) 407 return 408 } 409 return nil, nil 410 } 411 // NOTE: store is also used in closure above. 412 db := memdb.NewMemDB() 413 baseStore := dbadapter.StoreConstructor(db, stypes.StoreOptions{}) 414 iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{}) 415 store = gno.NewStore(nil, baseStore, iavlStore) 416 store.SetPackageGetter(getPackage) 417 store.SetNativeStore(teststdlibs.NativeStore) 418 store.SetPackageInjector(testPackageInjector) 419 store.SetStrictGo2GnoMapping(false) 420 return 421 } 422 423 func loadStdlib(rootDir, pkgPath string, store gno.Store, stdout io.Writer) (*gno.PackageNode, *gno.PackageValue) { 424 dirs := [...]string{ 425 // normal stdlib path. 426 filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath), 427 // override path. definitions here override the previous if duplicate. 428 filepath.Join(rootDir, "gnovm", "tests", "stdlibs", pkgPath), 429 } 430 files := make([]string, 0, 32) // pre-alloc 32 as a likely high number of files 431 for _, path := range dirs { 432 dl, err := os.ReadDir(path) 433 if err != nil { 434 if os.IsNotExist(err) { 435 continue 436 } 437 panic(fmt.Errorf("could not access dir %q: %w", path, err)) 438 } 439 440 for _, f := range dl { 441 // NOTE: RunMemPackage has other rules; those should be mostly useful 442 // for on-chain packages (ie. include README and gno.mod). 443 if !f.IsDir() && strings.HasSuffix(f.Name(), ".gno") { 444 files = append(files, filepath.Join(path, f.Name())) 445 } 446 } 447 } 448 if len(files) == 0 { 449 return nil, nil 450 } 451 452 memPkg := gno.ReadMemPackageFromList(files, pkgPath) 453 m2 := gno.NewMachineWithOptions(gno.MachineOptions{ 454 // NOTE: see also pkgs/sdk/vm/builtins.go 455 // Needs PkgPath != its name because TestStore.getPackage is the package 456 // getter for the store, which calls loadStdlib, so it would be recursively called. 457 PkgPath: "stdlibload", 458 Output: stdout, 459 Store: store, 460 }) 461 save := pkgPath != "testing" // never save the "testing" package 462 return m2.RunMemPackageWithOverrides(memPkg, save) 463 } 464 465 func testPackageInjector(store gno.Store, pn *gno.PackageNode) { 466 // Test specific injections: 467 switch pn.PkgPath { 468 case "strconv": 469 // NOTE: Itoa and Atoi are already injected 470 // from stdlibs.InjectNatives. 471 pn.DefineGoNativeType(reflect.TypeOf(strconv.NumError{})) 472 pn.DefineGoNativeValue("ParseInt", strconv.ParseInt) 473 } 474 } 475 476 //---------------------------------------- 477 478 type dummyReader struct{} 479 480 func (*dummyReader) Read(b []byte) (n int, err error) { 481 for i := 0; i < len(b); i++ { 482 b[i] = byte((100 + i) % 256) 483 } 484 return len(b), nil 485 } 486 487 //---------------------------------------- 488 489 type TestReport struct { 490 Name string 491 Verbose bool 492 Failed bool 493 Skipped bool 494 Output string 495 }