-
-
Notifications
You must be signed in to change notification settings - Fork 656
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CI] Run display tests with both xml and json rpc api (#11506)
* [tests] prepare for json rpc vs xml display tests * [tests] add display test for static field completion Also run again a couple tests after caching type * [tests] display test with both xml and json rpc api * [ci] run both xml and jsonrpc display tests * [display] send completion error when no results (same as xml display api) * [tests] don't rely on result.result == null on completion error * [tests] only run Toplevel.testDuplicates for xml display
- Loading branch information
Showing
29 changed files
with
899 additions
and
361 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import haxe.io.Bytes; | ||
|
||
using StringTools; | ||
|
||
import Types; | ||
|
||
class BaseDisplayTestContext { | ||
static var haxeServer = haxeserver.HaxeServerSync.launch("haxe", []); | ||
|
||
var markers:Map<Int, Int>; | ||
var fieldName:String; | ||
|
||
public final source:File; | ||
|
||
public function new(path:String, fieldName:String, source:String, markers:Map<Int, Int>) { | ||
this.fieldName = fieldName; | ||
this.source = new File(path, source); | ||
this.markers = markers; | ||
} | ||
|
||
public function pos(id:Int):Position { | ||
var r = markers[id]; | ||
if (r == null) | ||
throw "No such marker: " + id; | ||
return new Position(r); | ||
} | ||
|
||
public function range(pos1:Int, pos2:Int) { | ||
return normalizePath(source.formatRange(pos(pos1), pos(pos2))); | ||
} | ||
|
||
public function hasErrorMessage(f:()->Void, message:String) { | ||
return try { | ||
f(); | ||
false; | ||
} catch (exc:HaxeInvocationException) { | ||
return exc.message.indexOf(message) != -1; | ||
} | ||
} | ||
|
||
static public function runHaxe(args:Array<String>, ?stdin:String) { | ||
return haxeServer.rawRequest(args, stdin == null ? null : Bytes.ofString(stdin)); | ||
} | ||
|
||
static function normalizePath(p:String):String { | ||
if (!haxe.io.Path.isAbsolute(p)) { | ||
p = Sys.getCwd() + p; | ||
} | ||
if (Sys.systemName() == "Windows") { | ||
// on windows, haxe returns paths with backslashes, drive letter uppercased | ||
p = p.substr(0, 1).toUpperCase() + p.substr(1); | ||
p = p.replace("/", "\\"); | ||
} | ||
return p; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import haxe.display.Display; | ||
import haxe.display.JsonModuleTypes; | ||
|
||
using Lambda; | ||
|
||
class DisplayPrinter { | ||
var indent = ""; | ||
public function new() {} | ||
|
||
public function printPath(path:JsonTypePath) { | ||
final qualified = !(path.moduleName == "StdTypes" && path.pack.length == 0); | ||
final isSubType = path.moduleName != path.typeName; | ||
final isToplevelType = path.pack.length == 0 && !isSubType; | ||
|
||
if (isToplevelType && path.importStatus == Shadowed) { | ||
path.pack.push("std"); | ||
} | ||
|
||
function printFullPath() { | ||
var printedPath = if (isSubType) path.typeName else path.moduleName; | ||
if (path.pack.length > 0) { | ||
printedPath = path.pack.join(".") + "." + printedPath; | ||
} | ||
return printedPath; | ||
} | ||
|
||
return if (qualified) printFullPath() else path.typeName; | ||
} | ||
|
||
public function printPathWithParams(path:JsonTypePathWithParams) { | ||
final s = printPath(path.path); | ||
if (path.params.length == 0) { | ||
return s; | ||
} else { | ||
var sparams = path.params.map(printType).join(", "); | ||
return '$s<$sparams>'; | ||
} | ||
} | ||
|
||
public function printType<T>(t:JsonType<T>) { | ||
return switch t.kind { | ||
case TMono: "Unknown<0>"; | ||
case TInst | TEnum | TType | TAbstract: printPathWithParams(t.args); | ||
case TDynamic: | ||
if (t.args == null) { | ||
"Dynamic"; | ||
} else { | ||
final s = printTypeRec(t.args); | ||
'Dynamic<$s>'; | ||
} | ||
case TAnonymous: | ||
final fields = t.args.fields; | ||
final s = [ | ||
for (field in fields) { | ||
var prefix = if (hasMeta(field.meta, ":optional")) "?" else ""; | ||
'$prefix${field.name} : ${printTypeRec(field.type)}'; | ||
} | ||
].join(", "); | ||
s == '' ? '{ }' : '{ $s }'; | ||
case TFun: | ||
var hasNamed = false; | ||
function printFunctionArgument(arg:JsonFunctionArgument) { | ||
if (arg.name != "") { | ||
hasNamed = true; | ||
} | ||
return this.printFunctionArgument(arg); | ||
} | ||
final args = t.args.args.map(printFunctionArgument); | ||
var r = printTypeRec(t.args.ret); | ||
if (t.args.ret.kind == TFun) r = '($r)'; | ||
switch args.length { | ||
case 0: '() -> $r'; | ||
case 1 if (hasNamed): '(${args[0]}) -> $r'; | ||
case 1: '${args[0]} -> $r'; | ||
case _: '(${args.join(", ")}) -> $r'; | ||
} | ||
} | ||
} | ||
|
||
function printTypeRec<T>(t:JsonType<T>) { | ||
final old = indent; | ||
indent += " "; | ||
final t = printType(t); | ||
indent = old; | ||
return t; | ||
} | ||
|
||
public function printFunctionArgument<T>(arg:JsonFunctionArgument):String { | ||
final nullRemoval = removeNulls(arg.t); | ||
final concreteType = if (!arg.opt) arg.t else nullRemoval.type; | ||
|
||
var argument = (if (arg.opt && arg.value == null) "?" else "") + arg.name; | ||
if (concreteType.kind != TMono || arg.name == "") { | ||
var hint = printTypeRec(concreteType); | ||
if (concreteType.kind == TFun) hint = '($hint)'; | ||
argument += (arg.name == "" ? "" : " : ") + hint; | ||
} | ||
if (arg.value != null) { | ||
argument += " = " + arg.value.string; | ||
} | ||
return argument; | ||
} | ||
|
||
public function printSignatureFunctionArgument<T>(arg:JsonFunctionArgument):String { | ||
final nullRemoval = removeNulls(arg.t); | ||
final concreteType = if (!arg.opt) arg.t else nullRemoval.type; | ||
|
||
var argument = (if (arg.opt && arg.value == null) "?" else "") + arg.name; | ||
var hint = printTypeRec(concreteType); | ||
if (concreteType.kind == TFun) hint = '($hint)'; | ||
argument += ":" + hint; | ||
if (arg.value != null) { | ||
argument += " = " + arg.value.string; | ||
} | ||
return argument; | ||
} | ||
|
||
public function printCallArguments<T>(signature:JsonFunctionSignature, printFunctionArgument:JsonFunctionArgument->String) { | ||
return "(" + signature.args.map(printFunctionArgument).join(", ") + ")"; | ||
} | ||
|
||
function removeNulls<T>(type:JsonType<T>, nullable:Bool = false):{type:JsonType<T>, nullable:Bool} { | ||
switch type.kind { | ||
case TAbstract: | ||
final path:JsonTypePathWithParams = type.args; | ||
if (getDotPath(type) == "StdTypes.Null") { | ||
if (path.params != null && path.params[0] != null) { | ||
return removeNulls(path.params[0], true); | ||
} | ||
} | ||
case _: | ||
} | ||
return {type: type, nullable: nullable}; | ||
} | ||
|
||
inline function isVoid<T>(type:JsonType<T>) { | ||
return getDotPath(type) == "StdTypes.Void"; | ||
} | ||
|
||
function getDotPath<T>(type:JsonType<T>):Null<String> { | ||
final path = getTypePath(type); | ||
if (path == null) { | ||
return null; | ||
} | ||
return printPath(path.path); | ||
} | ||
|
||
function getTypePath<T>(type:JsonType<T>):Null<JsonTypePathWithParams> { | ||
return switch type.kind { | ||
case null: null; | ||
case TInst | TEnum | TType | TAbstract: type.args; | ||
case _: null; | ||
} | ||
} | ||
|
||
function hasMeta(?meta:JsonMetadata, name:String) { | ||
return meta != null && meta.exists(meta -> meta.name == cast name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,114 +1,5 @@ | ||
import haxe.display.Position.Range; | ||
import utest.Assert; | ||
import Types; | ||
|
||
using Lambda; | ||
|
||
@:autoBuild(Macro.buildTestCase()) | ||
class DisplayTestCase implements utest.ITest { | ||
var ctx:DisplayTestContext; | ||
|
||
public function new() {} | ||
|
||
// api | ||
inline function pos(name) | ||
return ctx.pos(name); | ||
|
||
inline function fields(pos) | ||
return ctx.fields(pos); | ||
|
||
inline function toplevel(pos) | ||
return ctx.toplevel(pos); | ||
|
||
inline function type(pos) | ||
return ctx.type(pos); | ||
|
||
inline function position(pos) | ||
return ctx.position(pos); | ||
|
||
inline function usage(pos) | ||
return ctx.usage(pos); | ||
|
||
inline function range(pos1, pos2) | ||
return ctx.range(pos1, pos2); | ||
|
||
inline function signature(pos1) | ||
return ctx.signature(pos1); | ||
|
||
inline function doc(pos1) | ||
return ctx.doc(pos1); | ||
|
||
inline function metadataDoc(pos1) | ||
return ctx.metadataDoc(pos1); | ||
|
||
inline function diagnostics() | ||
return ctx.diagnostics(); | ||
|
||
inline function noCompletionPoint(f) | ||
return ctx.hasErrorMessage(f, "No completion point"); | ||
|
||
inline function typeNotFound(f, typeName) | ||
return ctx.hasErrorMessage(f, "Type not found : " + typeName); | ||
|
||
function assert(v:Bool) | ||
Assert.isTrue(v); | ||
|
||
function eq<T>(expected:T, actual:T, ?pos:haxe.PosInfos) { | ||
Assert.equals(expected, actual, pos); | ||
} | ||
|
||
function arrayEq<T>(expected:Array<T>, actual:Array<T>, ?pos:haxe.PosInfos) { | ||
Assert.same(expected, actual, pos); | ||
} | ||
|
||
function arrayCheck<T>(expected:Array<T>, actual:Array<T>, f:T->String, ?pos:haxe.PosInfos) { | ||
var expected = [for (expected in expected) f(expected) => expected]; | ||
for (actual in actual) { | ||
var key = f(actual); | ||
Assert.isTrue(expected.exists(key), "Result not part of expected Array: " + Std.string(actual), pos); | ||
expected.remove(key); | ||
} | ||
|
||
for (expected in expected) { | ||
Assert.fail("Expected result was not part of actual Array: " + Std.string(expected), pos); | ||
return; | ||
} | ||
} | ||
|
||
function hasField(a:Array<FieldElement>, name:String, type:String, ?kind:String):Bool { | ||
return a.exists(function(t) return t.type == type && t.name == name && (kind == null || t.kind == kind)); | ||
} | ||
|
||
function hasToplevel(a:Array<ToplevelElement>, kind:String, name:String, ?type:String = null):Bool { | ||
return a.exists(function(t) return t.kind == kind && t.name == name && (type == null || t.type == type)); | ||
} | ||
|
||
function hasPath(a:Array<FieldElement>, name:String):Bool { | ||
return a.exists(function(t) return t.name == name); | ||
} | ||
|
||
function diagnosticsRange(start:Position, end:Position):Range { | ||
var range = ctx.source.findRange(start, end); | ||
// this is probably correct...? | ||
range.start.character--; | ||
range.end.character--; | ||
return range; | ||
} | ||
|
||
function sigEq(arg:Int, params:Array<Array<String>>, sig:SignatureHelp, ?pos:haxe.PosInfos) { | ||
eq(arg, sig.activeParameter, pos); | ||
eq(params.length, sig.signatures.length, pos); | ||
for (i in 0...params.length) { | ||
var sigInf = sig.signatures[i]; | ||
var args = params[i]; | ||
eq(sigInf.parameters.length, args.length, pos); | ||
for (i in 0...args.length) { | ||
eq(sigInf.parameters[i].label, args[i], pos); | ||
} | ||
} | ||
} | ||
|
||
function report(message, pos:haxe.PosInfos) { | ||
Assert.fail(message, pos); | ||
} | ||
} | ||
#if (display.protocol == "jsonrpc") | ||
typedef DisplayTestCase = RpcDisplayTestCase; | ||
#else | ||
typedef DisplayTestCase = XmlDisplayTestCase; | ||
#end |
Oops, something went wrong.