Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

gop/cl/cltest #1884

Merged
merged 1 commit into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions cl/cltest/cltest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cltest

import (
"bytes"
"os"
"testing"

"github.com/goplus/gogen"
"github.com/goplus/gop"
"github.com/goplus/gop/cl"
"github.com/goplus/gop/parser"
"github.com/goplus/gop/parser/fsx/memfs"
"github.com/goplus/gop/scanner"
"github.com/goplus/gop/token"
"github.com/goplus/mod/env"
"github.com/goplus/mod/modfile"
)

var (
Gop *env.Gop
Conf *cl.Config
)

func init() {
Gop = &env.Gop{Version: "1.0"}
gogen.SetDebug(gogen.DbgFlagAll)
cl.SetDebug(cl.DbgFlagAll | cl.FlagNoMarkAutogen)
fset := token.NewFileSet()
imp := gop.NewImporter(nil, Gop, fset)
Conf = &cl.Config{
Fset: fset,
Importer: imp,
Recorder: gopRecorder{},
LookupClass: LookupClass,
NoFileLine: true,
NoAutoGenMain: true,
}
}

func LookupClass(ext string) (c *modfile.Project, ok bool) {
switch ext {
case ".tgmx", ".tspx":
return &modfile.Project{
Ext: ".tgmx", Class: "*MyGame",
Works: []*modfile.Class{{Ext: ".tspx", Class: "Sprite"}},
PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx", "math"}}, true
case ".t2gmx", ".t2spx", ".t2spx2":
return &modfile.Project{
Ext: ".t2gmx", Class: "Game",
Works: []*modfile.Class{{Ext: ".t2spx", Class: "Sprite"},
{Ext: ".t2spx2", Class: "Sprite2"}},
PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
case "_t3spx.gox", ".t3spx2":
return &modfile.Project{
Works: []*modfile.Class{{Ext: "_t3spx.gox", Class: "Sprite"},
{Ext: ".t3spx2", Class: "Sprite2"}},
PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
case "_spx.gox":
return &modfile.Project{
Ext: "_spx.gox", Class: "Game",
Works: []*modfile.Class{{Ext: "_spx.gox", Class: "Sprite"}},
PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx3", "math"},
Import: []*modfile.Import{{Path: "github.com/goplus/gop/cl/internal/spx3/jwt"}}}, true
case "_xtest.gox":
return &modfile.Project{
Ext: "_xtest.gox", Class: "App",
Works: []*modfile.Class{{Ext: "_xtest.gox", Class: "Case"}},
PkgPaths: []string{"github.com/goplus/gop/test", "testing"}}, true
}
return
}

func Named(t *testing.T, name string, gopcode, expected string) {
t.Run(name, func(t *testing.T) {
Do(t, gopcode, expected)
})
}

func Do(t *testing.T, gopcode, expected string) {
DoExt(t, Conf, "main", gopcode, expected)
}

func DoWithFname(t *testing.T, gopcode, expected string, fname string) {
fs := memfs.SingleFile("/foo", fname, gopcode)
DoFS(t, Conf, fs, "main", expected)
}

func DoExt(t *testing.T, conf *cl.Config, pkgname, gopcode, expected string) {
fs := memfs.SingleFile("/foo", "bar.gop", gopcode)
DoFS(t, conf, fs, pkgname, expected)
}

func Mixed(t *testing.T, pkgname, gocode, gopcode, expected string, outline ...bool) {
conf := *Conf
conf.Outline = (outline != nil && outline[0])
fs := memfs.TwoFiles("/foo", "a.go", gocode, "b.gop", gopcode)
DoFS(t, &conf, fs, pkgname, expected)
}

func DoFS(t *testing.T, conf *cl.Config, fs parser.FileSystem, pkgname, expected string) {
cl.SetDisableRecover(true)
defer cl.SetDisableRecover(false)

fset := conf.Fset
pkgs, err := parser.ParseFSDir(fset, fs, "/foo", parser.Config{Mode: parser.ParseComments})
if err != nil {
scanner.PrintError(os.Stderr, err)
t.Fatal("ParseFSDir:", err)
}
bar := pkgs[pkgname]
pkg, err := cl.NewPackage("github.com/goplus/gop/cl", bar, conf)
if err != nil {
t.Fatal("NewPackage:", err)
}
var b bytes.Buffer
err = pkg.WriteTo(&b)
if err != nil {
t.Fatal("gogen.WriteTo failed:", err)
}
result := b.String()
if result != expected {
t.Fatalf("\nResult:\n%s\nExpected:\n%s\n", result, expected)
}
}
70 changes: 70 additions & 0 deletions cl/cltest/error_msg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cltest

import (
"os"
"testing"

"github.com/goplus/gop/ast"
"github.com/goplus/gop/cl"
"github.com/goplus/gop/parser"
"github.com/goplus/gop/parser/fsx/memfs"
"github.com/goplus/gop/scanner"
)

func Error(t *testing.T, msg, src string) {
ErrorEx(t, "main", "bar.gop", msg, src)
}

func ErrorEx(t *testing.T, pkgname, filename, msg, src string) {
fs := memfs.SingleFile("/foo", filename, src)
pkgs, err := parser.ParseFSDir(Conf.Fset, fs, "/foo", parser.Config{})
if err != nil {
scanner.PrintError(os.Stderr, err)
t.Fatal("parser.ParseFSDir failed")
}
conf := *Conf
conf.NoFileLine = false
conf.RelativeBase = "/foo"
bar := pkgs[pkgname]
_, err = cl.NewPackage("", bar, &conf)
if err == nil {
t.Fatal("no error?")
}
if ret := err.Error(); ret != msg {
t.Fatalf("\nError: \"%s\"\nExpected: \"%s\"\n", ret, msg)
}
}

func ErrorAst(t *testing.T, pkgname, filename, msg, src string) {
f, _ := parser.ParseFile(Conf.Fset, filename, src, parser.AllErrors)
pkg := &ast.Package{
Name: pkgname,
Files: map[string]*ast.File{filename: f},
}
conf := *Conf
conf.NoFileLine = false
conf.RelativeBase = "/foo"
_, err := cl.NewPackage("", pkg, &conf)
if err == nil {
t.Fatal("no error?")
}
if ret := err.Error(); ret != msg {
t.Fatalf("\nError: \"%s\"\nExpected: \"%s\"\n", ret, msg)
}
}
123 changes: 123 additions & 0 deletions cl/cltest/recorder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cltest

import (
"go/types"

"github.com/goplus/gop/ast"
)

type gopRecorder struct {
}

// Type maps expressions to their types, and for constant
// expressions, also their values. Invalid expressions are
// omitted.
//
// For (possibly parenthesized) identifiers denoting built-in
// functions, the recorded signatures are call-site specific:
// if the call result is not a constant, the recorded type is
// an argument-specific signature. Otherwise, the recorded type
// is invalid.
//
// The Types map does not record the type of every identifier,
// only those that appear where an arbitrary expression is
// permitted. For instance, the identifier f in a selector
// expression x.f is found only in the Selections map, the
// identifier z in a variable declaration 'var z int' is found
// only in the Defs map, and identifiers denoting packages in
// qualified identifiers are collected in the Uses map.
func (info gopRecorder) Type(e ast.Expr, tv types.TypeAndValue) {
}

// Instantiate maps identifiers denoting generic types or functions to their
// type arguments and instantiated type.
//
// For example, Instantiate will map the identifier for 'T' in the type
// instantiation T[int, string] to the type arguments [int, string] and
// resulting instantiated *Named type. Given a generic function
// func F[A any](A), Instances will map the identifier for 'F' in the call
// expression F(int(1)) to the inferred type arguments [int], and resulting
// instantiated *Signature.
//
// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs
// results in an equivalent of Instances[id].Type.
func (info gopRecorder) Instantiate(id *ast.Ident, inst types.Instance) {
}

// Def maps identifiers to the objects they define (including
// package names, dots "." of dot-imports, and blank "_" identifiers).
// For identifiers that do not denote objects (e.g., the package name
// in package clauses, or symbolic variables t in t := x.(type) of
// type switch headers), the corresponding objects are nil.
//
// For an embedded field, Def maps the field *Var it defines.
//
// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
func (info gopRecorder) Def(id *ast.Ident, obj types.Object) {
}

// Use maps identifiers to the objects they denote.
//
// For an embedded field, Use maps the *TypeName it denotes.
//
// Invariant: Uses[id].Pos() != id.Pos()
func (info gopRecorder) Use(id *ast.Ident, obj types.Object) {
}

// Implicit maps nodes to their implicitly declared objects, if any.
// The following node and object types may appear:
//
// node declared object
//
// *ast.ImportSpec *PkgName for imports without renames
// *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
// *ast.Field anonymous parameter *Var (incl. unnamed results)
func (info gopRecorder) Implicit(node ast.Node, obj types.Object) {
}

// Select maps selector expressions (excluding qualified identifiers)
// to their corresponding selections.
func (info gopRecorder) Select(e *ast.SelectorExpr, sel *types.Selection) {
}

// Scope maps ast.Nodes to the scopes they define. Package scopes are not
// associated with a specific node but with all files belonging to a package.
// Thus, the package scope can be found in the type-checked Package object.
// Scopes nest, with the Universe scope being the outermost scope, enclosing
// the package scope, which contains (one or more) files scopes, which enclose
// function scopes which in turn enclose statement and function literal scopes.
// Note that even though package-level functions are declared in the package
// scope, the function scopes are embedded in the file scope of the file
// containing the function declaration.
//
// The following node types may appear in Scopes:
//
// *ast.File
// *ast.FuncType
// *ast.TypeSpec
// *ast.BlockStmt
// *ast.IfStmt
// *ast.SwitchStmt
// *ast.TypeSwitchStmt
// *ast.CaseClause
// *ast.CommClause
// *ast.ForStmt
// *ast.RangeStmt
func (info gopRecorder) Scope(n ast.Node, scope *types.Scope) {
}
Loading
Loading