-- major tests for drawings -- We minimize assumptions about specific pixels, and try to test at the level -- of specific shapes. In particular, no tests of freehand drawings. function test_creating_drawing_saves() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.filename = 'foo' Editor_state.lines = load_array{} Text.redraw_all(Editor_state) edit.draw(Editor_state) -- click on button to create drawing edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1) -- file not immediately saved edit.update(Editor_state, 0.01) check_nil(App.filesystem['foo'], 'early') -- wait until save Current_time = Current_time + 3.1 edit.update(Editor_state, 0) -- filesystem contains drawing and an empty line of text check_eq(App.filesystem['foo'], '```lines\n```\n\n', 'check') end function test_draw_line() -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.filename = 'foo' Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'line' edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw a line edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 1, '#shapes') check_eq(#drawing.points, 2, '#points') check_eq(drawing.shapes[1].mode, 'line', 'shape:1') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'p1:x') check_eq(p1.y, 6, 'p1:y') check_eq(p2.x, 35, 'p2:x') check_eq(p2.y, 36, 'p2:y') -- wait until save Current_time = Current_time + 3.1 edit.update(Editor_state, 0) -- The format on disk isn't perfectly stable. Table fields can be reordered. -- So just reload from disk to verify. load_from_disk(Editor_state) Text.redraw_all(Editor_state) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 1, 'save/#shapes') check_eq(#drawing.points, 2, 'save/#points') check_eq(drawing.shapes[1].mode, 'line', 'save/shape:1') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'save/p1:x') check_eq(p1.y, 6, 'save/p1:y') check_eq(p2.x, 35, 'save/p2:x') check_eq(p2.y, 36, 'save/p2:y') end function test_draw_horizontal_line() -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'manhattan' edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw a line that is more horizontal than vertical edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+26, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 1, '#shapes') check_eq(#drawing.points, 2, '#points') check_eq(drawing.shapes[1].mode, 'manhattan', 'shape_mode') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'p1:x') check_eq(p1.y, 6, 'p1:y') check_eq(p2.x, 35, 'p2:x') check_eq(p2.y, p1.y, 'p2:y') end function test_draw_circle() -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'line' edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw a circle App.mouse_move(Editor_state.left+4, Editor_state.top+Drawing_padding_top+4) -- hover on drawing edit.run_after_keychord(Editor_state, 'C-o') edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawin
#
#
#           The Nim Compiler
#        (c) Copyright 2017 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## The compiler can generate debuginfo to help debuggers in translating back
## from C/C++/JS code to Nim. The data structure has been designed to produce
## something useful with Nim's marshal module.

import sighashes

type
  FilenameMapping* = object
    package*, file*: string
    mangled*: SigHash
  EnumDesc* = object
    size*: int
    owner*: SigHash
    id*: int
    name*: string
    values*: seq[(string, int)]
  DebugInfo* = object
    version*: int
    files*: seq[FilenameMapping]
    enums*: seq[EnumDesc]
    conflicts*: bool

proc sdbmHash(package, file: string): SigHash =
  result = 0
  for i in 0..<package.len:
    result &= package[i]
  result &= '.'
  for i in 0..<file.len:
    result &= file[i]

proc register*(self: var DebugInfo; package, file: string): SigHash =
  result = sdbmHash(package, file)
  for f in self.files:
    if f.mangled == result:
      if f.package == package and f.file == file: return
      self.conflicts = true
      break
  self.files.add(FilenameMapping(package: package, file: file, mangled: result))

proc hasEnum*(self: DebugInfo; ename: string; id: int; owner: SigHash): bool =
  for en in self.enums:
    if en.owner == owner and en.name == ename and en.id == id: return true

proc registerEnum*(self: var DebugInfo; ed: EnumDesc) =
  self.enums.add ed

proc init*(self: var DebugInfo) =
  self.version = 1
  self.files = @[]
  self.enums = @[]

var gDebugInfo*: DebugInfo
debuginfo.init gDebugInfo

import marshal, streams

proc writeDebugInfo*(self: var DebugInfo; file: string) =
  let s = newFileStream(file, fmWrite)
  store(s, self)
  s.close

proc writeDebugInfo*(file: string) = writeDebugInfo(gDebugInfo, file)

proc loadDebugInfo*(self: var DebugInfo; file: string) =
  let s = newFileStream(file, fmRead)
  load(s, self)
  s.close

proc loadDebugInfo*(file: string) = loadDebugInfo(gDebugInfo, file)
tor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1) edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 2, 'baseline/#shapes') check_eq(drawing.shapes[1].mode, 'line', 'baseline/shape:1') check_eq(drawing.shapes[2].mode, 'line', 'baseline/shape:2') -- hover on one of the lines and delete App.mouse_move(Editor_state.left+25, Editor_state.top+Drawing_padding_top+26) edit.run_after_keychord(Editor_state, 'C-d') -- only that line is deleted check_eq(drawing.shapes[1].mode, 'deleted', 'shape:1') check_eq(drawing.shapes[2].mode, 'line', 'shape:2') end function test_delete_point_from_polygon() -- create a drawing with two lines connected at a point App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'line' edit.draw(Editor_state) -- first point edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1) edit.run_after_text_input(Editor_state, 'g') -- polygon mode -- second point App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36) edit.run_after_text_input(Editor_state, 'p') -- add point -- third point App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+26) edit.run_after_text_input(Editor_state, 'p') -- add point -- fourth point edit.run_after_mouse_release(Editor_state, Editor_state.left+14, Editor_state.top+Drawing_padding_top+16, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 1, 'baseline/#shapes') check_eq(drawing.shapes[1].mode, 'polygon', 'baseline/mode') check_eq(#drawing.shapes[1].vertices, 4, 'baseline/vertices') -- hover on a point and delete App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+26) edit.run_after_keychord(Editor_state, 'C-d') -- just the one point is deleted check_eq(drawing.shapes[1].mode, 'polygon', 'shape') check_eq(#drawing.shapes[1].vertices, 3, 'vertices') end function test_delete_point_from_polygon() -- create a drawing with two lines connected at a point App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'line' edit.draw(Editor_state) -- first point edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1) edit.run_after_text_input(Editor_state, 'g') -- polygon mode -- second point App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36) edit.run_after_text_input(Editor_state, 'p') -- add point -- third point edit.run_after_mouse_release(Editor_state, Editor_state.left+14, Editor_state.top+Drawing_padding_top+16, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 1, 'baseline/#shapes') check_eq(drawing.shapes[1].mode, 'polygon', 'baseline/mode') check_eq(#drawing.shapes[1].vertices, 3, 'baseline/vertices') -- hover on a point and delete App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36) edit.run_after_keychord(Editor_state, 'C-d') -- there's < 3 points left, so the whole polygon is deleted check_eq(drawing.shapes[1].mode, 'deleted', 'check') end function test_undo_name_point() -- create a drawing with a line App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.filename = 'foo' Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'line' edit.draw(Editor_state) -- draw a line edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 1, 'baseline/#shapes') check_eq(#drawing.points, 2, 'baseline/#points') check_eq(drawing.shapes[1].mode, 'line', 'baseline/shape:1') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'baseline/p1:x') check_eq(p1.y, 6, 'baseline/p1:y') check_eq(p2.x, 35, 'baseline/p2:x') check_eq(p2.y, 36, 'baseline/p2:y') check_nil(p2.name, 'baseline/p2:name') check_eq(#Editor_state.history, 1, 'baseline/history:1') --? print('a', Editor_state.lines.current_drawing) -- enter 'name' mode without moving the mouse edit.run_after_keychord(Editor_state, 'C-n') edit.run_after_text_input(Editor_state, 'A') edit.run_after_keychord(Editor_state, 'return') check_eq(p2.name, 'A', 'baseline') check_eq(#Editor_state.history, 3, 'baseline/history:2') check_eq(Editor_state.next_history, 4, 'baseline/next_history') --? print('b', Editor_state.lines.current_drawing) -- undo edit.run_after_keychord(Editor_state, 'C-z') local drawing = Editor_state.lines[1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(Editor_state.next_history, 3, 'next_history') check_eq(p2.name, '', 'undo') -- not quite what it was before, but close enough -- wait until save Current_time = Current_time + 3.1 edit.update(Editor_state, 0) -- undo is saved load_from_disk(Editor_state) Text.redraw_all(Editor_state) local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2] check_eq(p2.name, '', 'save') end function test_undo_move_point() -- create a drawing with a line App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.filename = 'foo' Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'line' edit.draw(Editor_state) edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 1, 'baseline/#shapes') check_eq(#drawing.points, 2, 'baseline/#points') check_eq(drawing.shapes[1].mode, 'line', 'baseline/shape:1') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'baseline/p1:x') check_eq(p1.y, 6, 'baseline/p1:y') check_eq(p2.x, 35, 'baseline/p2:x') check_eq(p2.y, 36, 'baseline/p2:y') check_nil(p2.name, 'baseline/p2:name') -- move p2 edit.run_after_keychord(Editor_state, 'C-u') App.mouse_move(Editor_state.left+26, Editor_state.top+Drawing_padding_top+44) edit.update(Editor_state, 0.05) local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p2.x, 26, 'x') check_eq(p2.y, 44, 'y') -- exit 'move' mode edit.run_after_mouse_click(Editor_state, Editor_state.left+26, Editor_state.top+Drawing_padding_top+44, 1) check_eq(Editor_state.next_history, 4, 'next_history') -- undo edit.run_after_keychord(Editor_state, 'C-z') edit.run_after_keychord(Editor_state, 'C-z') -- bug: need to undo twice local drawing = Editor_state.lines[1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(Editor_state.next_history, 2, 'next_history') check_eq(p2.x, 35, 'x') check_eq(p2.y, 36, 'y') -- wait until save Current_time = Current_time + 3.1 edit.update(Editor_state, 0) -- undo is saved load_from_disk(Editor_state) Text.redraw_all(Editor_state) local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2] check_eq(p2.x, 35, 'save/x') check_eq(p2.y, 36, 'save/y') end function test_undo_delete_point() -- create a drawing with two lines connected at a point App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels Editor_state = edit.initialize_test_state() Editor_state.filename = 'foo' Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) Editor_state.current_drawing_mode = 'line' edit.draw(Editor_state) edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1) edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1) local drawing = Editor_state.lines[1] check_eq(#drawing.shapes, 2, 'baseline/#shapes') check_eq(drawing.shapes[1].mode, 'line', 'baseline/shape:1') check_eq(drawing.shapes[2].mode, 'line', 'baseline/shape:2') -- hover on the common point and delete App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+36) edit.run_after_keychord(Editor_state, 'C-d') check_eq(drawing.shapes[1].mode, 'deleted', 'shape:1') check_eq(drawing.shapes[2].mode, 'deleted', 'shape:2') -- undo edit.run_after_keychord(Editor_state, 'C-z') local drawing = Editor_state.lines[1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(Editor_state.next_history, 3, 'next_history') check_eq(drawing.shapes[1].mode, 'line', 'shape:1') check_eq(drawing.shapes[2].mode, 'line', 'shape:2') -- wait until save Current_time = Current_time + 3.1 edit.update(Editor_state, 0) -- undo is saved load_from_disk(Editor_state) Text.redraw_all(Editor_state) check_eq(#Editor_state.lines[1].shapes, 2, 'save') end