testing

How iKML Was Tested With Hand-Written Exports, Random Round Trips and Fuzzing

iKML makes two kinds of promise. When it reads a map file, it should report what’s really wrong with it, in a place you can find. When it writes a file, the shapes should come out where they went in. The tests check both, and this page says how.

Every number here comes from the test/results folder of the code repository. A script runs all the tests and writes those files, and the site quotes only what they say.

The recorded test run and the four fuzz targets

Files Written by Hand

The test files are written by hand, in the layouts that real programs produce: a Google Earth Pro export, a Google My Maps export, an ArcGIS export, a QGIS export, a DJI drone mission, a GPS track with times, a photo tour KMZ. Nine of them are good files, and every one must open with no errors.

Twenty-two are broken in one specific way each: latitude and longitude swapped, one placemark typed in backwards among correct ones, a polygon ring left open, a KMZ pointing at pictures on someone’s C drive, a point at 0,0 off the coast of Africa, an old KML 2.1 namespace, a GeoJSON file with a coordinate system iKML won’t guess at, a KMZ carrying files named to unpack outside their folder. For each, the test names the issue codes that must appear. All 22 were reported with their codes, and the recorded report for every file is in the results folder.

Where an issue is reported also gets tested. In the file with one backwards placemark, the swapped coordinates must be pinned to that placemark by name and to line 8, because “somewhere in this file” isn’t much help in a file of a thousand points.

Random Round Trips

Hand-written files only cover the cases someone thought of, so a second test makes up KML files at random: points, lines, polygons with holes, collections of shapes, GPS tracks with times, folders inside folders, names with characters that need escaping, extended data fields, and numbers written every way a program might write them, from 0 to 1.234e+02. Each goes KML to GeoJSON to KML again, and the test checks that every position survives. Going from GeoJSON back to KML must be exact, every coordinate the same number in the same order. The recorded run made 3,000 random files, and every one came through.

Exact means exact. A separate test writes 200,000 random floating-point numbers the way iKML writes coordinates and reads each back, and each must be the same number to the last bit. A converter that rounds to six decimals moves points by up to about 5 centimetres, which matters for a survey plan.

The cleaning step has its own test: clean a file, then clean the result again, and the second pass must change nothing. Every test file goes through this twice, once with the default fixes and once with all of them.

Fuzzing and Zip Bombs

The readers get files from anywhere, so they must never crash or hang. Go’s fuzzer threw mangled input at each reader for a minute: 571,114 KML inputs, 402,292 KMZ inputs, 774,565 GeoJSON inputs and 1,379,197 coordinate strings. None crashed.

A KMZ is a zip file, and a zip can be built to unpack to far more than it looks. iKML stops at 512 MiB unpacked and refuses entries that claim a compression ratio no real map file has. The tests build both kinds of bomb and check that iKML reports kmz-too-large and reads nothing.

A zip entry’s name can also point outside the folder it’s unzipped into: ../../.bashrc, /etc/cron.d/update, C:\Windows\System32\update.dll. No mapping program writes names like these, and some unzip tools would put the file wherever the name points. The independent review of 0.1.0 found that iKML only called such files unused, and that clean copied them into the cleaned KMZ. Since 0.1.1 they’re an error, kmz-unsafe-path, cleaned and converted copies leave them out, and iKML refuses to write such a name into any KMZ. A second review found two gaps in that fix: a second copy of the main doc.kml slipped through, and a ../evil.kml could be picked as the main document. 0.1.2 closes both. The 22nd broken test file carries four unsafe names and two duplicates, and a test checks that its cleaned copy holds exactly the real document and its one icon.

In the Browser

The inspector on this site runs the same Go code, compiled to WebAssembly. A smoke test loads the compiled file in Node and runs it on real test files: it must find the swapped coordinates, report no errors on a good file, convert a My Maps export and close an open ring. It also feeds it garbage, which must give an error rather than a crash.

That last check exists because of a bug found during testing. A test that drove the inspector’s page found that a file handed over in one particular way made the Go side panic, and a panic in WebAssembly stops the whole program, so the inspector went dead until the page was reloaded. The fix converts the file safely and catches any panic, so a bad file now gives an error message and the next file still works.

What Writing the Use Cases Found

Writing the DJI mission use case turned up a misleading note. iKML reported the mission’s waylines.wpml file as unused, since nothing in the KML refers to it. But the drone reads that file by name, so cleaning it out with --drop-unused would have broken the mission. iKML now knows that file, and the test for the DJI file checks that it isn’t reported.

Speed

On the 2-CPU machine that recorded the results, a made-up file of 1,000 tracks with 1,000,000 positions in all took 0.98 seconds to check as KML (27 MB) and 1.31 seconds as KMZ, and 3.78 seconds to convert to GeoJSON, best of three runs each. The most memory any run used was 380 MB.