github.com/vugu/vugu@v0.3.6-0.20240430171613-3f6f402e014b/gen/parser-go-pkg_test.go (about)

     1  package gen
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"regexp"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func TestSimpleParseGoPkgRun(t *testing.T) {
    15  
    16  	assert := assert.New(t)
    17  
    18  	tmpDir, err := os.MkdirTemp("", "TestParseGoPkgRun")
    19  	if err != nil {
    20  		t.Fatal(err)
    21  	}
    22  	defer os.RemoveAll(tmpDir)
    23  
    24  	// 	assert.NoError(os.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte(`
    25  	// module main
    26  	// `), 0644))
    27  
    28  	assert.NoError(os.WriteFile(filepath.Join(tmpDir, "root.vugu"), []byte(`
    29  <div id="root_comp">
    30  	<h1>Hello!</h1>
    31  </div>
    32  `), 0644))
    33  
    34  	p := NewParserGoPkg(tmpDir, nil)
    35  
    36  	assert.NoError(p.Run())
    37  
    38  	b, err := os.ReadFile(filepath.Join(tmpDir, "root_gen.go"))
    39  	assert.NoError(err)
    40  	// t.Logf("OUT FILE root_gen.go: %s", b)
    41  	// log.Printf("OUT FILE root_gen.go: %s", b)
    42  
    43  	if !bytes.Contains(b, []byte(`func (c *Root) Build`)) {
    44  		t.Errorf("failed to find Build method signature")
    45  	}
    46  
    47  	b, err = os.ReadFile(filepath.Join(tmpDir, "0_missing_gen.go"))
    48  	assert.NoError(err)
    49  
    50  	if !bytes.Contains(b, []byte(`type Root struct`)) {
    51  		t.Errorf("failed to find Root struct definition")
    52  	}
    53  
    54  }
    55  
    56  func TestRun(t *testing.T) {
    57  
    58  	debug := false
    59  
    60  	pwd, err := filepath.Abs("..")
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	type tcase struct {
    66  		name      string
    67  		opts      ParserGoPkgOpts
    68  		recursive bool
    69  		infiles   map[string]string              // file structure to start with
    70  		out       map[string][]string            // regexps to match in output files
    71  		afterRun  func(dir string, t *testing.T) // called after Run
    72  		bfiles    map[string]string              // additional files to write before building
    73  		build     string                         // "wasm", "default", "none"
    74  	}
    75  
    76  	tcList := []tcase{
    77  		{
    78  			name:      "simple",
    79  			opts:      ParserGoPkgOpts{},
    80  			recursive: false,
    81  			infiles: map[string]string{
    82  				"root.vugu": `<div>root here</div>`,
    83  				"go.mod":    "module testcase\nreplace github.com/vugu/vugu => " + pwd + "\n",
    84  				"main.go":   "package main\nfunc main(){}",
    85  			},
    86  			out: map[string][]string{
    87  				"root_gen.go":      {`func \(c \*Root\) Build`},
    88  				"0_missing_gen.go": {`type Root struct`},
    89  			},
    90  			build: "default",
    91  		},
    92  		{
    93  			name:      "simple-wasm",
    94  			opts:      ParserGoPkgOpts{},
    95  			recursive: false,
    96  			infiles: map[string]string{
    97  				"root.vugu": `<div>root here</div>`,
    98  				"go.mod":    "module testcase\nreplace github.com/vugu/vugu => " + pwd + "\n",
    99  			},
   100  			out: map[string][]string{
   101  				"root_gen.go":      {`func \(c \*Root\) Build`},
   102  				"0_missing_gen.go": {`type Root struct`},
   103  			},
   104  			build: "wasm",
   105  		},
   106  		{
   107  			name:      "recursive",
   108  			opts:      ParserGoPkgOpts{},
   109  			recursive: true,
   110  			infiles: map[string]string{
   111  				"root.vugu":            `<div>root here</div>`,
   112  				"go.mod":               "module testcase\nreplace github.com/vugu/vugu => " + pwd + "\n",
   113  				"main.go":              "package main\nfunc main(){}",
   114  				"subdir1/example.vugu": "<div>Example Here</div>",
   115  			},
   116  			out: map[string][]string{
   117  				"root_gen.go":            {`func \(c \*Root\) Build`, `root here`},
   118  				"0_missing_gen.go":       {`type Root struct`},
   119  				"subdir1/example_gen.go": {`Example Here`},
   120  			},
   121  			build: "default",
   122  		},
   123  		{
   124  			name:      "recursive-single",
   125  			opts:      ParserGoPkgOpts{MergeSingle: true},
   126  			recursive: true,
   127  			infiles: map[string]string{
   128  				"root.vugu":            `<div>root here</div>`,
   129  				"go.mod":               "module testcase\nreplace github.com/vugu/vugu => " + pwd + "\n",
   130  				"main.go":              "package main\nfunc main(){}",
   131  				"subdir1/example.vugu": "<div>Example Here</div>",
   132  			},
   133  			out: map[string][]string{
   134  				"0_components_gen.go":         {`func \(c \*Root\) Build`, `type Root struct`},
   135  				"subdir1/0_components_gen.go": {`Example Here`},
   136  				"root.vugu":                   {`root here`}, // make sure vugu files didn't get nuked
   137  				"subdir1/example.vugu":        {`Example Here`},
   138  			},
   139  			afterRun: func(dir string, t *testing.T) {
   140  				noFile(filepath.Join(dir, "subdir1/example_gen.go"), t)
   141  			},
   142  			build: "default",
   143  		},
   144  		{
   145  			name:      "events",
   146  			opts:      ParserGoPkgOpts{},
   147  			recursive: false,
   148  			infiles: map[string]string{
   149  				"root.vugu": `<div>root here</div>`,
   150  				"go.mod":    "module testcase\nreplace github.com/vugu/vugu => " + pwd + "\n",
   151  				"main.go":   "package main\nfunc main(){}\n\n//vugugen:event Sample\n",
   152  			},
   153  			out: map[string][]string{
   154  				"root_gen.go":      {`func \(c \*Root\) Build`},
   155  				"0_missing_gen.go": {`type Root struct`, `SampleEvent`, `SampleHandler`, `SampleFunc`},
   156  			},
   157  			build: "default",
   158  		},
   159  	}
   160  
   161  	for _, tc := range tcList {
   162  		tc := tc
   163  		t.Run(tc.name, func(t *testing.T) {
   164  
   165  			tmpDir, err := os.MkdirTemp("", "TestRun")
   166  			if err != nil {
   167  				t.Fatal(err)
   168  			}
   169  
   170  			if debug {
   171  				t.Logf("Test %q using tmpDir: %s", tc.name, tmpDir)
   172  			} else {
   173  				t.Parallel()
   174  			}
   175  
   176  			tstWriteFiles(tmpDir, tc.infiles)
   177  
   178  			if tc.recursive {
   179  				err = RunRecursive(tmpDir, &tc.opts)
   180  			} else {
   181  				err = Run(tmpDir, &tc.opts)
   182  			}
   183  			if err != nil {
   184  				t.Fatal(err)
   185  			}
   186  
   187  			for fname, patterns := range tc.out {
   188  				b, err := os.ReadFile(filepath.Join(tmpDir, fname))
   189  				if err != nil {
   190  					t.Errorf("failed to read file %q after Run: %v", fname, err)
   191  					continue
   192  				}
   193  				for _, pattern := range patterns {
   194  					re := regexp.MustCompile(pattern)
   195  					if !re.Match(b) {
   196  						t.Errorf("failed to match regexp on file %q: %s", fname, pattern)
   197  					}
   198  				}
   199  			}
   200  
   201  			if tc.afterRun != nil {
   202  				tc.afterRun(tmpDir, t)
   203  			}
   204  
   205  			tstWriteFiles(tmpDir, tc.bfiles)
   206  
   207  			cmd := exec.Command("go", "mod", "tidy")
   208  			cmd.Dir = tmpDir
   209  			b, err := cmd.CombinedOutput()
   210  			if err != nil {
   211  				t.Fatalf("go mod tidy error: %s; OUTPUT:\n%s", err, b)
   212  			}
   213  
   214  			switch tc.build {
   215  
   216  			case "wasm":
   217  				cmd := exec.Command("go", "build", "-o", "main.wasm", ".")
   218  				cmd.Dir = tmpDir
   219  				cmd.Env = os.Environ() // needed?
   220  				cmd.Env = append(cmd.Env, "GOOS=js", "GOARCH=wasm")
   221  				b, err := cmd.CombinedOutput()
   222  				if err != nil {
   223  					t.Fatalf("build error: %s; OUTPUT:\n%s", err, b)
   224  				}
   225  
   226  			case "default":
   227  				cmd := exec.Command("go", "build", "-o", "main.out", ".")
   228  				cmd.Dir = tmpDir
   229  				b, err := cmd.CombinedOutput()
   230  				if err != nil {
   231  					t.Fatalf("build error: %s; OUTPUT:\n%s", err, b)
   232  				}
   233  
   234  				cmd = exec.Command(filepath.Join(tmpDir, "main.out"))
   235  				cmd.Dir = tmpDir
   236  				b, err = cmd.CombinedOutput()
   237  				if err != nil {
   238  					t.Fatalf("run error: %s; OUTPUT:\n%s", err, b)
   239  				}
   240  
   241  			case "none":
   242  
   243  			default:
   244  				t.Errorf("unknown build value %q", tc.build)
   245  			}
   246  
   247  			// only if everthing is golden do we remove
   248  			if !t.Failed() {
   249  				os.RemoveAll(tmpDir)
   250  			}
   251  
   252  		})
   253  	}
   254  
   255  }
   256  
   257  func noFile(p string, t *testing.T) {
   258  	_, err := os.Stat(p)
   259  	if err == nil {
   260  		t.Errorf("file %q should not exist but does", p)
   261  	}
   262  }
   263  
   264  func tstWriteFiles(dir string, m map[string]string) {
   265  
   266  	for name, contents := range m {
   267  		p := filepath.Join(dir, name)
   268  		err := os.MkdirAll(filepath.Dir(p), 0755)
   269  		if err != nil {
   270  			panic(err)
   271  		}
   272  		err = os.WriteFile(p, []byte(contents), 0644)
   273  		if err != nil {
   274  			panic(err)
   275  		}
   276  	}
   277  
   278  }