New project: Shtory

Nothing’s funnier than a joke about something people have long since stopped talking about. Introducing shtory: stories, for the Unix shell. You’re welcome, and I’m sorry.

First Snapchat introduced its “stories” feature as a broadcast alternative to its original model of sending self-destructing snaps directly to individual users; then Facebook was rebuffed in trying to buy Snapchat and Mark Zuckerberg directed Instagram to reproduce an identical “stories” feature in Instagram, prompting all kinds of jokes about what software would have “stories” next.

from Know Your Meme, a photoshopped image of Microsoft Excel with "stories" — a row of usernames and icons between the toolbars and the spreadsheet proper, just like in Snapchat and Instagram.
Excel, perhaps

Then in 2020, Twitter announced its — also identical — feature, this time called “Fleets” (see, like “tweets”, but they’re “fleeting”), and there was a whole new round of jokes.

In particular, Jef Poskanzer tweeted:

Tweet from Jef Poskanzer (@jefposk), November 18, 2020, reading “/bin/sh has stories now too.”, with an attached image of a shell prompt under a row of ASCII-art faces

And I thought, “heh. that’s pretty funny.”

And then I thought, “you know, I bet I could actually write a program to do that.”

And then I thought, “that’s a terrible idea.”

So, obviously I’m doing it. Introducing my new project: shtory — Snapchat/Instagram/Twitter-style stories, for the Unix shell. You’re welcome, and I’m sorry.

Everyone knows nothing’s funnier than a joke about something people have long since stopped talking about, so in keeping with that principle I hope to have an alpha of shtory ready to put up on github by about the end of the month. Ultimately the concept here isn’t very dissimilar to the traditional Unix utility finger, which displays the .plan and .project files, if any, that a user has in their home directory; but individual poshts in a shtory will, like their social media inspirations, only live for 24 hours before being automatically deleted, and I plan on implementing more granular privacy controls of the type we’re used to in modern social media, like follow lists, blocking, locked accounts, mutual-only posts, etc.

Cproj: v1.0.0 is live!

I’ve just pushed v1.0.0 of Cproj up to Github. Usage is very simple: source cproj.sh (I have a line in my .bashrc to do that when my shell starts up), then type cproj <projname>. Cproj creates a <projname> directory and populates it with a ready-to-build project skeleton including source and header files, Makefile, and basic test suite using Scuttle. If Scuttle isn’t installed on the system (Cproj checks the default installation location /usr/local), Cproj attempts to download it from the main Github repository.

You can immediately cd <projname>; make to build the project and unit tests and run the test harness.

New project: Cproj

Next I’ll start a real project. Or maybe I’ll just discover another bit of tooling that needs revision.

A couple of weeks ago, when I got started on what became Scuttle, I mentioned that the reason I wanted a simple but adequately featureful plain-C unit testing framework was that I was frustrated with the limitations of the more ad-hoc solution I’d built into my old cproj() shell script. It creates an even simpler test facility, but it was too simple.

Now that Scuttle v1.0.0 is done and published, I want to revisit Cproj. I haven’t touched this script in years, and I think there’s a lot of room to clean it up and make it more useful — and update it to use Scuttle as the testing framework it builds into the project skeletons it generates!

Much like Scuttle, the animating philosophy of Cproj is that it should be as self-contained as possible: the script contains its templates for the files it generates as here-documents, and runs pattern substitutions on them to produce the right output. The first time it’s run, the old Cproj actually writes out those templates to /etc/skel/proj/ (if run as root) or to $HOME/.skel/proj/, and on subsequent uses reads them from disk instead. I’m no longer sure there’s much value in doing that, so I’ll probably remove that functionality, and just use the here-docs directly every time, like Scuttle does.

After I finish fixing up Cproj, I should be ready to use my revised tooling to start a, you know, real project. Or maybe I’ll just discover another bit of tooling that needs revision.

Scuttle: v1.0.0 is live!

All right, it took me two weeks, but Scuttle v1.0.0 is now live. I think it’s pretty easy to use, and it’s very lightweight. The full README is over at github, of course, but it basically works as I outlined in the project introduction. No one really needed a new unit test framework, but I wrote one anyway, and now I’m going to use it in subsequent projects as often as I can.

I hope other people also find it useful!

Scuttle: pre-update update

Scuttle is now working as intended for the standard use case. As described in the introductory post, for a C project with a standard layout, adding unit testing with Scuttle requires the following steps:

  1. Install scuttle.h either locally to the project or in the system include path
  2. Install scuttle.sh either locally to the project or somewhere on your $PATH
  3. Add test suite source files under test/, each corresponding to a module of your project, named test_<module>.c, using Scuttle’s simple macros
  4. Add a test target to your main Makefile as follows:
