From: [EMAIL PROTECTED]
Date: June 14, 2007 10:46:56 AM PDT
To: [EMAIL PROTECTED]
Subject: [Laszlo-checkins] r5407 - in openlaszlo/branches/legals/
docs/src/developers: . programs
Reply-To: [email protected]
Author: frisco
Date: 2007-06-14 10:46:50 -0700 (Thu, 14 Jun 2007)
New Revision: 5407
Added:
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-1.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-10.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-2.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-3.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-4.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-5.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-6.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-7.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-8.lzx
openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-9.lzx
Modified:
openlaszlo/branches/legals/docs/src/developers/lzunit.dbk
Log:
Change 20070614-laszlosystems-W by [EMAIL PROTECTED]
on 2007-06-14 10:41:08 PDT
in /Users/laszlosystems/src/svn/openlaszlo/branches/legals
for http://svn.openlaszlo.org/openlaszlo/branches/legals
Summary: Section on test-driven development added to LzUnit
devguide chapter, plus some tiny edits to the old stuff
New Features:
Bugs Fixed in Perpetuity: LPP-314
Technical Reviewer: (pending)
QA Reviewer: (pending)
Doc Reviewer: (pending)
Documentation:
Release Notes:
Details:
Tests:
Modified: openlaszlo/branches/legals/docs/src/developers/lzunit.dbk
===================================================================
--- openlaszlo/branches/legals/docs/src/developers/lzunit.dbk
2007-06-14 06:44:54 UTC (rev 5406)
+++ openlaszlo/branches/legals/docs/src/developers/lzunit.dbk
2007-06-14 17:46:50 UTC (rev 5407)
@@ -2,26 +2,20 @@
<title>Unit Testing</title>
-<para><indexterm><primary>LzUnit</primary></
indexterm><classname>LzUnit</classname> is OpenLaszlo's
implementation of the xUnit testing framework. LzUnit enables
automated unit-testing of Laszlo
-applications and libraries.</para>
+<para><indexterm><primary>LzUnit</primary></
indexterm><classname>LzUnit</classname> is OpenLaszlo's
implementation of the xUnit testing framework, enabling automated
unit testing of OpenLaszlo applications and libraries.</para>
-<para/><section id="lzunit.overview"><title>Overview</title>
-
<para>The functionality provided by the LzUnit framework is
essentially comprised of two public classes —
<indexterm><primary>TestCase</primary></
indexterm><classname>TestCase</classname> and
<indexterm><primary>TestSuite</primary></
indexterm><classname>TestSuite</classname>. Each
<indexterm><primary>TestSuite</primary></
indexterm><classname>TestSuite</classname>
contains one or more children that are instances of
<indexterm><primary>TestCase</primary></
indexterm><classname>TestCase</classname>. An LZX program that
- consists of a <indexterm><primary>TestSuite</primary></
indexterm><sgmltag class="element"><TestSuite></
sgmltag><remark role="fixme">[unknown tag]</remark>
-<!--unknown tag: TestSuite-->
- will, when loaded, automatically run all of its child
<indexterm><primary>TestCase</primary></
indexterm><classname>TestCase</classname>s and report the number
of test cases run, the number of failures, and the number of
runtime errors. If any
- failures occur, an obvious error message is presented.</para>
+ includes a <indexterm><primary>TestSuite</primary></indexterm>
+ will run all of its child <indexterm><primary>TestCase</
primary></indexterm><classname>TestCase</classname>s, then report
the number of test cases run, the number of failures (plus error
messages), and the number of runtime errors.</para>
<para/></section><section><title>Including the lzunit component</
title>
<para>
The unit testing code is not a part of the OpenLaszlo Runtime
Library; you must explicitly include it using <include
href="lzunit"/>.
The <indexterm><primary>lzunit</primary></indexterm><sgmltag
class="element"><lzunit></sgmltag><remark role="fixme">
[unknown tag]</remark>
<!--unknown tag: lzunit-->
library has a <debug
- y="500"/> in it. If you would like to see the debugger
elsewhere (as in the examples below), assign it a different "y"
value. Put the <debug> before the include statement. (The
compiler ignores all but the first occurence when it
- sees two debug tags.)
+ y="500"/> in it; if you would like to see the debugger
elsewhere (as in the examples below), assign it a different "y"
value. Put the <debug> before the include statement -- the
compiler ignores all but the first occurrence of <debug>.
</para>
<para/></section><section id="lzunit.TestCases"><title>Writing
test cases</title>
@@ -442,9 +436,552 @@
</programlisting><?lzx-edit programs/lzunit-$5.lzx></example?>
<para>
Of course, running both animators simultaneously will superimpose
the effect of each on the other and the tests will fail; this is
true of any simultaneous animators applied to the same attribute
of the same object.
-</para>
+</para></section>
-
+<section id="lzunit.testdriven"><title>An Introduction to Test-
Driven Development in OpenLaszlo</title>
+<para>In the late '80s, the Talking Moose on my Macintosh SE
recited at startup the "waterfall" development model taught in a
first-year computer science class: "Problem statement. Analysis.
Algorithm. Implementation. Testing." From that model, I learned
the frustration of perpetual coding and debugging, and returned to
journalism school for good.</para>
-<para/></section></chapter>
+<para>Somehow I ended up at a software company as a tester (which
I still think is odder than fiction). I often wonder when my lack
of skill and experience will catch up with me, but a co-worker
suggested some reading that changed my thinking, helped me become
a more useful colleague, and even served to get me out of bed
earlier in the morning: <emphasis role="i">Test-Driven Development
by Example</emphasis> by Kent Beck, in which Beck teaches this
development cycle:</para>
+
+<itemizedlist spacing="compact"><listitem><para> Write a failing
automated test before writing any code</para></listitem>
+<listitem><para>Pass the test by any means necessary</para></
listitem>
+<listitem><para>Remove duplication</para></listitem></itemizedlist>
+
+<section id="lzunit.think"><title>Think Like a Grandmaster</title>
+
+<para>According to Beck, test-driven development gives a
programmer courage. When the end of a difficult programming task
is nowhere in sight, bringing one failing or "red" test to "green"
signifies one concrete step forward.</para>
+
+<para>Test-driven development demands thoughtful design. In the
"waterfall" model, testing is the last phase, so programmers
stumble through the implementation phase, not certain that the
code will fulfill the requirements <emphasis role="i">because it
is untested</emphasis>. In test-first development, the programmer
must be accurate and specific about what the code is meant to
accomplish, and design a test for that before going further.</para>
+
+<para>The strongest chessplayers play their best moves at the end
of the game. The players who study the opening find that they
drift into fearful territory, while the players proficient at
endgames grow in confidence. Those endgame-savvy chessplayers are
like "test-infected" developers who worked on the <emphasis
role="i">last phase first</emphasis>: they always know where
they're headed.</para>
+
+<para>A similar analogy: A well-trained chess student should be
coached to play moves that are foolish at the start -- as practice
for difficult situations in the future. Beck instructs that at the
start of the TDD cycle to write a test that <emphasis
role="i">fails</emphasis>. Red in the opening, green in the
endgame.</para></section>
+
+<section id="lzunit.doc"><title>Test-Driven Documentation</title>
+
+<para>The computer science instructors who stress the "Big Design
Up Front" methodology would probably require that I understand TDD
at a professional level before I write this. The test-driven
methodology, on the other hand, says that if each code example
takes a tiny step while adhering to test-driven principles, I can
write with confidence.</para>
+
+<para>The aim of this work is fourfold:</para>
+
+<itemizedlist spacing="compact"><listitem><para>To demonstrate
test-driven development through the construction of simple
OpenLaszlo applications;</para></listitem>
+<listitem><para>To build upon the OpenLaszlo developers' guide
section about LzUnit, the XUnit framework for OpenLaszlo;</para></
listitem>
+<listitem><para>To complete the LzUnit-related documentation
tasks assigned to me in the OpenLaszlo bug reporting database, so
I can think of this as actual work;</para></listitem>
+<listitem><para>To learn.</para></listitem></itemizedlist></section>
+
+<section id="lzunit.tiny"><title>Teeny Tiny Steps</title>
+
+<para>Many folks seem to be certain that a writer's life is
mystical and arcane, but Beck, a software engineer, understands it
completely. I knew Beck was speaking my language when he wrote in
<emphasis role="i">JUnit Pocket Guide</emphasis>: "Writers write.
Testers test." In <emphasis role="i">Test-Driven Development</
emphasis>, Beck cut through the knot that has buried my
programming education since the beginning. "Take teeny tiny
steps," he said.</para>
+
+<para>By taking the smallest steps possible in program
development, it is easier to step backward if necessary.
Experienced developers, said Beck, benefit from taking small steps
because they can always increase their size, but if they began
with large steps, they wouldn't know if smaller steps were
appropriate.</para>
+
+<para>The smallest step possible in OpenLaszlo is initializing
the <literal>canvas</literal>, the <literal>view</literal> at the
foundation of every OpenLaszlo application. Many programming
tutorials start by demonstrating a stub application that compiles
and runs successfully, but doesn't actually do anything. In
OpenLaszlo, that would be:</para>
+
+<example role="live-example"><title>Canvas</title><programlisting>
+<canvas/>
+</programlisting></example>
+
+<para>However, that stub is too simple to break, so it can't be a
useful example in the test-driven development model.</para>
+
+<itemizedlist spacing="compact"><listitem><para>Red -- Write a
test that doesn't compile (the LzUnit console runs red);</para></
listitem>
+<listitem><para>Green -- Make the test green by any means, no
matter how inelegant or distasteful (Beck recommends faking it, if
necessary);</para></listitem>
+<listitem><para>Refactor -- Bring the test to respectability by
removing duplication.</para></listitem></itemizedlist>
+
+<para>I tried a beginning programming problem in a test-driven
manner: <emphasis role="i">1. Display series of numbers in an
infinite loop. The program should quit if someone hits a specific
key.</emphasis></para>
+
+<para>We need two things: a while loop to display numbers in the
debugger, and a button to terminate the loop. If we make the
infinite loop first, we'll have to pull the plug to stop it, so we
should make the button first.</para>
+
+<example role="live-example"><title>Go button</
title><programlisting>
+<can
+</programlisting></example>
+
+<para>Stop!</para>
+
+<para><emphasis role="b">Before writing any code, write a failing
test.</emphasis></para></section>
+
+<section id="lzunit.intro"><title>Introducing LzUnit</title>
+
+<para>XUnit is a testing framework that Beck created in 1994,
which evolved first into SUnit for Smalltalk. He and Erich Gamma
(author of <emphasis role="i">Design Patterns</emphasis>) modified
XUnit for Java while sharing a flight to a developers' conference
three years later. JUnit became the best known of the XUnit
family; there are XUnit frameworks for C++, C#, Python, Fortran,
Perl, Visual Basic, and others, including OpenLaszlo.</para>
+
+<para>The lightweight XUnit contains three classes and 12
methods. "Never in the field of software development was so much
owed by so many to so few lines of code," said object-oriented
design authority Martin Fowler. </para></section>
+
+<section id="lzunit.count"><title>Counting Infinitely</title>
+
+<para>In test-driven development, we devise a successful test
case first, and then we fail it (because we wrote the test first).
We want the first button click to change "stop" to "go", and the
second click to "stop". The easiest solution, I think, is to give
the button a "go" attribute which is a boolean, where its initial
state is "false".</para>
+
+<para>Sometimes I think I most often revisit the OpenLaszlo
developers' guide <xref linkend="methods-events-
attributes">methods, events, and attributes</xref>. This attribute
is a simple one, though: we'll instantiate an instance of the
button class, and assign it an attribute named "go", of the type
boolean, with two values: "go" or "stop" according to the boolean
expression "true" or "false".</para>
+
+<para>The first test is to check for the button's initial state.
I am a lazy bum; every programming task I do starts with this
LzUnit test template (I even use the antiquated trick of putting a
space at the start of its filename so it shows up at the top of
the Open... dialog):</para>
+
+<example role="live-example"><title>Test stub</
title><programlisting>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<TestSuite>
+ <TestCase>
+ <method name="test">
+
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting></example>
+
+<para>This is an empty canvas (it will compile and run, resulting
in a blank canvas plus the debugger window and the LzUnit output
console; the <literal>simplelayout</literal> tag separates the
LzUnit console from the visual objects). The script's inclusion of
the LzUnit directory enables us to create instances of the
<literal>TestSuite</literal> class, which binds any number of
instances of <literal>TestCase</literal>. TestCases include the
<literal>Assert</literal> classes, which are the basis for unit
testing in the XUnit framework. The TestCase method that makes the
assertions must have "test" at the start of its name, so I make
that part of the template.</para>
+
+<example role="live-example">
+ <title>testGoButtonTrue</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-1.lzx"/></
textobject>
+ </programlisting>
+</example>
+
+<?example role="live-example"><title>testGoButtonTrue</
title><programlisting role="lzx-
embednew"><filename>testdriven-1.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-1.lzx></example?>
+
+<para>When we compile and run that, it's red, of course, because
there's no code to test, but the test-driven cycle of red-green-
remove_duplication becomes an addiction; the programmer's
confidence and courage is bolstered every time each small step
goes from red to green.</para>
+
+<para>The TestCase method name must be descriptive. If a test
applies to a numbered bug in the Laszlo bug database, I'll name
the file LPP-nnnn accordingly, but the TestCase method should
always describe the basis of the test.</para>
+
+<para><literal>AssertTrue(goButton.go)</literal> asks, "Is the
'go' attribute of the 'goButton' instance true?". We could also
<literal>assertEquals(true, goButton.go)</literal>, which asks the
same question, but here I want to stress the boolean nature of
goButton.go.</para>
+
+<para>The button code:</para>
+
+
+<example role="live-example">
+ <title>testGoButtonTrue</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-2.lzx"/></
textobject>
+ </programlisting>
+</example>
+
+<?example role="live-example"><title>testGoButtonTrue</
title><programlisting role="lzx-
embednew"><filename>testdriven-2.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<button name="goButton" width="100" text="Go">
+
+ <attribute name="go" type="boolean" value="true"/>
+
+</button>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-2.lzx></example?>
+
+<para>When this compiles, the test runs green, and that is an
accomplishment.</para>
+
+<para>Our button is set to "go". Now we need an <literal>onclick</
literal> method for "stop" -- that is, to set the "go" attribute
to false. The LzUnit testing framework doesn't enable us to test
the mouseclick; <emphasis role="i">integration testing</emphasis>
tests functionality, which comes after the unit testing phase.</para>
+
+<para>On a whim, I thought to enable the button to stop and start
the loop. Without an LzUnit option to test the button's function,
I tested the button method in the debugger window:</para>
+
+<example role="live-example">
+ <title>testGoButtonTrueFalse</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-3.lzx"/></
textobject>
+ </programlisting>
+</example>
+
+<?example role="live-example"><title>testGoButtonTrueFalse</
title><programlisting role="lzx-
embednew"><filename>testdriven-3.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<button name="goButton" width="100" text="Go">
+
+<attribute name="go" type="boolean" value="true"/>
+
+<method event="onclick">
+ if (goButton.go==true) {
+ this.setAttribute('text', 'Stop');
+ this.setAttribute('go', false);
+ Debug.write(this.getAttribute('go'), "should be 'false'");
+ }
+ else {
+ this.setAttribute('text', 'Go');
+ this.setAttribute('go', true);
+ Debug.write(this.getAttribute('go'), "should be 'true'");
+ }
+</method>
+
+</button>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-3.lzx></example?>
+
+<para>A while statement attached to (goButton.go==true) would
loop infinitely, or until a buttonclick set the "go" attribute to
false, but how to test for infinity? Maybe it's something the
developer has to take on faith, but JavaScript does have its
limit: <literal>Number.MAX_VALUE</literal> is the largest number
JavaScript can represent. The while statement is <literal>while
(goButton.counter < Number.MAX_VALUE)</literal>.</para>
+
+<para>I am not sure if it is good style to declare "counter" as a
button attribute, but I think that must be better than
initializing the counter variable on the canvas (with the script
<literal><method event="oninit">var goButton.counter=1;</
method></literal>). Before adding the code for the while loop
and the code for the button attribute, there's a test to write:
Does the goButton have a attribute "displayed" that equals 1?</para>
+
+<example role="live-example">
+ <title>testGoButtonCounter</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-4.lzx"/></
textobject>
+ </programlisting>
+</example>
+
+<?example role="live-example"><title>testGoButtonCounter</
title><programlisting role="lzx-
embednew"><filename>testdriven-4.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<button name="goButton" width="100" text="Go">
+
+<attribute name="go" type="boolean" value="true"/>
+
+<method event="onclick">
+ if (goButton.go==true) {
+ this.setAttribute('text', 'Stop');
+ this.setAttribute('go', false);
+ }
+ else {
+ this.setAttribute('text', 'Go');
+ this.setAttribute('go', true);
+ }
+</method>
+
+</button>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertEquals(1, goButton.counter);
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-4.lzx></example?>
+
+<para>The test runs red because the counter attribute hasn't been
written. (Also, the button's test for true/false was removed
because it served its purpose).</para>
+
+<example role="live-example">
+ <title>testGoButtonCounter</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-5.lzx"/></
textobject>
+ </programlisting>
+</example>
+<?example role="live-example"><title>testGoButtonTrueFalse</
title><programlisting role="lzx-
embednew"><filename>testdriven-5.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<button name="goButton" width="100" text="Go">
+
+<attribute name="go" type="boolean" value="true"/>
+<attribute name="counter" type="number" value="1" when="once"/
>
+
+<method event="onclick">
+ if (goButton.go==true) {
+ this.setAttribute('text', 'Stop');
+ this.setAttribute('go', false);
+ }
+ else {
+ this.setAttribute('text', 'Go');
+ this.setAttribute('go', true);
+ }
+</method>
+
+</button>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertEquals(1, goButton.counter);
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-5.lzx></example?>
+
+<para>That runs green. Here's the while loop:</para>
+
+<example role="live-example">
+ <title>testGoButtonWhile</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-6.lzx"/></
textobject>
+ </programlisting>
+</example>
+<?example role="live-example"><title>testGoButtonTrueFalse</
title><programlisting role="lzx-
embednew"><filename>testdriven-6.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<button name="goButton" width="100" text="Go">
+
+<attribute name="go" type="boolean" value="true"/>
+<attribute name="counter" type="number" value="1" when="once"/
>
+
+<method event="onclick">
+ if (goButton.go==true) {
+ this.setAttribute('text', 'Stop');
+ this.setAttribute('go', false);
+ while (goButton.counter<Number.MAX_VALUE) {
+ Debug.write(goButton.counter);
+ counter=counter+1;
+ }
+ }
+ else {
+ this.setAttribute('text', 'Go');
+ this.setAttribute('go', true);
+ }
+</method>
+
+</button>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertEquals(1, goButton.counter);
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-6.lzx></example?>
+
+<para>One of the maxims of test-driven development is "do the
simplest thing that works". This LZX script satisfies the
programming problem "display numbers infinitely", but the compiled
application does not work! An infinitely looping
<literal>Debug.write</literal> statement fills up memory, and
results in the browser choking. We need the script to Debug.write,
pause for breath, Debug.write, pause for breath, and so on. The
answer is in the global object LzIdle, which I have never used.
The problem now presents a challenge to learn something new about
the language!</para>
+
+<para>How do you test for the idle state? Doesn't the testing
itself mean the universe isn't idle?</para>
+
+<para>Writing the test first, my best guess is that we're
checking for <literal>this.idle</literal>:</para>
+
+<example role="live-example">
+ <title>testIdle</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-7.lzx"/></
textobject>
+ </programlisting>
+</example>
+<?example role="live-example"><title>testGoButtonTrueFalse</
title><programlisting role="lzx-
embednew"><filename>testdriven-7.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<TestSuite>
+ <TestCase>
+ <method name="testIdle">
+ assertTrue(this.idle);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-7.lzx></example?>
+
+<para>We expect a newly-written test to fail, but in this
instance, I don't know if it'll ever pass, or how to make it pass
in the red-green-refactor cycle.</para>
+
+<para>I copied some code out the developers' guide <xref
linkend="layout-and-design">Layout and Design</xref> from the
example <literal>Building a 'floating view'</literal>:</para>
+
+<example role="live-example"><title>startDraggingFloater method</
title><programlisting>
+<method name="startDraggingFloater">
+ this.d = new LzDelegate(this, "adjustFloaterPosition", LzIdle,
"onidle");
+ this.gm = new LzDelegate(this, "cancelFloater", LzGlobalMouse,
"onmouseup");
+</method>
+
+<method name="adjustFloaterPosition">
+ this.f.setX(canvas.getMouse("x")-this.x_offset);
+ this.f.setY(canvas.getMouse("y")-this.y_offset);
+</method>
+</programlisting></example>
+
+<para>Then modified it for simplicity and our purpose:</para>
+
+<example role="live-example">
+ <title>testIdle</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-8.lzx"/></
textobject>
+ </programlisting>
+</example>
+<?example role="live-example"><title>testGoButtonTrueFalse</
title><programlisting role="lzx-
embednew"><filename>testdriven-8.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+ <method name="wake" event="oninit">
+ foo = new LzDelegate(this, "sleep", LzIdle, "onidle");
+ </method>
+
+ <method name="sleep">
+ Debug.write("Sleeping");
+ </method>
+
+<TestSuite>
+ <TestCase>
+ <method name="testIdle">
+ assertTrue(this.idle);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-8.lzx></example?>
+
+<para>I wasn't surprised to find that the assertion failed, but I
was delighted to discover that after the TestSuite ran, the
universe went to an idle state, causing the <literal>sleep</
literal> method to write "Sleeping" repeatedly. The idle state
itself is an "infinite loop"! </para>
+
+<para>Here's a problem. The test-driven development routine says
"no new code without a new test", but I still don't know how to
test for the idle state, and since the <literal>wake</literal> and
<literal>sleep</literal> methods will be folded into goButton's
onclick method, that still falls under the integration testing
umbrella. </para>
+
+<para>If we break the rules and plow ahead, the while loop is
removed (because the idle state replaces it) and the
<literal>&& goButton.counter<Number.MAX_VALUE</literal>
condition moves into the <literal>if</literal> statement:</para>
+
+<example role="live-example">
+ <title>testGoButtonCount</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-9.lzx"/></
textobject>
+ </programlisting>
+</example>
+<?example role="live-example"><title>testGoButtonTrueFalse</
title><programlisting role="lzx-
embednew"><filename>testdriven-9.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<button name="goButton" width="100" text="Go">
+
+<attribute name="go" type="boolean" value="true"/>
+<attribute name="counter" type="number" value="1" when="once"/
>
+
+<method event="onclick">
+ if (goButton.go==true) {
+ if (goButton.counter<Number.MAX_VALUE) {
+ foo = new LzDelegate(this, "count", LzIdle, "onidle");
+ this.setAttribute('text', 'Stop');
+ this.setAttribute('go', false);
+ }
+ }
+ else {
+ Debug.write("Paused");
+ this.setAttribute('text', 'Go');
+ this.setAttribute('go', true);
+ }
+</method>
+
+<method name="count">
+ Debug.write(counter);
+ counter=counter+1;
+</method>
+
+</button>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertEquals(1, goButton.counter);
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-9.lzx></example?>
+
+<para>It almost works! The problem now is that even though the
"Stop" button sets goButton.go to false, the LzIdle function is
still active, and triggers the <literal>count</literal> method. </
para>
+
+<para>The solution is also found in the Layout and Design
example. Idling calls the method <literal>startDraggingFloater</
literal>, and its terminating condition is <literal>cancelFloater</
literal>:</para>
+
+<example role="live-example"><title>cancelFloater method</
title><programlisting>
+<method name="cancelFloater">
+ this.gm.unregisterAll();
+ this.d.unregisterAll();
+ this.f.destroy();
+</method>
+</programlisting></example>
+
+<para>The cancelFloater method demonstrates how to unregister the
LzIdle delegate with <literal>unregisterAll()</literal>. In our
application, we will unregister the idle method when goButton is
set to false:</para>
+
+<example role="live-example">
+ <title>testGoButtonCount</title>
+ <programlisting language="lzx">
+ <textobject><textdata fileref="programs/testdriven-10.lzx"/></
textobject>
+ </programlisting>
+</example>
+<?example role="live-example"><title>testGoButtonTrueFalse</
title><programlisting role="lzx-
embednew"><filename>testdriven-10.lzx</filename><parameter/><literal>
+<canvas debug="true">
+<debug y="150"/>
+<include href="lzunit"/>
+<simplelayout axis="y" spacing="10"/>
+
+<button name="goButton" width="100" text="Go">
+
+<attribute name="go" type="boolean" value="true"/>
+<attribute name="counter" type="number" value="1" when="once"/
>
+
+<method event="onclick">
+ if (goButton.go==true) {
+ if (goButton.counter<Number.MAX_VALUE) {
+ foo = new LzDelegate(this, "count", LzIdle, "onidle");
+ this.setAttribute('text', 'Stop');
+ this.setAttribute('go', false);
+ }
+ }
+ else {
+ foo.unregisterAll();
+ Debug.write("Paused");
+ this.setAttribute('text', 'Go');
+ this.setAttribute('go', true);
+ }
+</method>
+
+<method name="count">
+ Debug.write(counter);
+ counter=counter+1;
+</method>
+
+</button>
+
+<TestSuite>
+ <TestCase>
+ <method name="testGoButtonTrue">
+ assertEquals(1, goButton.counter);
+ assertTrue(goButton.go);
+ </method>
+ </TestCase>
+</TestSuite>
+
+</canvas>
+</programlisting><?lzx-edit programs/testdriven-10.lzx></example?>
+
+<para>The application works! I won't call this a complete
success, though, because the development was not wholly test-
driven. Perhaps the next example will be.</para></section>
+</section></chapter>
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-1.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-1.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-10.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-10.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-2.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-2.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-3.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-3.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-4.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-4.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-5.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-5.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-6.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-6.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-7.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-7.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-8.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-8.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: openlaszlo/branches/legals/docs/src/developers/programs/
testdriven-9.lzx
Property changes on: openlaszlo/branches/legals/docs/src/
developers/programs/testdriven-9.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
_______________________________________________
Laszlo-checkins mailing list
[EMAIL PROTECTED]
http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins