Monday, February 15, 2016

Unit testing scrollable-area

Seemed like a good idea to start with a library rather than my whole project.

So I made a new git branch locally, then ran "munit ct" as in an earlier post.  All good.

I'd like the library to work with html5, but it will probably not test properly in a JavaScript-only vacuum, so I think I will steal some code from openfl to try to get html5 testing to work.

Now I'm facing new trouble: even the as3 step in test.hxml (the first platform...) is not working with the default example:

...scrollable-area>haxelib run munit t
Massive Unit - Copyright 2016 Massive Interactive. Version 2.1.2
   haxe -main TestMain -lib munit -lib hamcrest -cp src -cp test -swf-version 11
 -cmd lime\ build\ html5 -swf build/as3_test.swf
HaxeWrapper.hx:73: 'lime\' is not recognized as an internal or external command,

HaxeWrapper.hx:73: operable program or batch file.
HaxeWrapper.hx:73: Error: Command failed with error 1
Error: Error compiling hxml for as3
Target as3 ...scrollable-area\build\as3_test.s
wf

So, where is it getting this "lime\" from?  It looks like, because test.hxml includes this line with spaces in it, that somewhere along the line the spaces get escaped, and then another place along the line, a command interpreter, the escaping doesn't seem to count and the system chokes on the backslash.

Hmm.  Well, it could just be because it's old code, which I hadn't at first noticed (openfl-validation is deprecated.)  The more recent version of openfl's test.hxml looks a bit different.  That's because they're making use of their own "haxelib run openfl" script.  Where does that come from?  Haxelib's docs say to look for a file named "run.n".  It's in openfl's repo root, but it's precompiled.  Looking at its changelog, it seems RunScript.hx was modified at the same time, so probably run.n comes from there.  But the source doesn't even mention a "build" parameter...so how can test.hxml be calling "haxelib run openfl build html5" or "haxelib run openfl build flash" successfully?

I guess the final line passes on the parameters to haxelib.  But haxelib doesn't have a "build" parameter either.  So I'm still not quite sure how this functions, exactly.

Making scrollable areas swipe-able in HaxeFlixel

So, this weekend I went to add some basic code to scrollable-area to make it more touch-friendly, because using 20px-thick scrollbars on an actual phone is a PITA. At least on what I endearingly term "my low-budget crap phone."

Anyway, I ran into a problem: FlxG.swipes reports *any* touch event as a swipe. This isn't a problem in and of itself, as it's useful for custom code. The problem is that, even a distance=0 swipe, which is clearly a tap by anyone's algorithm, shows up in FlxG.swipes. This means we must implement some custom code to distinguish between swipes and taps. This may not seem terribly complicated at first, but...well, for example, read this wonderful resource to get an idea of the situation.

The next part of the problem, which is a little bit more of a problem, is that basing an algorithm on pixels, rather than a real-world unit of measurement like centimetres, means that the algorithm will work vastly differently on a "retina" device versus an older-generation, lower-density budget device like mine.  If I calibrate to retina devices, what would be a 0.75 cm "tap" there could be a 2.25 cm "swipe" on my crap phone...and I wouldn't want a nearly inch-long gesture to necessarily be interpreted as a tap.

So we should know the DPI.  Is this possible in HaxeFlixel?  Sure, if you want to include custom code for each platform like this library does.  A possible workaround, we really just need to start overriding Android Java code, and more or less just exposing the results of this one call, I think.

And if we want something cleaner?  Thankfully, there's something in the pipe.  Because my game is not far enough along just yet that I'm testing a lot on phones, I will opt to wait.  Also because the Java override technique would mean scrollable-area would go from being a fairly straightforward include (even though you have to set up your content offscreen for it, at the moment) to something much more integrated.  So, hopefully by the time I'm ready for swipeyness, Lime will be too.  :)

Thursday, February 11, 2016

Writing an actual test...not yet.

