smlunit – using without a python dependency

I’m currently taking the Coursera Programming Languages course which starts with ML.  Or more specifically SML/NJ.

Why I was looking at unit testing in SML

The instructor gave a test file with statements like:

val foo = function_to_write(1) = 2;

This is good in that it clearly shows the API and some of the expectations.  It’s not so good because you have to go thru the output to look whether all the values are set to true.  We were provided with 11 tests and told to write more.  I had 57 by the time I was done.  And I haven’t looked at the two challenge problems yet.  The instructor was very direct in saying we shouldn’t compare SML to languages we know because it impedes learning.  Well, I didn’t.  I compared the infrastructure.  And I learned more SML learning how this works than covered in week 1 of the class so I think I honored the spirit of this.

Choosing a SMLUnit implementation

I found two options in search

  1. SML Sharp‘s SML Unit – I found more references to this one on the internet.  However it requires steps to install and looks like it takes longer to get started with.
  2. SML Unit – This is the #1 hit on google for SML Unit and appears to be something written by a university student.  It’s very good though and I recommend it.

Python dependency

SMLUnit comes with good documentation and examples.  It also comes with a runner that uses Python to kick off the tests and report on the status.  This can’t be done purely in SML because reporting on the results requires state.

I’m sure it is a good runner, but I didn’t notice it existed.  (I started on the code page with the assertions not the main page that explains all this.)  Since I went to the (minor) trouble to create a runner in UNIX,  I’m posting it in case anyone wants to test SML on a machine that doesn’t have Python.  Not likely I know – most machines have Python.

The runner

The runner feeds your unit tests to SML and then reports on whether there were any failures.


# Since sml is a functional language, I couldn't figure out a way to output a summary
# at the end as to whether any tests failed. Wrapping in a shell script to avoid
# this problem. [realized afterwards it comes with a Python runner]
#
# Usage:
# smlunit_runner.sh name_of_tests.sml
#
# Assumes the sml test suite has "use" statements for all dependencies including smlunit

if [ $# -eq 0 ]
then
 echo "SML test file is mandatory"
 exit
fi

OUTPUT=`cat $1 | /usr/local/smlnj/bin/sml`

echo "$OUTPUT"

NUM_FAIL=`echo "$OUTPUT" | grep "^FAIL$" | wc -l`
NUM_PASS=`echo "$OUTPUT" | grep "^OK$" | wc -l`

echo ""
echo ""
echo "Test summary: (also look at last output to make sure not a stack trace)"
echo "$NUM_FAIL tests failed"
echo "$NUM_PASS tests passed"

What do the tests look like?

Code – helloWorld.sml

fun hello_world() = "hello"

fun add(a:int, b:int) = a + b

Tests – helloWorldTest.sml

use "asserts.sml";

<em id="__mceDel">use "helloWorld.sml";</em>

assertTrue true "assert true";
assertEqual "hello" (hello_world()) "compare two values";
assertEqual 3 (add(1,2)) "compare two values again";

And what about the asserts?

asserts.sml is a direct copy of smlunit.sml


(* from https://github.com/dellsystem/smlunit/blob/master/lib/smlunit.sml *)

fun roundish (x:real) = Real.realRound(x * 100000000000.0) / 100000000000.0;

fun assert expr1 expr2 (desc:string) function =
 (print ("*********** " ^ desc ^ " ***********\n" );
 (if function (expr1, expr2) then
 print "OK\n"
 else
 print "FAIL\n"
 );
 [expr1, expr2]);

fun assertEqual expr1 expr2 (desc:string) = assert expr1 expr2 desc (fn (x, y) => x = y);
fun assertRealEqual (expr1:real) (expr2:real) (desc:string) = assert expr1 expr2 desc (fn (x, y) => Real.== (roundish(x), roundish(y)));

fun assertTrue expr desc = (assertEqual expr true desc);
fun assertFalse expr desc = (assertEqual expr false desc);

(* Functions for comparing a lot of things in one list *)
fun assertReals f [] (desc:string) = []
 | assertReals f ((input, output, expl)::t) (desc:string) =
 (assertRealEqual (f input) output (desc ^ ": " ^ expl)) @ (assertReals f t (desc));

fun assertRegulars f [] (desc:string) = []
 | assertRegulars f ((input, output, expl)::t) (desc:string) =
 (assertEqual (f input) output (desc ^ ": " ^ expl)) @ (assertRegulars f t (desc));

Comments?

I have less than 5 hours experience with SML   (I did know LISP in college and recursion certainly isn’t new.)  So I’m sure there are naming conventions and the like that I’m not following.  I look forward to hearing comments on what I did.

maker faire and rain plans

mf3

Last year, I blogged about co-organizing a booth at World Maker Faire and that we won a blue ribbon.  This year we won a red educators choice ribbon.  Which was also very cool.  Plus this year, my friend and I ran a whole tent.

It was a lot of fun and we got to see some from robots that the kids made.  Of course we had all the FIRST robots (FIRST Lego League, FIRST Tech Challenge and FIRST Robotic Challenge) that the students build.  But we also had a heat/motion sensitive hand sanitizer dispenser.  Especially helpful since we were next to the port-a-potties!

mf2

Speaking of rain, our rain plans get better every year!  Let me tell you what happened this year.

Saturday 9:30am

Norm and I gave the morning briefing to the teams.  Which covered what to do if it rains.

  • move everything into the middle of the tent
  • cover everything electronic with tarp or painters plastic
  • leave a couple team members with the stuff and have everyone else go into the Hall of Science (you can only fit so many people in a tent)

Saturday 9:40am

It starts drizzling.  Luckily everyone was listening and was able to execute the rain plan.  And also luckily, the rain didn’t last long.  While we were waiting under the tent, Bronx Science added an umbrella to their robot. mf8

Saturday about 11am

More rain.  Again, everyone dealt with the electronics quickly and the rain didn’t last long.

Saturday 7pm

The prediction was for rain 8pm-midnight and downpours (.5 to 1 inch per hour) from midnight-6am.  Clearly we needed to protect the booth well.  We moved everything to the center, flipped tables over and covered the FTC field with:

  • painters plastic
  • a table cloth
  • a garbage bag
  • chairs to hold everything down

mf7

Sunday 7:30am

I was first to the booth and was thrilled to see that our plan worked.  I had some students help me lift the plastic from the corners in.  Someone said it looked like a goldfish bowl – there was so much water inside.  But the tops of the foam mats were mostly dry.

mf6mf5mf4

Sunday 6pm

While we protected the top of the field, we didn’t for the bottom.  It was fine, but I hadn’t realized how wet it was down there until we picked up the field!

mf1

Improvement

The tablecloths worked better than I’d have ever imagined.  The painter’s plastic also worked really well.  And for FRC teams – “bag and tag” bags work wonders for rain protection – great idea Xavier!

What is the lesson?

Have good plans.  Communicate them.  Make them better year after year.

 

java puzzlers at the java sig

I got to see Josh Bloch and Bob Lee perform 9 Java Puzzlers.  That’s right.  I said perform.  They were funny, dynamic and had great banter.  It felt so natural.  And it was educational on top of all that.

I’m not going to try to transcribe the puzzlers.  You can read the book for many like this.  What I am going to do is mention my favorite 3 things I learned.

1) Double.MIN_VALUE isn’t what you think

Integer.MIN_VALUE is the smallest possible Integer.  So clearly Double.MIN_VALUE is the smallest possible Double.  Nope!  Double.MIN_VALUE is the smallest positive number that can be represented as a double.  4.9E-324.  Double.NEGATIVE_INFINITY is the smallest possible Double.

2) Compile time “constants” can change

System.out.println(Const.A + ” ” + Const.B);

If you compile and run this println against

interface Const {

String A = “a”;

String B = null;

}

it prints “a null”.

Then if you change it to run against

interface Const {

String A = “A”;

String B = “B”;

}

it prints “a B”.  That’s right.  The “a” is remembered because compile time constants are compiled in.  Whereas null isn’t a compile time constant so the new value is used.  Same for enums – not a compile time constant.

3) Autoboxing is a minefield

Ok.  So this one I knew already.  But it’s really important.  When comparing Integer objects, == is used.  Not autoboxing.  And JDKs are free to have the cutoff for where Integer objects are cached for autoboxing in different places.  So it is important not to rely on this.