1366 lines
26 KiB
Go
1366 lines
26 KiB
Go
package lang
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestGenerateGoHello(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun greet(name: String): String {
|
|
return "hello, " + name
|
|
}
|
|
|
|
fun main() {
|
|
val message = greet("world")
|
|
println(message)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`package demo`,
|
|
`func greet(name string) string`,
|
|
`return "hello, " + name`,
|
|
`fmt.Println(message)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoImports(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import strings
|
|
import rand math.rand
|
|
|
|
fun main() {
|
|
val upper = strings.ToUpper("go")
|
|
println(upper)
|
|
println(rand.Intn(3))
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`"strings"`,
|
|
`rand "math/rand"`,
|
|
`upper := gotlinAutoThrow(strings.ToUpper("go"))`,
|
|
`fmt.Println(rand.Intn(3))`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoQualifiedTypes(t *testing.T) {
|
|
src := `
|
|
package demo.web
|
|
|
|
import fmt
|
|
import net.http
|
|
|
|
fun helloHandler(w: http.ResponseWriter, r: *http.Request) {
|
|
fmt.Fprintln(w, "hello")
|
|
}
|
|
|
|
fun main() {
|
|
http.HandleFunc("/", helloHandler)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`package web`,
|
|
`func helloHandler(w http.ResponseWriter, r *http.Request)`,
|
|
`fmt.Fprintln(w, "hello")`,
|
|
`http.HandleFunc("/", helloHandler)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoTypedLambda(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import fmt
|
|
import net.http
|
|
|
|
fun main() {
|
|
http.HandleFunc("/", { w: http.ResponseWriter, r: *http.Request ->
|
|
fmt.Fprintln(w, "hello from lambda")
|
|
})
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {`,
|
|
`fmt.Fprintln(w, "hello from lambda")`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoTrailingLambda(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun apply(value: String, fn: (String) -> Unit) {
|
|
fn(value)
|
|
}
|
|
|
|
fun main() {
|
|
apply("world") { value -> println(value) }
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`apply("world", func(value string) {`,
|
|
`fmt.Println(value)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoHandleFuncKotlinSyntax(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import fmt
|
|
import net.http
|
|
|
|
fun helloHandler(w: http.ResponseWriter, r: *http.Request) {
|
|
fmt.Fprintln(w, "hello")
|
|
}
|
|
|
|
fun main() {
|
|
http.HandleFunc("/") { w, r -> helloHandler(w, r) }
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {`,
|
|
`helloHandler(w, r)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoImplicitItLambda(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun apply(value: String, fn: (String) -> Unit) {
|
|
fn(value)
|
|
}
|
|
|
|
fun main() {
|
|
apply("world", { println(it) })
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`func apply(value string, fn func(string))`,
|
|
`apply("world", func(it string) {`,
|
|
`fmt.Println(it)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoClass(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
class Greeter(val name: String) {
|
|
fun greet() {
|
|
println("hello, " + name)
|
|
println(this.name)
|
|
}
|
|
}
|
|
|
|
fun main() {
|
|
val greeter = Greeter("world")
|
|
greeter.greet()
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`type Greeter struct {`,
|
|
`name string`,
|
|
`func NewGreeter(name string) *Greeter`,
|
|
`func (self *Greeter) greet() {`,
|
|
`fmt.Println("hello, " + self.name)`,
|
|
`fmt.Println(self.name)`,
|
|
`greeter := gotlinAutoThrow(NewGreeter("world"))`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoInterface(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
interface Greeter {
|
|
fun greet(name: String): String
|
|
}
|
|
|
|
class ConsoleGreeter(val prefix: String) {
|
|
fun greet(name: String): String {
|
|
return prefix + name
|
|
}
|
|
}
|
|
|
|
fun greetAll(greeter: Greeter) {
|
|
println(greeter.greet("world"))
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`type Greeter interface {`,
|
|
`greet(name string) string`,
|
|
`type ConsoleGreeter struct {`,
|
|
`func (self *ConsoleGreeter) greet(name string) string {`,
|
|
`return self.prefix + name`,
|
|
`func greetAll(greeter Greeter)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoHTTPServerWithInterfaceMethodHandler(t *testing.T) {
|
|
src := `
|
|
package examples.http
|
|
|
|
import fmt
|
|
import net.http
|
|
|
|
interface EpicController {
|
|
fun hello(w: http.ResponseWriter, r: *http.Request)
|
|
}
|
|
|
|
class EpicControllerImpl: EpicController {
|
|
override fun hello(w: http.ResponseWriter, r: *http.Request) {
|
|
fmt.Fprintln(w, "hello from gotlin")
|
|
}
|
|
}
|
|
|
|
fun main() {
|
|
val epicController: EpicController = EpicControllerImpl()
|
|
fmt.Println("serving http://localhost:8080")
|
|
http.HandleFunc("/", epicController.hello)
|
|
http.ListenAndServe(":8080", http.DefaultServeMux)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`type EpicController interface {`,
|
|
`hello(w http.ResponseWriter, r *http.Request)`,
|
|
`type EpicControllerImpl struct {`,
|
|
`func NewEpicControllerImpl() *EpicControllerImpl`,
|
|
`func (self *EpicControllerImpl) hello(w http.ResponseWriter, r *http.Request) {`,
|
|
`var epicController EpicController = gotlinAutoThrow(NewEpicControllerImpl())`,
|
|
`http.HandleFunc("/", epicController.hello)`,
|
|
`http.ListenAndServe(":8080", http.DefaultServeMux)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAliasedStringImport(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import _ github.com.lib.pq
|
|
import database.sql
|
|
|
|
fun main() {
|
|
println(sql.Drivers())
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`_ "github.com/lib/pq"`,
|
|
`"database/sql"`,
|
|
`fmt.Println(sql.Drivers())`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoMultiAssignAndNull(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import database.sql
|
|
|
|
fun main() {
|
|
val db, err = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")
|
|
if (err != null) {
|
|
return
|
|
}
|
|
db.Close()
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`db, err := sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")`,
|
|
`if err != nil {`,
|
|
`db.Close()`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAutoBlankImportForSideEffects(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import github.com.lib.pq
|
|
import database.sql
|
|
|
|
fun main() {
|
|
val db, err = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")
|
|
if (err != null) {
|
|
return
|
|
}
|
|
db.Close()
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`_ "github.com/lib/pq"`,
|
|
`"database/sql"`,
|
|
`db, err := sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoExceptionsAndRunCatching(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun mayFail() {
|
|
throw "boom"
|
|
}
|
|
|
|
fun main() {
|
|
val result = runCatching({ mayFail() })
|
|
if (result.isSuccess()) {
|
|
println("ok")
|
|
} else {
|
|
println(result.exceptionOrNull())
|
|
}
|
|
|
|
try {
|
|
mayFail()
|
|
} catch (e: String) {
|
|
println(e)
|
|
}
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`type gotlinResult struct {`,
|
|
`func gotlinRunCatching(fn func()) (result gotlinResult) {`,
|
|
`panic("boom")`,
|
|
`result := gotlinAutoThrow(gotlinRunCatching(func() {`,
|
|
`if recovered := recover(); recovered != nil {`,
|
|
`e := recovered`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAutoThrowForGoErrorResults(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import database.sql
|
|
|
|
fun main() {
|
|
val db = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")
|
|
val err = db.Ping()
|
|
if (err != null) {
|
|
throw err
|
|
}
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`func gotlinAutoThrow[T any](value T, rest ...any) T {`,
|
|
`db := gotlinAutoThrow(sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable"))`,
|
|
`err := gotlinAutoThrow(db.Ping())`,
|
|
`if err != nil {`,
|
|
`panic(err)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAutoThrowForErrorExprStmt(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import database.sql
|
|
|
|
fun main() {
|
|
val db = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")
|
|
db.Ping()
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`db := gotlinAutoThrow(sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable"))`,
|
|
`db.Ping()`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoClassWithoutBodyBraces(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
class User(val Name: String)
|
|
|
|
fun main() {
|
|
val user = User("alice")
|
|
println(user.Name)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`type User struct {`,
|
|
`func NewUser(Name string) *User`,
|
|
`user := gotlinAutoThrow(NewUser("alice"))`,
|
|
`fmt.Println(user.Name)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoGenericListAndMapTypes(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
class Store(val names: List<String>, val counts: Map<String, Int>)
|
|
|
|
fun apply(values: List<String>, f: (Map<String, Int>) -> Unit) {
|
|
f(null)
|
|
println(values)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`type Store struct {`,
|
|
`names []string`,
|
|
`counts map[string]int`,
|
|
`func NewStore(names []string, counts map[string]int) *Store`,
|
|
`func apply(values []string, f func(map[string]int))`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoListAndMapLiterals(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
val names: List<String> = listOf("alice", "bob")
|
|
val ages: Map<String, Int> = mapOf("alice", 30, "bob", 25)
|
|
println(names)
|
|
println(ages)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`var names []string = gotlinAutoThrow([]string{"alice", "bob"})`,
|
|
`var ages map[string]int = gotlinAutoThrow(map[string]int{"alice": 30, "bob": 25})`,
|
|
`fmt.Println(names)`,
|
|
`fmt.Println(ages)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoListAndMapLiteralsWithCallTypeArgs(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
val test = listOf<Int>()
|
|
val labels = mapOf<String, Int>()
|
|
println(test)
|
|
println(labels)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`test := gotlinAutoThrow([]int{})`,
|
|
`labels := gotlinAutoThrow(map[string]int{})`,
|
|
`fmt.Println(test)`,
|
|
`fmt.Println(labels)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoUnusedLocalDoesNotEmitNamedVariable(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
val test = listOf<Int>(1, 7)
|
|
println("ok")
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`_ = gotlinAutoThrow([]int{1, 7})`,
|
|
`fmt.Println("ok")`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
if strings.Contains(code, `test :=`) || strings.Contains(code, `var test `) {
|
|
t.Fatalf("generated Go should not contain named unused local:\n%s", code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoGoStatement(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import fmt
|
|
|
|
fun runWorker(name: String) {
|
|
fmt.Println(name)
|
|
}
|
|
|
|
fun main() {
|
|
go runWorker("alice")
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`func runWorker(name string)`,
|
|
`fmt.Println(name)`,
|
|
`go func() {`,
|
|
`runWorker("alice")`,
|
|
`println("async panic:", recovered)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoForbidsWorkerSelfCalls(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
worker Counter {
|
|
val n = 0
|
|
fun inc() {
|
|
inc()
|
|
}
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
_, err = GenerateGo(prog)
|
|
if err == nil {
|
|
t.Fatal("expected worker self-call generation error")
|
|
}
|
|
if !strings.Contains(err.Error(), "worker self-calls are forbidden") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoRejectsValReassignment(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
val count = 0
|
|
count = 1
|
|
}
|
|
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
_, err = GenerateGo(prog)
|
|
if err == nil {
|
|
t.Fatal("expected val reassignment error")
|
|
}
|
|
if !strings.Contains(err.Error(), "cannot reassign immutable name count") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoDefer(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import database.sql
|
|
|
|
fun main() {
|
|
val rows = sql.Open("postgres", "dsn")
|
|
defer rows.Close()
|
|
}
|
|
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
if !strings.Contains(string(out), "defer rows.Close()") {
|
|
t.Fatalf("generated Go missing defer:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoDecimalLiteral(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun interest(): Double {
|
|
return 0.025
|
|
}
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
for _, want := range []string{"func interest() float64", "return 0.025"} {
|
|
if !strings.Contains(string(out), want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoRejectsMultiValReassignment(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import database.sql
|
|
|
|
fun main() {
|
|
val db, err = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")
|
|
err = nil
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
_, err = GenerateGo(prog)
|
|
if err == nil {
|
|
t.Fatal("expected multi-val reassignment error")
|
|
}
|
|
if !strings.Contains(err.Error(), "cannot reassign immutable name err") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAllowsVarReassignment(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
var count = 0
|
|
count = 1
|
|
count += 2
|
|
println(count)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`count := 0`,
|
|
`count = 1`,
|
|
`count += 2`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoRejectsReassignmentToImmutableClassField(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
class Counter(val count: Int, var total: Int) {
|
|
fun freeze() {
|
|
count = 1
|
|
}
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
_, err = GenerateGo(prog)
|
|
if err == nil {
|
|
t.Fatal("expected immutable class field reassignment error")
|
|
}
|
|
if !strings.Contains(err.Error(), "cannot reassign immutable name count") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAllowsReassignmentToMutableClassField(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
class Counter(var count: Int) {
|
|
fun inc() {
|
|
count += 1
|
|
}
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
if !strings.Contains(string(out), `self.count += 1`) {
|
|
t.Fatalf("generated Go missing mutable field assignment:\n%s", string(out))
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoWorkerSyncPanicPropagationScaffolding(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
worker Counter {
|
|
fun boom() {
|
|
throw "x"
|
|
}
|
|
}
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`panicCh := make(chan any, 1)`,
|
|
`if recovered := recover(); recovered != nil {`,
|
|
`panicCh <- recovered`,
|
|
`case recovered := <-panicCh:`,
|
|
`panic(recovered)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseGoStatementRequiresCall(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
go 123
|
|
}
|
|
`
|
|
|
|
_, err := Parse(src)
|
|
if err == nil {
|
|
t.Fatal("expected parse error")
|
|
}
|
|
if !strings.Contains(err.Error(), "'go' expects a function call expression") {
|
|
t.Fatalf("unexpected parse error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoChannelsAndSelect(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun writer(ch: Channel<Int>) {
|
|
ch.send(7)
|
|
}
|
|
|
|
fun main() {
|
|
val ch = Channel<Int>()
|
|
go writer(ch)
|
|
select {
|
|
ch -> println(it)
|
|
}
|
|
val v = ch.read()
|
|
println(v)
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`func writer(ch chan int)`,
|
|
`ch <- 7`,
|
|
`ch := gotlinAutoThrow(make(chan int))`,
|
|
`go func() {`,
|
|
`writer(ch)`,
|
|
`select {`,
|
|
`case it := <-ch:`,
|
|
`fmt.Println(it)`,
|
|
`v := gotlinAutoThrow(<-ch)`,
|
|
`fmt.Println(v)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoSelectAfterAndEvery(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
select {
|
|
after(1000) -> println(it)
|
|
every(250) -> println("tick")
|
|
}
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`"time"`,
|
|
`func gotlinEveryMs(ms int) <-chan time.Time {`,
|
|
`case it := <-time.After(time.Duration(1000) * time.Millisecond):`,
|
|
`fmt.Println(it)`,
|
|
`case <-gotlinEveryMs(250):`,
|
|
`fmt.Println("tick")`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoEveryRejectsNonPositiveMilliseconds(t *testing.T) {
|
|
cases := []string{
|
|
`every(0)`,
|
|
`every(-1)`,
|
|
}
|
|
for _, timerCall := range cases {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
select {
|
|
` + timerCall + ` -> println("x")
|
|
}
|
|
}
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
_, err = GenerateGo(prog)
|
|
if err == nil {
|
|
t.Fatalf("expected go generation error for %s", timerCall)
|
|
}
|
|
if !strings.Contains(err.Error(), "every(ms) requires ms > 0") {
|
|
t.Fatalf("unexpected error for %s: %v", timerCall, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAfterEveryArityValidation(t *testing.T) {
|
|
cases := []struct {
|
|
call string
|
|
want string
|
|
}{
|
|
{`after()`, "after(ms) expects exactly one Int argument"},
|
|
{`after(1, 2)`, "after(ms) expects exactly one Int argument"},
|
|
{`every()`, "every(ms) expects exactly one Int argument"},
|
|
{`every(1, 2)`, "every(ms) expects exactly one Int argument"},
|
|
}
|
|
for _, tc := range cases {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
select {
|
|
` + tc.call + ` -> println("x")
|
|
}
|
|
}
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
_, err = GenerateGo(prog)
|
|
if err == nil {
|
|
t.Fatalf("expected go generation error for %s", tc.call)
|
|
}
|
|
if !strings.Contains(err.Error(), tc.want) {
|
|
t.Fatalf("unexpected error for %s: %v", tc.call, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAfterEveryIntValidation(t *testing.T) {
|
|
cases := []struct {
|
|
call string
|
|
want string
|
|
}{
|
|
{`after("1s")`, "after(ms) expects an Int argument"},
|
|
{`every(true)`, "every(ms) expects an Int argument"},
|
|
}
|
|
for _, tc := range cases {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
select {
|
|
` + tc.call + ` -> println("x")
|
|
}
|
|
}
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
_, err = GenerateGo(prog)
|
|
if err == nil {
|
|
t.Fatalf("expected go generation error for %s", tc.call)
|
|
}
|
|
if !strings.Contains(err.Error(), tc.want) {
|
|
t.Fatalf("unexpected error for %s: %v", tc.call, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoNoTimeImportWhenUnused(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
fun main() {
|
|
println("ok")
|
|
}
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
code := string(out)
|
|
if strings.Contains(code, `"time"`) {
|
|
t.Fatalf("unexpected time import in generated code:\n%s", code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoNoTickerShorthandRewrite(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import time
|
|
|
|
fun main() {
|
|
val tick = time.NewTicker(time.Second)
|
|
select {
|
|
tick -> println("x")
|
|
}
|
|
}
|
|
`
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
code := string(out)
|
|
if strings.Contains(code, "<-tick.C") {
|
|
t.Fatalf("ticker shorthand rewrite should be removed:\n%s", code)
|
|
}
|
|
if !strings.Contains(code, "case <-tick:") {
|
|
t.Fatalf("expected raw source receive in generated code:\n%s", code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateGoAutoThrowForBunStyleCalls(t *testing.T) {
|
|
src := `
|
|
package demo
|
|
|
|
import context
|
|
import github.com.uptrace.bun
|
|
|
|
class Repo(val db: *bun.DB) {
|
|
fun run() {
|
|
val ctx = context.Background()
|
|
db.NewSelect().ColumnExpr("1").Scan(ctx)
|
|
val total = db.NewSelect().ColumnExpr("1").Count(ctx)
|
|
println(total)
|
|
}
|
|
}
|
|
`
|
|
|
|
prog, err := Parse(src)
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
|
|
out, err := GenerateGo(prog)
|
|
if err != nil {
|
|
t.Fatalf("go generation failed: %v", err)
|
|
}
|
|
|
|
code := string(out)
|
|
for _, want := range []string{
|
|
`self.db.NewSelect().ColumnExpr("1").Scan(ctx)`,
|
|
`total := gotlinAutoThrow(self.db.NewSelect().ColumnExpr("1").Count(ctx))`,
|
|
`fmt.Println(total)`,
|
|
} {
|
|
if !strings.Contains(code, want) {
|
|
t.Fatalf("generated Go missing %q:\n%s", want, code)
|
|
}
|
|
}
|
|
}
|