‘With a little help from my friends’, my tests are now fast. All of us are greater than any of us. This is important.

During last night’s Zoom Ensemble, as I like to call it, I mentioned that my tests were slow and that I had had little luck making them faster. So we looked at the situation a bit on a shared screen. We went through the usual ritual, most of which I had done before, but I was willing to do it again, both to bring Mike, Bryan, and Bill up to speed, but also because they might go about checking in different ways and of course I might have messed up or misinterpreted my results.

We ignored some tests, commented some out, and came to pretty much the same conclusion: there was no high fencepost test that was taking all the time: they all seemed about the same duration.

That tells you, of course, that the issue is in the setup and teardown, or in the inherent overhead of the testing framework. We were about to discuss whether I wanted to write a new testing framework when Bryan said something about his tests running really slowly until he noticed he was loading fonts over and over, and asked whether that might be going on.

“No”, I replied. “The setup on these tests is nearly trivial, just instantiating a couple of objects, and doing no real work”. I clicked into GameRunner init, certain that it was trivial. I found this:

function GameRunner:init(testX, testY)
    self.tileSize = 64
    self.tileCountX = testX or 85 -- if these change, zoomed-out scale 
    self.tileCountY = testY or 64 -- may also need to be changed.
    self.cofloater = Floater(50,25,4)
    self.musicPlayer = MonsterPlayer(self)
    self.dungeonLevel = 0
    self.requestNewLevel = false
    self.playerRoom = 1
    Bus:subscribe(self, self.createNewLevel, "createNewLevel")

“See”, I began … and noticed the call to initializeSprites. I popped over to that:

function GameRunner:initializeSprites()
    local names
    local sheet
    sheet = asset.gear_staffs_2
    names = {"crossed", "green_staff", "feather_staff",
        "purple_staff", "snake_staff", "cudgel_staff", "skull_staff",
    "bird_staff", "jewel_staff", "knob_staff", "crystal_staff", "twist_staff"}
    Sprites:add(names, sheet, 1,0, 0,1)
    sheet = asset.items_health
    names = { "blue_pack", "red_pack", "green_pack",
        "blue_vial", "red_vial", "green_vial",
        "blue_flask", "red_flask", "green_flask",
        "blue_jar", "red_jar", "green_jar",
        "blue_vase", "red_vase", "green_vase",
    assert(#names==23, "bad count for bottles and jars")
    Sprites:add(names, sheet, 1,0, 0,1)
    sheet = asset.mimic_spritesheet_walk
    names = {"mwalk01","mwalk02","mwalk03","mwalk04","mwalk05","mwalk06","mwalk07","mwalk08","mwalk09","mwalk10"}
    assert(#names==10, "bad count for mimic walk")
    Sprites:add(names,sheet, 0,0,0,0)
    sheet = asset.mimic_spritesheet_hide
    names = {"mhide01","mhide02","mhide03","mhide04","mhide05","mhide06","mhide07","mhide08","mhide09","mhide10"}
    assert(#names==10, "bad count for mimic hide")
    Sprites:add(names,sheet, 0,0,0,0)
    sheet = asset.mimic_spritesheet_attack
    names = {"mattack01","mattack02","mattack03","mattack04","mattack05","mattack06","mattack07","mattack08","mattack09","mattack10"}
    assert(#names==10, "bad count for mimic attack")
    Sprites:add(names,sheet, 0,0,0,0)
    sheet = asset.mimic_spritesheet_death
    names = { "mdead01", "mdead02", "mdead03", "mdead04","mdead05","mdead06","mdead07","mdead08","mdead09","mdead10"}
    assert(#names==10, "bad count for mimic death")
    Sprites:add(names,sheet, 0,0,0,0)
    sheet = asset.mimic_spritesheet_idle
    names = { "midle01", "midle02", "midle03", "midle04","midle05","midle06","midle07","midle08","midle09","midle10"}
    assert(#names==10, "bad count for mimic idle")
    Sprites:add(names,sheet, 0,0,0,0)
    sheet = asset.mimic_spritesheet_hurt
    names = { "mhurt01", "mhurt02", "mhurt03", "mhurt04","mhurt05","mhurt06","mhurt07","mhurt08","mhurt09","mhurt10"}
    assert(#names==10, "bad count for mimic hurt")
    Sprites:add(names,sheet, 0,0,0,0)

“Um, wow. That’s reading all the sprite assets and chopping them up, and it does it on every creation of a GameRunner.”

So I commented out that line and the tests ran in a recorded 0.0 seconds, good to the nearest second.

I have since removed that line from GameRunner and moved it to setup:

function setup()
    if CodeaUnit then 
        local t = os.time()
        CodeaTestsVisible = true
        print("test time ", os.difftime(os.time(),t))
    OperatingMode = PlayerMode()

I’m not at all sure why that method is part of GameRunner. It doesn’t refer to GameRunner at all.

Could we make it part of Sprites? I think we probably can. Let’s first commit what we have: Initialize Sprites only once, not in each GameRunner.

Now let’s move that method over to sprites and change the call. Works perfectly. Commit: Sprites initialization moved to Sprites class-instance.

So tests now run in a second or less. Sweet. Let’s reflect.

Reflection: Emergent Properties of Teams

I don’t want to over-generalize this too far, but I specifically recall hearing Bryan talk about his font fix in previous ensemble sessions. And I’m pretty sure that we talked about my tests and that I said that I had scoped down the initialization to “trivial”.

Last night, we were all assembled around the code. We weren’t just talking about what we knew, guessed, or believed. As one does in pair or mob or ensemble or group programming, we were all looking at the same code at the same time. For my part, as the keyboard owner, I would move to the bit of code we were talking about. I’d tell what a method did, but I’d have it on the screen in front of all of us.

Alone, I wouldn’t look at all those things, because a) they’re working and have a name that has meaning, so no reason to look, and b) I “know” what they do anyway. Even if I did look, it would be more a matter of refreshing my memory. I would rarely look at each line to see whether I needed to explain it to the group. I’d scan the method and see its shape and meaning, and move on.

When we’re working with a group, what we find is that when the group works well together, the group is stronger than any of the individuals in it, and the group comes up with ideas and understanding that no one of the individuals would have come up with.

There are a lot of ways a group working together can go. One of those ways, unfortunately, is where someone comes up with an idea and one or more others immediately YABBUT it to death. Groups like that are competing with each other and they become less powerful than any of the individuals in the group.

But the other way! The other way! Someone has a fragment of an idea and someone else supports and someone else says “Yes, and then …” and a small, weak idea bounces around the group and becomes strong, robust, interesting, and inspiring.

Honestly, I’m happier about this little group discovery than I’d be had I discovered it myself. If I had found it on my own, which was clearly less likely but possible, I might have felt rueful and kind of silly for not noticing. But as part of our Friday Night Boys Night Outxxx In Team, I was happy, and I think we were all happy, because we had won one.

We all enjoy winning. And it’s the most fun when it’s shared. Are you part of a “team”? If so, do what you can to remove the quotes. You’ll be glad you did.

See you next time!