test:
    bash scuttle.sh test
    $(MAKE) -C test

Scuttle will generate test/Makefile, test/test_<module>.h and test/test_<module>_gen.h for each test/test_<module>.c suite, and the test/test_<project>.c harness; and the generated Makefile’s test target will build and run the test harness and pipe the output to test/log/test_<project>.log.

There’s a lot of room to make this more flexible and add some convenience features, but in terms of functionality it’s at MVP level now.

Tomorrow I’ll do a quick cleanup pass, make a few small tweaks I’ve already thought of, write some basic documentation, and actually push the code up to a public repo so if anyone’s interested in trying it out, it’ll be available.

New project: Scuttle

A basic principle of engineering, software or otherwise, is “don’t reinvent the wheel.” I’m gonna anyhow.

Last week I wrote about wanting a very lightweight C unit testing solution, and, in my bullheaded way, wanting to write it myself rather than learn an already-existing system. It’s a commonplace in software engineering that the impulse to start over from scratch is one of the worst habits of programmers; one way or another, reinventing the wheel means doing lots of work you didn’t have to, usually to get a less satisfactory result. Either you end up hand-rolling something you should have just used a library for; or you look at a mass of legacy code full of cryptic comments and weird edge-case handling, think “whew, what a mess, how hard to maintain! better to just scrap it all and start fresh, so it will be cleaner and more elegant,” only to discover along the way that all those weird edge cases really exist, and end up with just as patchwork a code base as the one you meant to improve.

All that said, I’m doing it anyway. It’s not like I’m busy right now, and this is what’s engaging my brain.

So, suppose you have a project projname, and you want to add unit testing. Your project layout looks like this:

~/projname$ ls
include/
Makefile
src/
~/projname$ ls include
include/foo.h
~/projname$ ls src
src/foo.c
src/main.c

You want to add unit tests for the foo module, which is sensible because it’s pretty complex and you want to be sure all of it works exactly right. The foo module looks like this:

foo.h

#ifndef _FOO_H
#define _FOO_H

int foo();

#endif /* _FOO_H */

foo.c

#include "foo.h"

int foo()
{
    return 42;
}

To add unit testing with Scuttle (let’s say it stands for “simple C unit testing tool, limited edition”), you just need to put scuttle.h in your include path and scuttle.sh in your executable path, create a test/ directory, and add test_projname_foo.c:

#include "foo.h"
#include "test_projname_foo.h"
#include "scuttle.h"
#include <stdio.h>

SSUITE_INIT(foo)
    printf("foo suite init\n");
SSUITE_READY

STEST_SETUP
    printf("foo test setup\n");
STEST_SETUP_END

STEST_TEARDOWN
    printf("foo test teardown\n");
STEST_TEARDOWN_END

STEST_START(foo_return_true)
    int i = foo();
    SASSERT_EQ(42, i)
STEST_END

STEST_START(foo_return_false)
    int i = foo();
    SREFUTE(i == 69)
STEST_END

You’ll have noticed that no test_projname_foo.h header exists: scuttle.sh will generate that, as well as a test/test_projname_foo_gen.c source file with some data structures, and test/Makefile, for you.

~/projname$ ls test/
test/test_projname_foo.c
~/projname$ scuttle.sh
This is Scuttle, v1.0.0.
Working...
    * found suite test/test_projname_foo.c
    * generated suite header test/test_projname_foo.h
    * generated suite data test/test_projname_foo_gen.c
    * generated harness test/test_projname.c
    * generated makefile test/Makefile
Done.
Type 'make -C test/ test' to build and run your test harness.
~/projname$ ls test/
test/bin/
test/log/
test/Makefile
test/obj/
test/test_projname.c
test/test_projname_foo.c
test/test_projname_foo.h
test/test_projname_foo_gen.c
~/projname$ make -C test/ test
[$(CC) output]
test/test_projname > test/log/test_projname.log
~/projname$ cat test/log/test_projname.log
This is Scuttle, v1.0.0.
Running test harness for: projname

Test suite projname_foo:
 *** Suite passed: 2 / 2 tests passed.
 *** 1 / 1 suites passed

Aside from the simple SASSERT(x) and SREFUTE(x), Scuttle provides convenience macros SASSERT_NULL(x), SASSERT_EQ(x,y), and SASSERT_STREQ(x,y), as well as SREFUTE versions of the same.

As of this writing, scuttle.h is at or near v1.0 completeness, targets for scuttle.sh‘s output are determined, and I’m beginning work on scuttle.sh itself. More on those soon.