Add structured coroutines and explicit error handling
This commit is contained in:
parent
de1262b4cf
commit
fe62e81152
31 changed files with 1701 additions and 325 deletions
142
internal/lang/result_test.go
Normal file
142
internal/lang/result_test.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package lang
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResultQuestionPropagatesGoError(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
import strconv
|
||||
fun parse(value: String): Result<Int, Error> {
|
||||
val parsed = strconv.atoi(value)?
|
||||
return Result::Ok(parsed)
|
||||
}`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := GenerateGo(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{"parsed, gotlinError1 := strconv.Atoi(value)", "if gotlinError1 != nil {", "GotlinResult[int]{Err: gotlinError1}", "GotlinResult[int]{Value: parsed}"} {
|
||||
if !strings.Contains(string(out), want) {
|
||||
t.Fatalf("missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoValueAndErrorReturnConvertsToResult(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
import strconv
|
||||
fun parse(value: String): Result<Int, Error> { return strconv.atoi(value) }
|
||||
fun assign(value: String) { val result: Result<Int, Error> = strconv.atoi(value); println(result.unwrapOr(0)) }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := GenerateGo(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{
|
||||
"value, err := strconv.Atoi(value)",
|
||||
"GotlinResult[int]{Value: value, Err: err}",
|
||||
"var result GotlinResult[int] = func() GotlinResult[int]",
|
||||
} {
|
||||
if !strings.Contains(string(out), want) {
|
||||
t.Fatalf("missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoErrorOnlyReturnConvertsToUnitResult(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
import os
|
||||
fun changeDirectory(path: String): Result<Unit, Error> { return os.chdir(path) }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := GenerateGo(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{
|
||||
"func changeDirectory(path string) GotlinResult[struct{}]",
|
||||
"err := os.Chdir(path)",
|
||||
"GotlinResult[struct{}]{Err: err}",
|
||||
} {
|
||||
if !strings.Contains(string(out), want) {
|
||||
t.Fatalf("missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResultQuestionPropagatesGotlinResult(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
import errors
|
||||
fun inner(ok: Boolean): Result<String, Error> { if (!ok) { return Result::Err(errors.new("failed")) }; return Result::Ok("ok") }
|
||||
fun outer(): Result<String, Error> { val value = inner(true)?; return Result::Ok(value) }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := GenerateGo(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{"gotlinResult1 := inner(true)", "gotlinResult1.Err", "value := gotlinResult1.Value"} {
|
||||
if !strings.Contains(string(out), want) {
|
||||
t.Fatalf("missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResultUnwrapAndUnwrapOr(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
fun value(result: Result<String, Error>): String { return result.unwrapOr("fallback") }
|
||||
fun required(result: Result<String, Error>): String { return result.unwrap() }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := GenerateGo(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{"gotlinResultUnwrapOr(result, \"fallback\")", "gotlinResultUnwrap(result)"} {
|
||||
if !strings.Contains(string(out), want) {
|
||||
t.Fatalf("missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuestionRequiresResultFunction(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
import strconv
|
||||
fun parse(value: String): Int { val parsed = strconv.atoi(value)?; return parsed }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = GenerateGo(prog)
|
||||
if err == nil || !strings.Contains(err.Error(), "returning Result") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuestionUseKeepsReferencedVariableLive(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
import strconv
|
||||
fun parse(value: String): Result<Int, Error> {
|
||||
val input = value
|
||||
val parsed = strconv.atoi(input)?
|
||||
return Result::Ok(parsed)
|
||||
}`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := GenerateGo(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(out), "input := value") || strings.Contains(string(out), "_ = value") {
|
||||
t.Fatalf("variable used by ? expression was removed:\n%s", out)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue