search the lab

Embedded Testing with Unity & CMock (by Mark)

Embedded Testing with Unity & CMock

James Grenning's book (features Unity)

Test Driven Development for Embedded C

Topics

Entries in config (1)

Wednesday
Apr202011

YAML Primer

We, the developers of CMock, Unity, and Ceedling, like YAML. YAML is a markup language... or... I guess it's not since it stands for YAML Ain't Markup Language. In any case it's a handy way to serialize configuration data in a human-readable and machine-readable format. So we use it to configure all those fancy-pants Ruby tools like generate_test_runner and CMock and Ceedling.

So if you're thinking you'd like to use these tools but are perplexed by what you see when you crack those .yml files open, maybe this tutorial will help. I'm not planning to show you everything YAML-related. Instead, I'm going to concentrate on the features we use the most to get you up and running. If you want to see the full YAML specification, go to yaml.org

The Basics

YAML is stored in text files with a .yml extension (or occasionally .yaml). These files care quite a bit about white space, how you indent things, and have all sorts of fun with dashes and semi-colons. Let's talk about those things. If you want to be a proper mad scientist, you'll just have to remember a few rules.

Here's a preview of what kind of stuff we're creating:

:cmock:
  :when_no_prototypes: :warn
  :enforce_strict_ordering: TRUE
  :unity_helper_path: "../unity/helpers/"
  :plugins:
    - :ignore
    - :cexception
    - :arrays
  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8

We use two spaces for each level of indent. That's not official YAML, but consistency is nice, right? Avoid using tabs. Don't put any white space at the end of a line. White space in the middle of a value is fine, but it's best if you use quotes then.

YAML nests hashes and arrays as deep as it wants. Let's try picking this apart to see what we can learn.

:cmock:

In the example above, the first key you find is the line that says :cmock: This tells us two things. The colon at the end says that :cmock is a key, and whatever is on the right or indented below is the value for that key. The colon on the left tells us that :cmock is a symbol. If you know anything about Ruby, this means something to you. If you don't just understand that we use symbols for almost all of the keys because it's way faster and it flags them as something special.

  :when_no_prototypes: :warn

Let's look at the second line in the example. Here we see another key value, right? We have a key :when_no_prototypes (we know it's a key because there is a : after it). It's value is :warn. Both :when_no_prototypes and :warn are symbols again. The key isn't a surprise, as mentioned. The value, however, is usually just a string (or number, or whatever). Sometimes, though, there are a limited number of options that apply. Often we use symbols for those situations too. These will be spelled out in the documentation.

  :enforce_strict_ordering: TRUE

Ah. Another key-value pair. This time, the value is TRUE. The Boolean types TRUE and FALSE are built into the language too. For those of you that spend a lot of time in C, you might expect to be able to use 0 and 1 to the same effect. You'd be surprised when you find that both of these evaluate to TRUE. So let's just stick with TRUE and FALSE.

  :unity_helper_path: "../unity/helpers/"

Here we see a key-value pair where the value is a string. There are a number of ways to add strings, but let's concentrate on two of them. One is to just write the text (making sure it doesn't start with a colon!). Another is to put double-quotes around it. We tend to use the latter for long strings or strings that contain spaces. Better safe than sorry, right?

  :plugins:
    - :ignore
    - :cexception
    - :arrays

Hey! Now we're getting fancier! Our key :plugins has a value that is an array! We know it's an array because it's a bunch of lines starting with a dash and a space. For this array, all the members are symbols. For some (like :includes) they could be text. We could go really crazy and have them be hashes or sub arrays or other crazy stuff... but we try to avoid that sort of thing so that you don't hate us.

  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8

Instead this is just about as fancy as we get. This is a key :treat_as whose value is another hash! Note that the other hash is indented further. Once you stop indenting, you're back out of this sub-hash. Here you can see that keys aren't always symbols. These ones are strings. Their values are also strings. This is because we don't know ahead of time all the options for either... instead it depends a lot on what the user has configured. Strings are the best for open-ended information.

Also, note that the white space between the key and value doesn't matter.

Fancy Stuff

Check this out:

:defines:
  :commmon: &common_defines
    - F_CPU=16000000UL
  :release:
    - *common_defines
  :test:
    - *common_defines
    - TEST
  :test_preprocess:
    - *common_defines
    - TEST

See that &common_defines ? That creates a name "common_defines" for everything that is in the value of :common. In this case, it's a one-element array containing the string "F_CPU=1600000UL". It could have been a huge array, a hash, or whatever.

So why name it?

We see almost immediately below. The *common_defines injects the contents of that named section where ever it occurs. So no we've duplicated the contents of :common under :release and :test: and :test_preprocess. How handy is that!?

There are some other tricks that we use too... but we'll get into some of those when we talk about Ceedling configuration specifically. The stuff in this primer will get you going.