First, I had to copy this very important line to TestMain.hx, and its requisite import lines.

Now, I'm curious if MUnit was tested with FlashDevelop, because I'm getting something strange regarding code completion.  If I type "Assert." (even in the example file, whose test passes; right below a line also beginning with "Assert.") and wait to see which methods I can use, I get the following in the Output pane:


Error: Could not process argument #--macro
Class name must start with uppercase character

It does not pop up a list of methods. I get the same error if I hit F4 over the token Assert, which is coloured blue to show it's correctly imported. Hmm...so, that's problem 1.  Update: I found this handy info from here:

There’s just one last thing required to properly get up and running writing tests: adding the munit library to the project so that you can get code-completion on munit’s API. Open application.xml and add the line <haxelib name="munit" /> to the classpath, haxe libs section so it looks like:

<!-- classpath, haxe libs -->
<source path="src" />
<haxelib name="openfl" />
<haxelib name="actuate" />
<haxelib name="munit" />

VoilĂ —for realsies!
Except that adding the "munit" line as above--to a default project.xml I had to create (with current templates I have yet to come across application.xml so I believe that's outdated and/or openfl-specific rather than haxeflixel-specific)--only changed the error whenever I try autocompletion to:

Invalid commandline class : test.ExampleTest should be ExampleTest
./test/ExampleTest.hx:3: characters 7-31 : Type not found : massive.munit.util.Timer

That's on the import line.  If I remove that, then it can't find the Timer class referenced in the example.  Back to the drawing board...well, hopefully not that far back.

Meanwhile, if I try to write a test anyway, my characterAIAimsAndFires class was apparently mal-named, despite following the naming convention in the help, because the compiler complains (via macros, while I type code for the test) that the file name ought not to start with a lowercase letter.  So now it's CharacterAIAimsAndFires.  In its setup(), I call FlxG.switchState() to get my main world set up--just for now, until I properly decouple some of my code, I suppose.

Just adding the above and trying to compile, I get an error about TestMain, that it can't find FlxG.  That's because test.hxml also needs some lines like these:

-lib flixel
-lib flixel-addons
-lib scrollable-area
-lib nape
-lib advanced-layout

...or at least the first one, in this case. But I thought since I'll be testing pretty broadly, I should include whatever I have included in my Project.xml via haxelib directives.

 Turns out that's not enough:

HaxeWrapper.hx:73: C:\HaxeToolkit\haxe\lib\openfl/3,6,0/openfl/display/IBitmapDrawable.hx:17: characters 8-66 : Type not found : RenderSession
Error: Error compiling hxml for as3

Now, even if I add "-lib openfl" and "-lib lime", I still get this.  So that's problem 2, which is significantly bigger than problem 1...

I see that IBitmapDrawable refers to RenderSession via the package openfl._internal.renderer, but its entire contents are conditionally compiled "#if (!display && !flash)".  I guess either display or flash is defined true when compiling with munit, but not normally.

Likewise if I target cpp instead of flash (which my game compiles to without trouble) in the test.hxml file, I get this:

HaxeWrapper.hx:73: C:\HaxeToolkit\haxe\lib\flixel/git/flixel/FlxGame.hx:3: characters 7-27 : You cannot access the flash package while targeting cpp (for flash.display.Bitmap)
HaxeWrapper.hx:73: test/unit/TestMain.hx:1: characters 7-21 :     referenced here
Error: Error compiling hxml for cpp


Welcome to the new blog

I decided to put development posts in their own blog.  Here.

Unit testing my HaxeFlixel-based code

Since I'm still in the prototyping stage, I won't say that I should have added unit testing to my project earlier, necessarily.  TDDers may argue otherwise.  So be it.

Anyhoo, it's time to see how well MUnit and a HaxeFlixel project will play together.  Pretty well, I'm assuming, since HF itself uses MUnit for its unit tests.

So how do we get set up?  As the docs say, we install it and run it with haxelib.

The help displayed when running it gave some additional hints:

Massive Unit - Copyright 2016 Massive Interactive. Version 2.1.2
Usage: munit [subcommand] [options]
Type 'help ' for help on a specific subcommand.

Available commands:
   gen (g) : Generate a test runner based on classes in a test src directory
   run (r) : Runs a single unit test target and generates results
   test (t) : Updates, compiles and runs all targets from an hxml file
   create (ct) : Create test class
   config (c) : Modify default project specific settings for munit
   report (re) : Generate reports for CI environments and 3rd party tools


So, what I needed to do next was CD to my project's root, then run it again with the config (c) option.  It asked me for a bunch of paths, which were different from the standard HF demo template I had started with.  The last three, templates and coverage related, I left as null.

After that, it was time to create a new test, using the ct option.  I have a Character class already made, so I ran this:

haxelib run munit ct characterAIAimsAndFires -for Character


It seems I didn't need to run the gen (g) option at this point, because it had already made the required files.  Time to run the tests, then, with the test (t) option.

Oops, it complains, "Error: Library hamcrest is not installed."  I did what it said and installed hamcrest using haxelib.  Not sure why that wasn't an auto-installed dependency.  Maybe it only applies to certain platforms.

Trying again, I got a new error: "Error: Error compiling hxml for as2."  To get past this, I had to customize the auto-generated test.hxml by chopping out the as2, neko, and other sections I don't need.  Then, success!  I got a result on the command line saying the whole as3 platform passed ("PLATFORMS TESTED: 1, PASSED: 1, FAILED: 0, ERRORS: 0, TIME: 1.839") and also a browser window saying the 2 individual tests within it passed ("PASSED  Tests: 2  Passed: 2  Failed: 0 Errors: 0 Ignored: 0 Time: 0.255.")

Now to write an actual test...

Saturday, January 30, 2016

Scrollbars usable!

They're not complete yet, but they're quite usable for my use case now, as of this commit.  The other features will come later, unless of course you'd like to...


Fork me on GitHub
You're welcome, Reddit person. If that's not what you were looking for, I hope you find it somewhere (or make it) and share it :)

