For my apps dataFude for Simulator and Rex Tables I have few command line tools that run some end-to-end tests. I use MarkCodable to easily print reports to the console. In this short post I’m gonna show how MarkCodable cuts down on some of the turnaround time when I’m making tools.

MarkCodable

I wrote about MarkCodable previously when I released it: Introducing MarkCodable but I wanted to give you some more examples, just for fun.

MarkCodable is a codec conforming in part to the Codable protocol. Long story short, if you have a codable struct called User you can encode and decode it from markdown text like this:

| userID | name |
|--------|------|
|1       | John |
|2       | Gui  |

In this post, I’ll give you a use-case for encoding useful data as text to make it easier to use or export to other tools.

Printing Human-friendly Test Results

Rex Tables makes it really easy to read the swift test command output and be able to sort the test results by duration or filter by test status:

Some features disabled because only available to pro users

However, sometimes I want to go even further — for example, compare regressions in test durations across runs, potentially across different periods of time.

For that, I need a format that I can compare either visually with a diff viewer or ingest it in another app to compare the results.

So let’s use the regex from the screenshot above to parse a Swift test log and then use MarkCodable to export the results to a human-friendly format.

Parsing the Swift compiler output

Let’s first parse the output from swift test which looks somewhat like this:

Test Case '-[MarkCodableTests.MarkCoderEnums testCustomCodingEnums]' started.
Test Case '-[MarkCodableTests.MarkCoderEnums testCustomCodingEnums]' passed (0.000 seconds).
Test Case '-[MarkCodableTests.MarkCoderEnums testPlainEnumsThrowError]' started.
Test Case '-[MarkCodableTests.MarkCoderEnums testPlainEnumsThrowError]' passed (0.001 seconds).
...

First of all, we need a struct to store the interesting data in that input:

1
2
3
4
5
struct Test: Codable {
  let name: String
  let passed: Bool
  let duration: Double
}

Then, we grab the name, status and duration with just few lines of Swift, by using the fancy new regex syntax:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func tests(input: String) throws {

  let rex = /-\[\w+Tests.([\w\s]+)]' (passed|failed) \(([\d\.]+)/
  var tests = input.matches(of: rex)
    .map { match in Test(
      name: String(match.1),
      passed: match.2 == "passed",
      duration: Double(match.3)!
    )}
  tests.sort(by: { $0.duration > $1.duration })

}

Once we have the data in the tests array, we can also sort it, filter it, etc. etc. — in the code above I just sort the tests by duration with the slowest first.

Now comes the interesting part: Make a new MarkEncoder instance, and call encode(_:) to turn the data into markdown:

1
2
3
4
let encoder = MarkEncoder()
print(
  try encoder.encode(tests)
)

Which prints:

|duration|name                                                   |passed|
|--------|-------------------------------------------------------|------|
|0.094   |MarkCoderTests testDeterministicFormatting             |true  |
|0.006   |CombineMarkCodableTests testDecoding                   |true  |
|0.003   |MarkCoderLists testAllTheIntsInLists                   |true  |
|0.002   |MarkCoderEnums testRawValueEnums                       |true  |
|0.002   |MarkCoderErrors testDecodeUnexpectedInput              |true  |
|0.002   |MarkCoderTests testCodingOptionalKeysDefaultTypes      |true  |
...

Ta-da!

This is much more useful for me to see after running the test suite, I can diff it or feed it to another tool that plots graphs over time.

Where to go from here?

Check out MarkCodable’s readme file for few more examples: https://github.com/MarkCodable/MarkCodable. Additionally, the demo app included in the repo will give your some more ideas how to make the most of the package.

Finally, check out my app dataFude for Simulator to super-charge your debugging flow.

Thank you!

My DMs are open at https://twitter.com/icanzilb and https://mastodon.social/@icanzilb