fixed colors module
This commit is contained in:
parent
d83b170e7f
commit
6c08ba8290
3 changed files with 142 additions and 150 deletions
|
|
@ -9,6 +9,8 @@
|
||||||
## This module implements graphical output for Nimrod; the current
|
## This module implements graphical output for Nimrod; the current
|
||||||
## implementation uses Cairo under the surface.
|
## implementation uses Cairo under the surface.
|
||||||
|
|
||||||
|
import strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
TColor* = distinct int ## a color stored as RGB
|
TColor* = distinct int ## a color stored as RGB
|
||||||
|
|
||||||
|
|
@ -16,9 +18,9 @@ proc `==` *(a, b: TColor): bool {.borrow.}
|
||||||
## compares two colors.
|
## compares two colors.
|
||||||
|
|
||||||
template extract(a: TColor, r, g, b: expr) =
|
template extract(a: TColor, r, g, b: expr) =
|
||||||
var r = a shr 16 and 0xff
|
var r = a.int shr 16 and 0xff
|
||||||
var g = a shr 8 and 0xff
|
var g = a.int shr 8 and 0xff
|
||||||
var b = a and 0xff
|
var b = a.int and 0xff
|
||||||
|
|
||||||
template rawRGB(r, g, b: expr): expr =
|
template rawRGB(r, g, b: expr): expr =
|
||||||
TColor(r shl 16 or g shl 8 or b)
|
TColor(r shl 16 or g shl 8 or b)
|
||||||
|
|
@ -64,9 +66,9 @@ template mix*(a, b: TColor, fn: expr): expr =
|
||||||
y = if y < 0: 0 else: 255
|
y = if y < 0: 0 else: 255
|
||||||
y
|
y
|
||||||
|
|
||||||
bind extract(a, ar, ag, ab)
|
(bind extract)(a, ar, ag, ab)
|
||||||
bind extract(b, br, bg, bb)
|
(bind extract)(b, br, bg, bb)
|
||||||
bind rawRGB(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb))
|
(bind rawRGB)(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb))
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -373,7 +375,7 @@ proc parseColor*(name: string): TColor =
|
||||||
## parses `name` to a color value. If no valid color could be
|
## parses `name` to a color value. If no valid color could be
|
||||||
## parsed ``EInvalidValue`` is raised.
|
## parsed ``EInvalidValue`` is raised.
|
||||||
if name[0] == '#':
|
if name[0] == '#':
|
||||||
result = TColor(parseHex(name))
|
result = TColor(parseHexInt(name))
|
||||||
else:
|
else:
|
||||||
var idx = binaryStrSearch(colorNames, name)
|
var idx = binaryStrSearch(colorNames, name)
|
||||||
if idx < 0: raise newException(EInvalidValue, "unkown color: " & name)
|
if idx < 0: raise newException(EInvalidValue, "unkown color: " & name)
|
||||||
|
|
|
||||||
|
|
@ -1140,4 +1140,19 @@ proc sleep*(milsecs: int) =
|
||||||
a.tv_nsec = (milsecs mod 1000) * 1000
|
a.tv_nsec = (milsecs mod 1000) * 1000
|
||||||
discard posix.nanosleep(a, b)
|
discard posix.nanosleep(a, b)
|
||||||
|
|
||||||
|
proc getFileSize*(file: string): biggestInt =
|
||||||
|
## returns the file size of `file`. Can raise ``EOS``.
|
||||||
|
when defined(windows):
|
||||||
|
var a: TWin32FindData
|
||||||
|
var resA = findfirstFileA(file, a)
|
||||||
|
if resA == -1: OSError()
|
||||||
|
result = rdFileSize(a)
|
||||||
|
findclose(resA)
|
||||||
|
else:
|
||||||
|
var f: TFile
|
||||||
|
if open(f, file):
|
||||||
|
result = getFileSize(f)
|
||||||
|
close(f)
|
||||||
|
else: OSError()
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
251
tests/tester.nim
251
tests/tester.nim
|
|
@ -8,73 +8,38 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
## This program verifies Nimrod against the testcases.
|
## This program verifies Nimrod against the testcases.
|
||||||
## The testcases may contain the directives '#ERROR' or '#ERROR_IN'.
|
|
||||||
## '#ERROR' is used to indicate that the compiler should report
|
|
||||||
## an error in the marked line (the line that contains the '#ERROR'
|
|
||||||
## directive.)
|
|
||||||
## The format for '#ERROR_IN' is:
|
|
||||||
## #ERROR_IN filename linenumber
|
|
||||||
## One can omit the extension of the filename ('.nim' is then used).
|
|
||||||
## Tests which contain none of the two directives should compile. Thus they
|
|
||||||
## are executed after successful compilation and their output is verified
|
|
||||||
## against the results specified with the '#OUT' directive.
|
|
||||||
## (Tests which require user interaction are not possible.)
|
|
||||||
## Tests can have an #ERROR_MSG directive specifiying the error message
|
|
||||||
## Nimrod shall produce.
|
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, pegs, os, osproc, streams, parsecsv
|
strutils, pegs, os, osproc, streams, parsecsv, browsers
|
||||||
|
|
||||||
const
|
const
|
||||||
cmdTemplate = r"nimrod cc --hints:on $options $filename"
|
cmdTemplate = r"nimrod cc --hints:on $# $#"
|
||||||
|
|
||||||
type
|
type
|
||||||
TSpec = object of TObject ## specification object
|
TMsg = tuple[
|
||||||
line: int ## line number where compiler should throw an error
|
file: string,
|
||||||
file: string ## file where compiler should throw an error
|
line: int,
|
||||||
err: bool ## true if the specification says there should be an error
|
msg: string,
|
||||||
outp: string ## output that should be produced
|
err: bool]
|
||||||
puremsg: string ## pure message of compiler
|
TOutp = tuple[file, outp: string]
|
||||||
|
TResult = tuple[test, expected, given: string, success: bool]
|
||||||
|
TResults = object
|
||||||
|
total, passed: int
|
||||||
|
data: seq[TResult]
|
||||||
|
|
||||||
proc myExec(cmd: string): string =
|
proc myExec(cmd: string): string =
|
||||||
#echo("Executing: " & cmd)
|
#echo("Executing: " & cmd)
|
||||||
result = osproc.execProcess(cmd)
|
result = osproc.execProcess(cmd)
|
||||||
#echo("Received: " & result)
|
#echo("Received: " & result)
|
||||||
|
|
||||||
proc parseTest(filename: string): TSpec =
|
|
||||||
var i = 0 # the line counter
|
|
||||||
var matches: array [0..2, string]
|
|
||||||
result.outp = ""
|
|
||||||
result.puremsg = ""
|
|
||||||
result.file = filename
|
|
||||||
for s in lines(filename):
|
|
||||||
inc(i)
|
|
||||||
if contains(s, peg"'#OUT' \s+ {.*}", matches):
|
|
||||||
result.outp = matches[0]
|
|
||||||
break
|
|
||||||
if contains(s, peg"'#ERROR_IN' \s* {\S*} \s* {\d+}", matches):
|
|
||||||
result.file = matches[0]
|
|
||||||
result.line = parseInt(matches[1])
|
|
||||||
result.err = true
|
|
||||||
break
|
|
||||||
if contains(s, peg"'#ERROR_MSG' \s* {.*}", matches):
|
|
||||||
result.line = i
|
|
||||||
result.outp = matches[0]
|
|
||||||
result.err = True
|
|
||||||
break
|
|
||||||
if contains(s, peg"'#ERROR' \s* !.", matches):
|
|
||||||
result.line = i
|
|
||||||
result.err = true
|
|
||||||
break
|
|
||||||
|
|
||||||
var
|
var
|
||||||
pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') Error:' \s* {.*}"
|
pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') Error:' \s* {.*}"
|
||||||
pegOtherError = peg"'Error:' \s* {.*}"
|
pegOtherError = peg"'Error:' \s* {.*}"
|
||||||
pegSuccess = peg"'Hint: operation successful'.*"
|
pegSuccess = peg"'Hint: operation successful'.*"
|
||||||
pegOfInterest = pegLineError / pegOtherError / pegSuccess
|
pegOfInterest = pegLineError / pegOtherError / pegSuccess
|
||||||
|
|
||||||
proc callCompiler(filename, options: string): TSpec =
|
proc callCompiler(filename, options: string): TMsg =
|
||||||
var c = parseCmdLine(cmdTemplate % ["filename", filename, "options", options])
|
var c = parseCmdLine(cmdTemplate % [options, filename])
|
||||||
var a: seq[string] = @[] # slicing is not yet implemented :-(
|
var a: seq[string] = @[] # slicing is not yet implemented :-(
|
||||||
for i in 1 .. c.len-1: add(a, c[i])
|
for i in 1 .. c.len-1: add(a, c[i])
|
||||||
var p = startProcess(command=c[0], args=a,
|
var p = startProcess(command=c[0], args=a,
|
||||||
|
|
@ -86,112 +51,122 @@ proc callCompiler(filename, options: string): TSpec =
|
||||||
if x =~ pegOfInterest:
|
if x =~ pegOfInterest:
|
||||||
# `s` should contain the last error message
|
# `s` should contain the last error message
|
||||||
s = x
|
s = x
|
||||||
result.outp = ""
|
result.msg = ""
|
||||||
result.puremsg = ""
|
|
||||||
result.file = ""
|
result.file = ""
|
||||||
result.err = true
|
result.err = true
|
||||||
|
result.line = -1
|
||||||
if s =~ pegLineError:
|
if s =~ pegLineError:
|
||||||
result.file = matches[0]
|
result.file = extractFilename(matches[0])
|
||||||
result.line = parseInt(matches[1])
|
result.line = parseInt(matches[1])
|
||||||
result.outp = s
|
result.msg = matches[2]
|
||||||
result.puremsg = matches[2]
|
|
||||||
elif s =~ pegOtherError:
|
elif s =~ pegOtherError:
|
||||||
result.puremsg = matches[0]
|
result.msg = matches[0]
|
||||||
result.outp = s
|
|
||||||
result.line = 1
|
|
||||||
elif s =~ pegSuccess:
|
elif s =~ pegSuccess:
|
||||||
result.outp = s
|
|
||||||
result.err = false
|
result.err = false
|
||||||
|
|
||||||
proc sameResults(filename: string, spec, comp: TSpec): bool =
|
proc setupCvsParser(cvsFile: string): TCsvParser =
|
||||||
# short filename for messages (better readability):
|
|
||||||
var shortfile = os.extractFilename(filename)
|
|
||||||
|
|
||||||
if comp.err and comp.outp == "":
|
|
||||||
# the compiler did not say "[Error]" nor "operation sucessful"
|
|
||||||
Echo("[Tester] $# -- FAILED; COMPILER BROKEN" % shortfile)
|
|
||||||
elif spec.err != comp.err:
|
|
||||||
Echo(("[Tester] $# -- FAILED\n" &
|
|
||||||
"Compiler says: $#\n" &
|
|
||||||
"But specification says: $#") %
|
|
||||||
[shortfile, comp.outp, spec.outp])
|
|
||||||
elif spec.err:
|
|
||||||
if extractFilename(comp.file) != extractFilename(spec.file):
|
|
||||||
Echo(("[Tester] $# -- FAILED: file names do not match:\n" &
|
|
||||||
"Compiler: $#\nSpec: $#") % [shortfile, comp.file, spec.file])
|
|
||||||
elif strip(spec.outp) notin strip(comp.puremsg):
|
|
||||||
Echo(("[Tester] $# -- FAILED: error messages do not match:\n" &
|
|
||||||
"Compiler: $#\nSpec: $#") % [shortfile, comp.pureMsg, spec.outp])
|
|
||||||
elif comp.line != spec.line:
|
|
||||||
Echo(("[Tester] $# -- FAILED: line numbers do not match:\n" &
|
|
||||||
"Compiler: $#\nSpec: $#") % [shortfile, $comp.line, $spec.line])
|
|
||||||
else:
|
|
||||||
Echo("[Tester] $# -- OK" % shortfile)
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
# we have to run the executable and check its output:
|
|
||||||
var exeFile = changeFileExt(filename, ExeExt)
|
|
||||||
if ExistsFile(exeFile):
|
|
||||||
if len(spec.outp) == 0:
|
|
||||||
# we have no output to validate against, but compilation succeeded,
|
|
||||||
# so it's okay:
|
|
||||||
Echo("[Tester] $# -- OK" % shortfile)
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
var buf = myExec(exeFile)
|
|
||||||
result = strip(buf) == strip(spec.outp)
|
|
||||||
if result:
|
|
||||||
Echo("[Tester] $# -- compiled program OK" % shortfile)
|
|
||||||
else:
|
|
||||||
Echo("[Tester] $# -- compiled program FAILED" % shortfile)
|
|
||||||
else:
|
|
||||||
Echo("[Tester] $# -- FAILED; executable not found" % shortfile)
|
|
||||||
|
|
||||||
proc main(options: string) =
|
|
||||||
# runs the complete testsuite
|
|
||||||
var total = 0
|
|
||||||
var passed = 0
|
|
||||||
for filename in os.walkFiles("tests/t*.nim"):
|
|
||||||
if extractFilename(filename) == "tester.nim": continue
|
|
||||||
var spec = parseTest(filename)
|
|
||||||
var comp = callCompiler(filename, options)
|
|
||||||
if sameResults(filename, spec, comp): inc(passed)
|
|
||||||
inc(total)
|
|
||||||
# ensure that the examples at least compile
|
|
||||||
for filename in os.walkFiles("examples/*.nim"):
|
|
||||||
var comp = callCompiler(filename, options)
|
|
||||||
var shortfile = os.extractFilename(filename)
|
|
||||||
if comp.err:
|
|
||||||
Echo("[Tester] Example '$#' -- FAILED" % shortfile)
|
|
||||||
else:
|
|
||||||
Echo("[Tester] Example $# -- OK" % shortfile)
|
|
||||||
inc(passed)
|
|
||||||
inc(total)
|
|
||||||
Echo("[Tester] $#/$# tests passed\n" % [$passed, $total])
|
|
||||||
|
|
||||||
proc reject(options: string) =
|
|
||||||
## handle all the tests that the compiler should reject
|
|
||||||
const csvFile = "tests/reject/spec.csv"
|
|
||||||
var p: TCsvParser
|
|
||||||
|
|
||||||
var s = newFileStream(csvFile, fmRead)
|
var s = newFileStream(csvFile, fmRead)
|
||||||
if s == nil: quit("cannot open the file" & csvFile)
|
if s == nil: quit("cannot open the file" & csvFile)
|
||||||
p.open(s, csvFile, separator=';', skipInitialSpace=true)
|
result.open(s, csvFile, separator=';', skipInitialSpace=true)
|
||||||
while readRow(p):
|
|
||||||
for val in items(x.row):
|
proc parseRejectData(dir: string): seq[TMsg] =
|
||||||
Echo "##", val, "##"
|
var p = setupCvsParser(dir / "spec.csv")
|
||||||
|
result = @[]
|
||||||
|
while readRow(p, 3):
|
||||||
|
result.add((p.row[0], parseInt(p.row[1]), p.row[2], true))
|
||||||
close(p)
|
close(p)
|
||||||
|
|
||||||
|
proc parseRunData(dir: string): seq[TOutp] =
|
||||||
|
var p = setupCvsParser(dir / "spec.csv")
|
||||||
|
result = @[]
|
||||||
|
while readRow(p, 2):
|
||||||
|
result.add((p.row[0], p.row[1]))
|
||||||
|
close(p)
|
||||||
|
|
||||||
proc accept(options: string) =
|
proc findSpec[T](specs: seq[T], filename: string): int =
|
||||||
nil
|
for s in items(specs):
|
||||||
|
if s.file == filename: return
|
||||||
|
inc(result)
|
||||||
|
quit("cannot find spec for file: " & filename)
|
||||||
|
|
||||||
|
proc initResults: TResults =
|
||||||
|
result.total = 0
|
||||||
|
result.passed = 0
|
||||||
|
result.data = @[]
|
||||||
|
|
||||||
|
proc colorBool(b: bool): string =
|
||||||
|
if b: result = "<span color:\"green\">yes</span>"
|
||||||
|
else: result = "<span color:\"red\">no</span>"
|
||||||
|
|
||||||
|
proc `$`(r: TResults): string =
|
||||||
|
result = "<table><tr><td>Test</td><td>Expected</td>" &
|
||||||
|
"<td>Given</td><td>Success</td></tr>\n"
|
||||||
|
for test, expected, given, success in items(r.data):
|
||||||
|
result.add("<tr><td>$#</td><td>$#</td><td>$#</td><td>$#</td></tr>\n" % [
|
||||||
|
test, expected, given, success.colorBool])
|
||||||
|
result.add("</table>\n")
|
||||||
|
|
||||||
|
proc cmpMsgs(r: var TResults, expected, given: TMsg, test: string) =
|
||||||
|
inc(r.total)
|
||||||
|
if strip(expected.msg) notin strip(given.msg):
|
||||||
|
r.data.add((test, expected.msg, given.msg, false))
|
||||||
|
elif expected.file != given.file:
|
||||||
|
r.data.add((test, expected.file, given.file, false))
|
||||||
|
elif expected.line != given.line:
|
||||||
|
r.data.add((test, $expected.line, $given.line, false))
|
||||||
|
else:
|
||||||
|
r.data.add((test, expected.msg, given.msg, true))
|
||||||
|
inc(r.passed)
|
||||||
|
|
||||||
|
proc reject(r: var TResults, dir, options: string) =
|
||||||
|
## handle all the tests that the compiler should reject
|
||||||
|
var specs = parseRejectData(dir)
|
||||||
|
|
||||||
|
for test in os.walkFiles(dir / "t*.nim"):
|
||||||
|
var t = extractFilename(test)
|
||||||
|
var expected = findSpec(specs, t)
|
||||||
|
var given = callCompiler(test, options)
|
||||||
|
cmpMsgs(r, specs[expected], given, t)
|
||||||
|
|
||||||
|
proc compile(r: var TResults, pattern, options: string) =
|
||||||
|
for test in os.walkFiles(pattern):
|
||||||
|
var t = extractFilename(test)
|
||||||
|
inc(r.total)
|
||||||
|
var given = callCompiler(test, options)
|
||||||
|
r.data.add((t, "", given.msg, not given.err))
|
||||||
|
if not given.err: inc(r.passed)
|
||||||
|
|
||||||
|
proc run(r: var TResults, dir, options: string) =
|
||||||
|
var specs = parseRunData(dir)
|
||||||
|
for test in os.walkFiles(dir / "t*.nim"):
|
||||||
|
var t = extractFilename(test)
|
||||||
|
inc(r.total)
|
||||||
|
var given = callCompiler(test, options)
|
||||||
|
if given.err:
|
||||||
|
r.data.add((t, "", given.msg, not given.err))
|
||||||
|
else:
|
||||||
|
var exeFile = changeFileExt(test, ExeExt)
|
||||||
|
var expected = specs[findSpec(specs, t)]
|
||||||
|
if existsFile(exeFile):
|
||||||
|
var buf = myExec(exeFile)
|
||||||
|
var success = strip(buf) == strip(spec.outp)
|
||||||
|
if success: inc(r.passed)
|
||||||
|
r.data.add((t, spec.outp, buf, success))
|
||||||
|
else:
|
||||||
|
r.data.add((t, spec.outp, "executable not found", false))
|
||||||
|
|
||||||
|
var options = ""
|
||||||
|
var rejectRes = initResults()
|
||||||
|
var compileRes = initResults()
|
||||||
|
var runRes = initResults()
|
||||||
|
|
||||||
var
|
|
||||||
options = ""
|
|
||||||
for i in 1.. paramCount():
|
for i in 1.. paramCount():
|
||||||
add(options, " ")
|
add(options, " ")
|
||||||
add(options, paramStr(i))
|
add(options, paramStr(i))
|
||||||
main(options)
|
|
||||||
|
reject(rejectRes, "tests/reject", options)
|
||||||
|
compile(compileRes, "tests/accept/compile/t*.nim", options)
|
||||||
|
compile(compileRes, "examples/*.nim", options)
|
||||||
|
compile(compileRes, "examples/gtk/*.nim", options)
|
||||||
|
run(runRes, "tests/accept/run", options)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue