_by Stephen Malina and Kartik Agaram_ Mu explores ways to turn arbitrary manual tests into reproducible automated tests. Hoped-for benefits: 1. Projects release with confidence without requiring manual QA or causing regressions for their users. 1. Open source projects become easier for outsiders to comprehend, since they can more confidently try out changes with the knowledge that they'll get rapid feedback if they break something. Projects also become more *rewrite-friendly* for insiders: it's easier to leave your project's historical accidents and other baggage behind if you can be confident of not causing regressions. 1. It becomes easier to teach programming by emphasizing tests far earlier than we do today. The hypothesis is that designing the entire system to be testable from day 1 and from the ground up would radically impact the culture of an eco-system in a way that no bolted-on tool or service at higher levels can replicate. It would make it easier to write programs that can be [easily understood by newcomers](http://akkartik.name/about). It would reassure authors that an app is free from regression if all automated tests pass. It would make the stack easy to rewrite and simplify by dropping features, without fear that a subset of targeted apps might break. As a result people might fork projects more easily, and also exchange code between disparate forks more easily (copy the tests over, then try copying code over and making tests pass, rewriting and polishing where necessary). The community would have in effect a diversified portfolio of forks, a “wavefront” of possible combinations of features and alternative implementations of features instead of the single trunk with monotonically growing complexity that we get today. Application writers who wrote thorough tests for their apps (something they just can’t do today) would be able to bounce around between forks more easily without getting locked in to a single one as currently happens. In this quest, Mu is currently experimenting with the following mechanisms: 1. New, testable interfaces for the operating system. Currently manual tests are hard to automate because a file you rely on might be deleted, the network might go down, etc. To make manual tests reproducible it suffices to improve the 15 or so OS syscalls through which a computer talks to the outside world. We have to allow programs to transparently write to a fake screen, read from a fake disk/network, etc. In Mu, printing to screen explicitly takes a screen object, so it can be called on the real screen, or on a fake screen inside tests, so that we can then check the expected state of the screen at the end of a test. Here's a test for a little text-mode chessboard program in Mu (delimiting the edge of the 'screen' with dots):       a screen test We've built up similarly *dependency-injected* interfaces to the keyboard, mouse, disk and network. 1. Support for testing side-effects like performance, deadlock-freedom, race-freeness, memory usage, etc. Mu's *white-box tests* can check not just the results of a function call, bu
#!/bin/sh
# Translate a given Mu program into an ELF binary on Linux.

set -e

cat $* [0-9]*.mu    |apps/mu    > a.subx

