The De-Evolution of the Estus Flask

The Estus Flask was genius. From Software created the Estus Flask in Dark Souls, their spiritual sequel to Demon’s Souls. In Demon’s Souls—like other action role-playing games—players could heal with consumable items. To progress through each level, players needed healing grasses to recover when damaged. If their supply was inadequate, they would need to return to previous levels, defeat weaker enemies, obtain currency, and purchase consumables. Or they could fight enemies that dropped the healing items. Either way, players would be replaying levels they had already mastered and re-fighting enemies tens or hundreds of times in order to gather resources. This mindless “farming” was trivial, tedious, and time-consuming. Since levels did not clearly indicate their length or difficulty, players would have to guess how many healing items they would need. Players might arrive with too few consumables, run out, and restart the farming process. Or players could arrive with too many healing items and potentially trivialize the level. The result was a combination of wasted time and inconsistent difficulty.

The Estus Flask eliminated farming and fine-tuned the challenge. In Dark Souls, From Software introduced the Estus Flask: a permanent item with limited usages (usually 5) that would automatically refill at each checkpoint/bonfire. Now there was no need to farm; simply return to the checkpoint to refill Estus. Each level’s healing was strictly controlled to prevent players from playing through the level sloppily and healing hundreds of times to compensate. This system was not a complete innovation—old-school games like Castlevania had the distinction between lives and continues to accomplish similar goals: require skill from the player while still allowing a few mistakes. But the Estus Flask modernized those old-school designs to solve the farming problem in today’s action role-playing games.

Every sequel to Dark Souls has made this healing system worse.

Testing a Java Memory Leak using System.gc() and WeakReference

When I worked on Liferay Faces, the team discovered a memory leak where JSF @RequestScoped, @ViewScoped, and @SessionScoped beans were never removed during Garbage Collection. The leak occurred in JBoss. JBoss expects Servlet event listeners to fire so it can clean up references to beans, but Liferay Faces runs in a Portlet environment, so JBoss never cleaned up its references. Fire the listener when the corresponding Portlet event occurs and voilà: memory leak fixed.

In order to prevent regressions, the team added a test that could verify that this issue did not recur. Unfortunately, the test required manual input. The tester would attach VisualVM to the app, generate a bunch of beans, log out via the browser, force garbage collection via VisualVM, and verify via VisualVM that all beans were cleaned up. I decided to automate this test. We already used Selenium for end-to-end testing so it was easy to automate the UI interactions that reproduced the leak. But I still had two roadblocks:

  1. How could I automate garbage collection (GC)?
  2. How could I track instances of leaked beans without creating new references that would ultimately prevent those beans from being removed?

Java Testing Tip #2:

Don’t Clean Up After Tests with RunListener.testRunFinished(), Add a Shutdown Hook Instead

Recently, while I was writing some tests which require a lot of temporary files, I ran into a problem: my tests never cleaned up the temporary files.

Do You Have Teeth?

There is a generation whose teeth are like swords and whose molars are like knives to devour the poor from the earth and the needy from among the human race.

—Proverbs 30:14

This evening, my one-year-old son, Jules, and I took a walk around the neighborhood. While Jules was cruising around picking up sticks, he spotted a woman walking her dog in the distance.

Java Testing Tip #1:

Don’t Assert.fail() on Exception, Wrap With AssertionError Instead

Recently while updating and writing tests, I ran into a pattern:

try {
    /* Tested code here... */
}
catch (Exception e) {
    Assert.fail("Exception was thrown with message: " + e.getMessage());
}

Many times when developers are writing tests, they run into a similar situation where throwing an exception should result in a test failure. An uncuaght exception causes a test error, so the exception is caught and Assert.fail() is called to fail the test instead. The above code is certainly correct, but it also hides all the information that the original exception contained.