JanetRossini.github.io

Lua, LSL, Blender, Python in Second Life


Project maintained by JanetRossini

Cleanup

Jun 23, 2025 • [designluamoverstesting]


I’m still working, slowly, to get my Mac SLua/luau development environment set up to work, if not perfectly smoothly, increasingly smoothly. I have one small idea to try.

Sometimes, when we’re working on something, we might wish that one or ore failing tests would just be quiet while we work on some detail. I mentioned the other day that I’ve taken to just adding ‘ignore_’ to the name of the test. Since the Tests framework only runs tests that start with ‘test_’, the ones marked ‘ignore_’, they are conveniently ignored.

However. They are ignored silently, and it is easy to forget to turn those tests back on when it is appropriate. Some larger and more capable testing frameworks keep track of ignored tests and include them in their final report. Let’s do the same. It should be pretty easy.

We’ll start in run_tests:

function Tests:run_tests(verbose)
   tests_passing = 0
   tests_failing = 0
   for name, func in pairs(Tests) do
      if name:sub(1, 5) == 'test_' and type(func) == 'function' then
         self.current_name = name
         if verbose then print(name) end
         local ok, message = pcall(func, Tests)
         if not ok then
            self:tally(ok)
            self:report(string.format('Error:  %s.', message))
         end
      end
   end
   self.current_name = 'run_tests'
   local m = string.format('All Tests: Pass %s Fail %s.', tests_passing, tests_failing)
   self:report(m)
end

Well would you look at that! Looks like everything we need to do is right in there. How really nice it is when the code that belongs together is together. I think I’ll just edit the tests file and then run my current development file to check the results.

My first attempt works fine:

function Tests:run_tests(verbose)
   tests_passing = 0
   tests_failing = 0
   tests_ignored = 0
   for name, func in pairs(Tests) do
      if name:sub(1, 5) == 'test_' and type(func) == 'function' then
         self.current_name = name
         if verbose then print(name) end
         local ok, message = pcall(func, Tests)
         if not ok then
            self:tally(ok)
            self:report(string.format('Error:  %s.', message))
         end
      elseif name:sub(1,7) == 'ignore_' and type(func) == 'function' then
         tests_ignored += 1
      end
   end
   self.current_name = 'run_tests'
   local m = string.format('All Tests: Pass %s Fail %s Ignored %s.', tests_passing, tests_failing, tests_ignored)
   self:report(m)
end

In my working file, we get this:

All Tests: Pass 849 Fail 0 Ignored 2.  Tests:run_tests
[Finished in 31ms]

There is some duplication in the above code, checking for type twice. I think I’ll let that ride, at least for now.

I am concerned that this little warning is probably not enough. I’ll get used to seeing Ignored 0 and may not notice when it says Ignored 36 or something. If that happens, I’ll someday notice that and maybe improve things again. For now, I’m satisfied.

But what are those two ignored tests, anyway?

function Tests:ignore_test_dump_l_bezier()
   local b = self:sample_bezier()
   local lb = b:linearized(3)
   local s = ''
   local total = 0
   for i,len in lb._lengths do
      total = total + len
      s = s .. tostring(len) .. '(' .. tostring(total) .. ' '
   end
   print(s)
   print("length", total)
   s = ''
   for i, pt in lb._points do
      s = s .. tostring(pt) .. ' '
   end
   --print(s)
end

function Tests:ignore_test_vector()
    local v = vector(1,2,3)
    print("type ", type(v))
    local mt = getmetatable(v)
    -- print("vector")
    -- for k,v in pairs(v) do -- not a table
    --     print("vector", k, v)
    -- end
    print("mt")
    for k, v in pairs(mt) do
        print("mt", k, v)
    end
    print("end mt")
end

The first of those was a debugging test, used briefly to dump out information when I had made some egregious error and couldn’t figure out what it was. The second is one that I wrote to display information about vector type. Both of those are in git if I eve need them. Let’s remove them so our tests will run cleanly.

OK, done. Committed to Git.

I’ve been doing all my work in a single file named test.lua, not to be confused with tests.lua, which contains the testing framework. These file names may not be my best work ever.

The file itself includes tests for two classes, Bezier, which is all about calculating Bezier curves and splitting them, and L_Bezier, which is the class that follows the approximate path of a Bezier by following the straight lines between the Bezier’s control points. This works terribly if you start with any bezier you’re likely to find lying around in the railroad yard but after partitioning that one three times, the eight new Beziers are very well approximated by their control points. See the article Quality of Approximation for details.

Let’s begin by renaming this file bezier_dev. Commit that.

Now that that is engraved in history let’s look for code we don’t need, and remove it.

I find a commented-out debug method to dump an L_Bezier. Removed.

Other than that, L_Bezier just has init, create_points, create_lengths, find_distance, and point_at_distance. Only the latter method is really intended to be public. Let’s rename the others to be prefixed with _, so that future versions of myself and my friends may be less confused. Commit that.

I think Bezier class has some code that is no longer needed at all. There is the method create_waypoints and a test for it. I think that was a step along the path that led to the current L_Bezier. Let’s remove the code and test. They are recorded in Git. Tests run. Commit.

There are two methods: compute_length and estimate_length. I used those while experimenting with the partitioning, having read that as you partition a Bezier more finely, the sum of the lengths of the sides of the control quadrilateral approximates twice the length of the bezier. Do we have need of these functions? I have my doubts, but we’ll let them live.

There is also tangent_at, which returns two points which have something to do with the tangent at the point in question. We will certainly want that kind of information when we start operating vehicles along paths, so we’ll leave that as well, more as a marker for an idea than as a solution.

Everything else looks OK for now. We’ve made our workspace a bit better. Job well done, Janet, take a break.

Safe paths!