Huzzah! Scrollbars, drawn.

It's a bit unfortunate, but I had to scrap the idea of using FlxCamera.zoom because the calculations got too messy for me.  So instead I'm back to the original, which is that you lay out everything manually yourself.

What did I do about the merry-go-round?  Your layout code works with the scroll area code.  You tell it whether you intend to FIT_WIDTH or FIT_HEIGHT, and then it tells you whether you can expect a scrollbar, or, in the weird glitchy zone of the merry-go-round, whether to do the opposite of what you normally would, so that it's a bit less glitchy.

There are some off-by-one conversion areas which make the glitchy zone still a tiny bit glitchy, but overall I'm fairly happy with the result.  This is a spoiler (if not much of one) from a game whose name is still under wraps, but you'll hear about more soon.  These show FIT_HEIGHT, although probably you would typically want FIT_WIDTH, which is more like a web page with a vertical scroll bar.


Normal window

Getting too short (you can see the scrollbar almost running out; the "CC" trophy is the last one)

 Too short for a scrollbar, but too tall to max the height of everything (this would be where the merry-go-round would normally happen)
Short and wide enough that we can max the height of everything without a scrollbar
The code is not yet complete (only the drawing is done, not the input response) but it's available here:

https://github.com/IBwWG/HaxeFlixelScrollableArea

Input response will be next.  For now, you can plunk those .hx files in your own project's source/flixel/addons subdirectory and create a FlxScrollableArea in your state's create() function.  This was made and tested with a non-standard scale mode in mind, where you want full control over resizing your content, so the next step is to override onResize() in your state, and there set the .viewPort property of your FlxScrollableArea, re-layout your content based on its .bestMode/horizontalScrollbarHeight/verticalScrollbarWidth properties, then finally set the .content property to match your newly-laid-out content.