in reply to Re^8: Corner cases are not always "code smell"
in thread Neither system testing nor user acceptance testing is the repeat of unit testing (OT)

I'll admit that I've been enjoying some TDD Kool-Aid of late. In part, this is a reaction to my attitude towards testing several years ago and how it's come back to haunt me now. Specifically, with PDF::Template.

In my view of the world (however limited in scope that might be), optimizations are an implementation detail, save where they are required by spec. For example, "This process must happen in 0.1 seconds or less per record." Then, you write tests to verify that not only does this occur for the 1-record situation, but that the process scales linearly as you run 100, 1000, and 1_000_000 records. (Obviously, the latter is an weekend-only test.)

When they are not required by spec, I begin to wonder why optimizations are needed. Assuming that they are, then it's just a different implementation that meets the same specification (as enforced by the unit-tests).

In your case, the original implementation had bugs in it. When you find the bug, it becomes part of the spec (through the testsuite) and you fix the implementation to pass the tests. If that results in a quicker runtime or a reduced memory usage, that's bonus. It's not an optimization - it's correctly functioning code.

Think of this another way - if your code has corner-cases, then your interface also has corner-cases. If the interface doesn't specify to the user that these corner-cases exist, then how will s/he know to take them into account? Or, let's say that there's what you would call a corner-case because of VMS. File::Spec is a good example of this. Well, the spec now includes "This will run on VMS." Whatever is needed to meet the spec is what is needed to be done. And in the File::Spec testsuite, there's a section that deals with VMS, as there should be.


My criteria for good software:
  1. Does it work?
  2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
  • Comment on Re^9: Corner cases are not always "code smell"

Replies are listed 'Best First'.
Re^10: Corner cases are not always "code smell"
by tilly (Archbishop) on Oct 25, 2005 at 17:30 UTC
    The point that you're ignoring is that to make the spec work, you need to decide internally where and when to switch strategies. (BTW your refusing to call this switch an "optimization" is disingenuous at best.) Saying "this must work" and "that must be fast" says that you need 2 different strategies. It doesn't say where the boundary between them should be. That boundary should be invisible to the user of your code. And therefore you have an internal code boundary that is not part of the external specification.

    Furthermore without knowing the exact implementation, you can't specify how it works, because the appropriate boundary depends on the optimization chosen, which is highly implementation-dependent.

      The point that you're ignoring is that to make the spec work, you need to decide internally where and when to switch strategies.

      Yes - any version that implements the spec is correct. If your version doesn't implement the spec, it is incorrect. Any changes you need to make to your version to have it match the spec is internal to that version.

      (BTW your refusing to call this switch an "optimization" is disingenuous at best.)

      Why? You said that the "optimization" fixed bugs. To me, that's a bugfix. That it optimized the code is often a consequence of correct implementation.

      Saying "this must work" and "that must be fast" says that you need 2 different strategies. It doesn't say where the boundary between them should be.

      "This must work according to spec." is all I'm saying. The spec contains a speed requirement.

      It doesn't say where the boundary between them should be. That boundary should be invisible to the user of your code. And therefore you have an internal code boundary that is not part of the external specification.

      Does the code implement the spec? If yes, you may pass Go. If no, then go directly to Jail. If your version implements the spec and is faster than mine, that's wonderful. If it's faster, but it's wrong, then we're not comparing apples.

      Furthermore without knowing the exact implementation, you can't specify how it works, because the appropriate boundary depends on the optimization chosen, which is highly implementation-dependent.

      This sentence makes absolutely no sense to me. A specification tells you what it does. It most certainly doesn't say how it accomplishes that goal. And, yes, an optimization may certainly be platform-dependent, tools-dependent, etc. If your implementation wants to take those factors into account, it is welcome to do so. However, you're still implementing the spec, which, at the end of the day, is all that matters.


      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
        Here is what you are missing.

        In the example that I provided, there is a general case matching algorithm and a special case matching algorithm. To get correct behaviour for all kinds of regular expressions, you sometimes need to use the general algorithm. To get the specified speed for some common regular expressions, you sometimes else need to use the special case matching algorithm.

        The public specification does not say where the boundary between the special case and the general case is. It is a basic fact of life that there is a good chance of finding bugs around that boundary.

        What I am saying is that you'll find corner cases along that boundary which are nowhere visible or obvious in the specification, and it is a very good idea to have white-box tests for those corner cases.

        In pseudo-code, your implementation will contains something that looks like this:

        if (some conditions here) { use special case with performance guarantees } else { use general case that may be slow }
        You need white-box testing to be sure that you're testing that all regex features which can show up under both implementations are tested under them. If you just use black-box testing, then your tests that the construct x{3,5} works properly may only test one implementation and your tests won't notice if it fails in the other. This is exactly what happened in Perl 5.6.0.

        I really hope that this explanation is enough for you to understand the issue. If it is not then I really don't know how I can say this more clearly.