This is an old revision of the document!


Appendix

In the appendix, we explain useful libraries available to the AmbientTalk/2 programmer. These libraries provide abstractions ranging from traditional, established “collections” up to newly researched language constructs, such as “ambient references”.

Unit Testing Framework

The file at/unit/test.at shipped with the AmbientTalk/2 system library defines a unit testing framework for AmbientTalk/2 which is similar in spirit and structure to JUnit and SUnit. Load the module by executing import /.at.unit.test.

Creating a Unit Test

To create your own unit test, make an extension of the UnitTestobject which is exported by the unit testing module. In the extension, define zero-arity methods starting with the prefix test. Here is an example:

def myUnitTest := extend: UnitTest.new("my unit test") with: {
  def testSomething() {
    self.assertEquals(3,1+2);
  }
}

You can run a unit test by sending to your unit test object the message runTest(), for example:

myUnitTest.runTest()

This will execute all test* methods in the given unit test (in an undefined order!), and print out which of the tests succeeded or failed. The runTest method can optionally take a “reporter” object as an argument, which can be used to implement a custom strategy for reporting success or failure of a unit test. The default reporter object is a text-based UI.

Like in JUnit and SUnit, it is possible to define two methods named setUp() and tearDown() that are invoked in between each individual test* method. Never rely on the lexical order of your unit test methods for the purposes of initialization, etc.! Unit test methods may be exacuted in an arbitrary order.

Assertions

Within a test* method, you can use a number of assertion methods to assert certain properties of your code:

assertTrue(boolean)
assertFalse(boolean)
assertEquals(o1,o2)
assertNotEquals(o1,o2) 
assertLessThan(o1,o2)
assertGreaterThan(o1,o2) 
assertLessThanOrEquals(o1,o2) 
assertGreaterThanOrEquals(o1,o2)
assertMatches(str, pattern)
assertNotNil(val)

Each of these methods also takes as an optional last parameter a reason, which is a text string describing what the assertion checks. This string is printed when the assertion fails and can be used to provide more understandable error messages.

Finally, two more useful auxiliary methods exist:

assert: exceptionType raisedIn: closure
fail(reason)

The assert:raisedIn: method executes the given closure and checks whether this leads to an exception of type exceptionType. If so, the exception is caught and further ignored. If no exception (or one of the wrong type) is raised, the assertion will fail. The fail method can be used to explicitly make a unit test fail with a given reason.

A common mistake is to invoke the above assertion methods as if they were lexically visible (e.g. invoking assertEquals(...)). However, these methods are not lexically visible, rather they are defined in the UnitTest parent object. Hence, the proper way to invoke them is via a self-send, as shown in the above example.

Asynchronous Unit Tests

Up to now, the unit testing framework assumed that all of your unit tests consisted of purely synchronous method invocations. When running the tests, all test* methods are invoked sequentially, and the unit test ends when the last test* method has been invoked.

To support unit test methods that need to perform asynchronous invocations (e.g. those performing concurrent or distributed unit tests), the unit testing framework introduces a new prefix: all methods that spawn asynchronous computation must be prefixed with testAsync.

When a method is prefixed with testAsync, the unit testing framework expects the method to return a future and will only process subsequent test methods once that future is resolved or ruined. Here is an example that tests whether future-type messaging works:

def testAsyncFutures() {
  import /.at.lang.futures;
  def adder := object: { def inc(x) { x+1 } };
  def f := when: adder<-inc(42)@FutureMessage becomes: { |val|
    self.assertEquals(43, val);
  };
  f
}

The unit test framework will invoke this method, receive a future f, and only continue once the future f has been resolved. In the above example, the future f is the return value of the when:becomes: function, which means that f implicitly depends on the future associated with the call to ←inc(42).

It is also possible to use makeFuture() to create a fresh future explicitly within the unit test method, and to use the returned resolver to resolve the future at the appropriate time.

Test Suites

It is possible to group multiple unit test objects into what is known as a “test suite”. Running the test suite runs all of the component unit tests. You can create a new test suite as follows:

def myTestSuite := TestSuite.new("my test suite",
  [unittest1,
   unittest2,
   ... ]);

The TestSuite object groups the given unit test objects. You can execute all tests in batch by sending the test suite object the runTest message, just like for running a single unit test. It is possible to nest multiple test suites within each other.

Basic Collections

The modules /.at.collections.vector and /.at.collections.list define a Vector and List datastructure respectively.

Vector

A vector is a dynamically resizable AmbientTalk table (aka array). Vectors may be created as follows:

import /.at.collections.vector;
def v := Vector.new(10); // a vector with starting length 10

The constructor optionally takes a comparator as a second argument. A comparator is a binary function returning a boolean whose job is to compare elements of the Vector. This comparator is used among others when sorting the vector. The Vector’s interface is as follows:

// returns the number of elements in the vector (not its capacity!)
length()

// returns whether the vector contains elements or not
isEmpty()

// is the vector at max. capacity?
atMaxCapacity()

// return idx'th element or raise an IndexOutOfBounds exception
at(idx)

// write idx'th element or raise IndexOutOfBounds exception
//atPut(idx, val)

// iterate over the vector
each: iterator, returns nil

// map a unary function over the vector, returns a new vector
map: fun

// accumulate a function with a given starting value
inject: init into: accum;

// returns a new vector whose elements satisfy "cond"
filter: cond;

// implode a vector of character strings into one text string
implode()

// join a vector of character strings together with the given string
join(txt)

// returns a range [start,stop[ as a table
select(start, stop)

// appends an element to the back of the vector
// returns the vector itself
add(element)
// alias for add(element)
<<(element)

// insert an element at a given position, causing subsequent elements to shift one pos to the right. Returns this vector
insert(atPos, element)

// delete the element at the given position, shifts all following elements one pos to the left. Returns the value of the element at the deleted position.
delete(atPos)

// adds elements to the back of the vector
push(element)

// deletes elements from the back of the vector
pop()

// return the index of the first element matching the unary predicate or nil if none is found
find: filter

// remove the given element from the vector, return true if the element was actually found and deleted, false otherwise
remove(elt, cmp := defaultComparator)

// remove all objects for which filter(elt) returns true
removeAll: filter

// destructively appends otherVector to self. Returns this vector
addAll(otherVector)

// empties the vector
clear()

// Return a new vector whose elements form the set-union of all elements in self U otherVector
union(otherVector, cmp := defaultComparator)

// Return a new vector whose elements form the set-intersection of all elements in self ^ otherVector
intersection(otherVector, cmp := defaultComparator)

// Return a new vector whose elements form the set-difference of self \ otherVector
difference(otherVector, cmp := defaultComparator)

// Quicksort the vector in-place. The comparator defines the ordering among elements.
sort(cmp := { |e1,e2| e1 < e2 })

// Turn the vector into a set without duplicates in O(nlogn + n)
// The vector's ordering is lost (it becomes sorted)
uniq(cmp := defaultComparator, ordercmp := {|e1,e2| e1 < e2 })

// return an element drawn randomly using a uniform distribution from the array or raise an EmptyVector exception.
random()

// return a table containing all elements of the vector
asTable()

List

at/tutorial/appendix.1215693078.txt.gz · Last modified: 2008/07/10 14:51 by tvcutsem
 
 
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki