Module: deuce-test-suite Synopsis: Test suite for the Deuce editor Author: Hugh Greene, Scott McKay Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // -=- Auxilliary classes and related methods -=- // Editor and frame classes. define sealed class () end class ; define class () end class ; // Window class and some dummy functions, mostly used by commands.dylan. define class () constant slot command-enabled?-table :: = make(
); end class ; define sealed method display-buffer-name (window :: , buffer :: false-or()) => () // Do nothing. end method; define sealed method command-enabled? (window :: , command :: ) => (enabled? :: ) element(window.command-enabled?-table, command, default: #t) end method; define sealed method command-enabled?-setter (enabled? :: , window :: , command :: ) => (enabled? :: ); window.command-enabled?-table[command] := enabled? end method; define variable *testing-editor* :: false-or() = #f; define function run-testing-deuce (#key reset?) when (~*testing-editor* | reset?) *testing-editor* := make(); end; when (~*editor-frame* | reset?) let frame = make(, editor: *testing-editor*); frame-window(frame) := make(, frame: frame); //---*** Globally set these because CAPI back-end never calls 'frame-top-level' *editor-frame* := frame; let buffer = make-empty-buffer(); *buffer* := buffer; end; end function run-testing-deuce; run-testing-deuce(reset?: #t); // Modes. define sealed class () end class; /* We need to do this to test sectionization, but we can't because // these mapping tables aren't exported :-( begin gethash(*keyword->major-mode*, #"testing") := ; gethash(*file-type->major-mode*, #"testing") := end; */ define variable *testing-mode-line* = #f; define method do-note-line-changed (mode :: , line :: ) => (line :: ) *testing-mode-line* := line; end method; define variable *did-resectionize* :: = #f; /* We're defining a method on a class we don't own here: . // Ideally it'd be defined on but we can't associate // any file type with , as mentioned above, so // resectionize-section would never get here. */ define sideways method do-resectionize-section (mode :: , section :: ) => (resectionized? :: ) *did-resectionize* := #t; end method; // Buffers. define sealed class () end class ; // Diagram lines. define sealed class () end class; define sealed class () end class; // -=- Data structures for testing -=- // lines, sections, nodes, container and buffer define variable (*line1-1*, *line1-2*, *line1-3*, *line2-1*, *line3-1*) = values(#f, #f, #f, #f, #f); define variable (*section1*, *section2*, *section3*) = values(#f, #f, #f); define variable (*node1*, *node2*, *node3*) = values(#f, #f, #f); // define variable *testing-buffer* = #f; define variable *container* = #f; define variable *testing-buffer* = #f; // BPs define variable (*start1-1-bp*, *middle1-1-bp*, *end1-1-bp*, *start1-2-bp*, *middle1-3-bp*, *end1-3-bp*, *start2-1-bp*, *middle3-1-bp*, *end3-1-bp*) = values(#f, #f, #f, #f, #f, #f, #f, #f, #f); // intervals define variable (*empty-interval*, *eol-interval*, *line-interval*, *part-lines-interval*, *section-interval*, *part-sections-interval*, *buffer-interval*) = values(#f, #f, #f, #f, #f, #f, #f); define function reset-testing-buffer-state () => () // Create the buffer, and its container. when (*testing-buffer*) kill-buffer(*testing-buffer*); end when; *container* := make(, pathname: "********.text"); // We don't try to read from this pathname! *testing-buffer* := make (, major-mode: find-mode(), container: *container*, name: "*testing*"); push!(container-buffers(*container*), *testing-buffer*); local method make-testing-node (section ::
, start-line :: , end-line :: ) => (node :: ) let node :: = make(, section: section, start-bp: make(, line: start-line, index: 0, buffer: *testing-buffer*), end-bp: make(, line: end-line, index: line-length(end-line), buffer: *testing-buffer*, moving?: #t)); section-nodes(section) := list(node); node end method; // ... 1st node & section, 3 lines *line1-1* := make(, contents: copy-sequence("Line 1")); *line1-2* := make(, contents: copy-sequence("Line 2")); *line1-3* := make(, contents: copy-sequence("Line 3")); *section1* := make(
, start-line: #f, end-line: #f); add-line!(*section1*, *line1-1*); add-line!(*section1*, *line1-2*); add-line!(*section1*, *line1-3*); *node1* := make-testing-node(*section1*, *line1-1*, *line1-3*); // ... 2nd node & section, 1 line *line2-1* := make(, contents: copy-sequence("Line 4")); *section2* := make(
, start-line: #f, end-line: #f); add-line!(*section2*, *line2-1*); *node2* := make-testing-node(*section2*, *line2-1*, *line2-1*); // ... 3rd node & section, 1 line *line3-1* := make(, contents: copy-sequence("Line 5")); *section3* := make(
, start-line: #f, end-line: #f); add-line!(*section3*, *line3-1*); *node3* := make-testing-node(*section3*, *line3-1*, *line3-1*); // ... put the sections into the container add-section!(*container*, *section1*); add-section!(*container*, *section2*); add-section!(*container*, *section3*); // ... put the nodes into the buffer add-node!(*testing-buffer*, *node1*); add-node!(*testing-buffer*, *node2*); add-node!(*testing-buffer*, *node3*); // ... some BPs *start1-1-bp* := line-start(*line1-1*); *middle1-1-bp* := make-bp(*line1-1*, 1); *end1-1-bp* := line-end(*line1-1*); *start1-2-bp* := line-start(*line1-2*); *middle1-3-bp* := make-bp(*line1-3*, 2); *end1-3-bp* := line-end(*line1-3*); *start2-1-bp* := line-start(*line2-1*); *middle3-1-bp* := make-bp(*line3-1*, 3); *end3-1-bp* := line-end(*line3-1*); // ... some intervals dynamic-bind(*buffer* = *testing-buffer*) // We need the dynamic-bind, or buffer-bp will be wrong, and so // order-bps, called within make on , will get the // wrong answer (but not fail). *empty-interval* := make-interval(*start1-1-bp*, *start1-1-bp*); *eol-interval* := make-interval(*end1-1-bp*, *start1-2-bp*); *line-interval* := make-interval(*start1-1-bp*, *end1-1-bp*); *part-lines-interval* := make-interval(*middle1-1-bp*, *middle1-3-bp*); *section-interval* := make-interval(*start1-1-bp*, *end1-3-bp*); *part-sections-interval* := make-interval(*middle1-3-bp*, *middle3-1-bp*); *buffer-interval* := make-interval(*start1-1-bp*, *end3-1-bp*); end; end function; begin reset-testing-buffer-state(); end; // -=- Dynamic access to these module variables -=- // The values of these change with each call to // reset-testing-buffer-state, so we have to evaluate them dynamically // to get the right value. define function testing-lines () => (lines :: /*of: */) vector(*line1-1*, *line1-2*, *line1-3*, *line2-1*, *line3-1*) end function; define function testing-intervals () => (intervals :: /*of: */) vector (*empty-interval*, *eol-interval*, *line-interval*, *part-lines-interval*, *section-interval*, *part-sections-interval*, *buffer-interval*) end function; // -=- Common test functions -=- define function last-line-from (line :: , #key in-buffer? :: = #f, verbose? :: = #f) => (last-line :: false-or()) // Follow the line-next chain from line, until we hit #f, // returning the last line we saw. Also check that // previous(next(line)) == line (when next(line) ~== #f), i.e. that // next and previous pointers match up. let (next, previous) = if (in-buffer?) values(rcurry(line-next-in-buffer, *buffer*), rcurry(line-previous-in-buffer, *buffer*)) else values(line-next, line-previous) end; block(return) for (this = line then next(this), until: ~next(this)) when (verbose?) *format-function*("this = %=", this); end; unless(previous(next(this)) == this) when (verbose?) *format-function* ("this.next = %=, this.next.previous = %=", this.next, this.next.previous); end when; return(#f); end; finally this end for end end function; define function check-buffer-bps (buffer :: , #key verbose? :: = #f) => (okay? :: ) block(return) // Check that the node-{start,end}-bps are indeed at the start/end // of lines which have no line-{previous,next}. for (node = buffer-start-node(buffer) then node-next(node), until: ~node) when (verbose?) *format-function*("node: %=", node); end when; let start-bp = interval-start-bp(node); let start-line = bp-line(start-bp); let start-index = bp-index(start-bp); let end-bp = interval-end-bp(node); let end-line = bp-line(end-bp); let end-index = bp-index(end-bp); unless ( ~line-previous(start-line) & start-index == 0 & ~line-next(end-line) & end-index == line-length(end-line) & ~moving-bp?(start-bp) & moving-bp?(end-bp) & ~simple-bp?(start-bp) & ~simple-bp?(end-bp)) when (verbose?) ~line-previous(start-line) | *format-function*("start line has a previous line\n"); start-index == 0 | *format-function*("start index not == 0\n"); ~line-next(end-line) | *format-function*("end line has a next line\n"); end-index == line-length(end-line) | *format-function* ("end index %= not == line length %=", end-index, line-length(end-line)); ~moving-bp?(start-bp) | *format-function*("start BP is a \n"); moving-bp?(end-bp) | *format-function*("end BP is not a \n"); ~simple-bp?(start-bp) | *format-function*("start BP is not a \n"); ~simple-bp?(end-bp) | *format-function*("end BP is not a \n"); end when; return(#f); end unless; end for; // Check that all line-bps are moving-bps, that they're on the // right line and that their indices are in range for the line // they're on. do-lines (method (line, si, ei, last?) ignore(si, ei, last?); for (bp in line-bps(line)) let _line = bp-line(bp); let _index = bp-index(bp); unless (moving-bp?(bp) & _line == line & 0 <= _index & _index <= line-length(line)) when (verbose?) moving-bp?(bp) | *format-function*("line BP is not a \n"); _line == line | *format-function*("line BP is on wrong line\n"); 0 <= _index | *format-function*("line BP has index < 0\n"); _index <= line-length(line) | *format-function*("line BP has index > line length\n"); end when; return(#f); end unless; end for; end method, buffer); // If we got this far, everything's okay. #t end; end function;