./translate_subx init.linux [0-9]*.subx mu-init.subx a.subx
`chessboard.mu` checks the initial position of a game of chess (delimiting the edges of the screen with dots): screen test Similarly you can fake the keyboard to pretend someone typed something: fake keyboard ..or clicked somewhere: fake console (keyboard, mouse, ..) Within tests you can map arbitrary paths (local files or URLs) to contents: fake file-system and network As we add graphics, audio, and so on, we'll augment scenarios with corresponding abilities. --- Mu assumes that all ingredients passed in to functions are immutable by default -- *unless* they are also products. So this program will throw an error: immutable ingredient triggering an error To modify `foo`'s ingredient, you have to add it to the list of products returned: mutable ingredient The names of the variables are important here: a function that takes an (immutable) address and returns a different one is different from a function that takes a mutable address (and also returns it). These immutability checks can be annoying, but the benefit they provide is that you can always tell what a function modifies just by looking at its header. In combination with dependency-injected hardware, they provide all the benefits of [referential transparency](https://en.wikipedia.org/wiki/Referential_transparency) that we typically associate with purely functional languages -- along with the option of imperatively modifying variables willy-nilly. --- You can append arbitrary properties to reagents besides types. Just separate them with slashes. ```nim x:array:number:3/uninitialized y:string/tainted:yes z:number/assign-once:true/assigned:false ``` Most properties are meaningless to Mu, and it'll silently skip them when running, but they are fodder for *meta-programs* to check or modify your programs, a task other languages typically hide from their programmers. For example, where other programmers are restricted to the checks their type system permits and forces them to use, you'll learn to create new checks that make sense for your specific program. If it makes sense to perform different checks in different parts of your program, you'll be able to do that. You can imagine each reagent as a table, rows separated by slashes, columns within a row separated by colons. So the last example above would become something like this: ``` z : number / assign-once : true / assigned : false ``` --- An alternative way to define factorial is by inserting labels and later inserting code at them. literate programming (You'll find this version in `tangle.mu`.) By convention we use the prefix '+' to indicate function-local label names you can jump to, and surround in '<>' global label names for inserting code at. --- Another example, this time with concurrency: forking concurrent routines ```shell $ ./mu fork.mu ``` Notice that it repeatedly prints either '34' or '35' at random. Hit ctrl-c to stop. [Yet another example](http://akkartik.github.io/mu/html/channel.mu.html) forks two 'routines' that communicate over a channel: ```shell $ ./mu channel.mu produce: 0 produce: 1 produce: 2 produce: 3 consume: 0 consume: 1 consume: 2 produce: 4 consume: 3 consume: 4 # The exact order above might shift over time, but you'll never see a number # consumed before it's produced. ``` Channels are the unit of synchronization in Mu. Blocking on a channel is the only way for the OS to put a task to sleep. The plan is to do all I/O over channels. Routines are expected to communicate purely by message passing, though nothing stops them from sharing memory since all routines share a common address space. However, idiomatic Mu will make it hard to accidentally read or clobber random memory locations. Bounds checking is baked deeply into the semantics, and using pointers after freeing them immediately fails. --- Mu has a programming environment: ```shell $ ./mu edit ``` Screenshot: programming environment You write functions on the left and try them out in *sandboxes* on the right. Hit F4 to rerun all sandboxes with the latest version of the code. More details: http://akkartik.name/post/mu. Beware, it won't save your edits by default. But if you create a sub-directory called `lesson/` under `mu/` it will. If you turn that directory into a git repo with `git init`, it will also back up your changes each time you hit F4. Use the provided `new_lesson` script to take care of these details. Once you have a sandbox you can click on its result to mark it as expected: expected result Later if the result changes it'll be flagged in red to draw your attention to it. Thus, manually tested sandboxes become reproducible automated tests. unexpected result Another feature: Clicking on the code in a sandbox expands its trace for you to browse. To add to the trace, use `stash`. For example: ```nim stash [first ingredient is], x ``` Invaluable at times for understanding program behavior, but it won't clutter up your screen by default. --- If you're still reading, here are some more things to check out: a) Look at the [chessboard program](http://akkartik.github.io/mu/html/chessboard.mu.html) for a more complex example with tests of blocking reads from the keyboard and what gets printed to the screen -- things we don't typically associate with automated tests. b) Try skimming the [colorized source code](https://akkartik.github.io/mu). You should be able to get a pretty good sense for how things work just by skimming the files in order, skimming the top of each file and ignoring details lower down. c) Try running the tests: ```shell $ ./mu test ``` d) Check out [the programming environment](https://github.com/akkartik/mu/tree/master/edit#readme), the largest app built so far in Mu. e) Look at the `build` scripts. Mu's compilation process is itself designed to support staged learning. Each of the scripts (`build0`, `build1`, `build2`, etc.) is self-contained and can compile the project by itself. Successive versions add new features and configurability -- and complexity -- to the compilation process. **Credits** Mu builds on many ideas that have come before, especially: - [Peter Naur](http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22) for articulating the paramount problem of programming: communicating a codebase to others; - [Christopher Alexander](http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512) and [Richard Gabriel](http://dreamsongs.net/Files/PatternsOfSoftware.pdf) for the intellectual tools for reasoning about the higher order design of a codebase; - Unix and C for showing us how to co-evolve language and OS, and for teaching the (much maligned, misunderstood and underestimated) value of concise *implementation* in addition to a clean interface; - Donald Knuth's [literate programming](http://www.literateprogramming.com/knuthweb.pdf) for liberating "code for humans to read" from the tyranny of compiler order; - [David Parnas](http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf) and others for highlighting the value of separating concerns and stepwise refinement; - [Lisp](http://www.paulgraham.com/rootsoflisp.html) for showing the power of dynamic languages, late binding and providing the right primitives *a la carte*, especially lisp macros; - The folklore of debugging by print and the trace facility in many lisp systems; - Automated tests for showing the value of developing programs inside an elaborate harness; - [Python doctest](http://docs.python.org/2/library/doctest.html) for exemplifying interactive documentation that doubles as tests; - [ReStructuredText](https://en.wikipedia.org/wiki/ReStructuredText) and [its antecedents](https://en.wikipedia.org/wiki/Setext) for showing that markup can be clean; - BDD for challenging us all to write tests at a higher level; - JavaScript and CSS for demonstrating the power of a DOM for complex structured documents. - Rust for demonstrating that a system-programming language can be safe.