Before reading the code for each command, we’ll start by looking at the command’s tests. In the spirit of “tests as executable documentation”, reading the tests first should give us a sense of what the expected behavior is. The test file for the rbenv
command is located here.
The BATS
test framework
The first line of code is:
#!/usr/bin/env bats
This is a shebang, which we’ve seen before. But importantly, it’s not a Bash shebang. Instead, it’s a bats
shebang. BATS is a test-runner program that Sam Stephenson (the original author of RBENV) wrote, and it’s used here as RBENV’s test framework. But it’s not RBENV-specific. In theory, you could use it to test any shell script.
The bats
repo is archived, meaning there won’t be further updates to it, but I still want to see if the code works and can be used to run tests in the rbenv
repo. I’d be surprised if it didn’t, since RBENV is still maintained and therefore still needs a way to run its own tests.
Installing bats
To run these tests, we’ll need to install bats
first. There are installation instructions on the Github repo’s README file, but I also discovered that there is a Homebrew package for BATS, so I used that:
$ brew install bats
You’ll know the installation was successful if you can run which bats
and a filepath appears, like this:
$ which bats
/usr/local/bin/bats
If you followed the “Installing Bats from source” instructions on the Github repo page, your filepath may look different from mine.
Experiment: running the BATS tests
Once that’s done, we can navigate to the home directory of our cloned RBENV codebase, and run the following:
$ cd ~/Workspace/OpenSource/rbenv/test/
$ bats ./test/rbenv.bats
✓ blank invocation
✓ invalid command
✓ default RBENV_ROOT
✓ inherited RBENV_ROOT
✓ default RBENV_DIR
✓ inherited RBENV_DIR
✓ invalid RBENV_DIR
✓ adds its own libexec to PATH
✓ adds plugin bin dirs to PATH
✓ RBENV_HOOK_PATH preserves value from environment
✓ RBENV_HOOK_PATH includes rbenv built-in plugins
11 tests, 0 failures
They all pass, as we’d expect since we haven’t (yet) done anything which breaks the code.
So now that these specs are running, what do they actually do? Reading the tests for a piece of code you’re studying is often a great source of documentation. And the BATS repo has helpful documentation on the commands it offers and the API it exposes.
Loading helper code
Returning to reading the test file, let’s look at the next line of code:
load test_helper
This load
function is defined in this block of code in the bats
repo.
Examining this function’s internals is beyond the scope of this guide (although it would make a great exercise for the reader). For now, let’s just say that the load
function does what it says on the tin- it loads a given file so that its contents are available to the test suite we’re looking at.
In this line of code, we’re loading a helper file called test_helper
, which lives here. Loading test_helper
does lots of things for us that help our tests run, such as:
- updating the value of
PATH
to include therbenv
commands that we want to test. export
ing the environment variables that we’ll need for those commands to run, such asRBENV_ROOT
,RBENV_HOOK_PATH
,HOME
, andRBENV_TEST_DIR
.- giving us access to helper functions that let us run those commands and assert that the results succeeded or failed. For example:
teardown
- cleans up the effects of our tests.flunk
- causes the test to exit with a non-zero exit code (i.e. the test fails), along with printing an error message.assert_success
- checks that the last command completed successfully, and (optionally) that the expected output of the command matched the actual output.assert_failure
- the opposite ofassert_success
. Checks that the last command did not complete successfully. This is useful in testing what happens when a command is used improperly.assert_equal
- checks that two values match.assert_output
- checks that the output of the most-recently-run command matches our expectations.assert_line
- checks that a given line of expected output was contained somewhere in the actual output. You can optionally pass a specific line number at which you expect the output to contain your string.refute_line
- There are two ways to use this function:- You pass it a string, to check that string is not present in the most recent output. Ex.-
refute_line "I hope this line is not present in the output"
- You pass an integer, to check that the number of lines in the output was less than that number. Ex.-
refute_line 5
.
- You pass it a string, to check that string is not present in the most recent output. Ex.-
assert
- Checks that the condition you pass is truthy. For example,assert [ 5 -lt 6 ]
.path_without
- This is only useful to test a few very specific situations, so we won’t worry about this for now.create_hook
- creates a fake hook that RBENV will subsequently register. This is only useful to test a few very specific situations, so we won’t worry about this for now.
The next block of code is also our first test. It starts with:
@test "blank invocation" {
...
}
Annotations
The first thing I notice is the @test
snippet. I’m not sure what other developers would call this, but I would call it an “annotation”, because similar syntax exists in the Java community, and they also refer to these as annotations.
In BATS, annotations are used as metadata, and they help identify which code represents tests that should be run. If we search the BATS codebase for the string @test
and look through the results, eventually we find this line of code. This is a regular expression (or a regex for short). If you aren’t familiar with regexes, they’re a very powerful tool for finding and parsing strings. See here for more information.
This isn’t a walk-through of the BATS codebase so I want to keep this part short, but essentially what’s happening here is we’re providing a pattern for Bash to use when searching for lines of code. Bash will read each line of code in a test file (for example, test/rbenv.bats
) and see if it matches the pattern @test
. If it does, we know we’ve found a test, and we’ll run the code we find.
Back to our test block:
@test "blank invocation" {
run rbenv
assert_failure
assert_line 0 "$(rbenv---version)"
}
Here we’re verifying that an attempt to run rbenv
without any arguments will fail. The steps in this test are:
- We use the BATS
run
command to execute therbenv
command without any arguments or flags.run
populates certain variables likeoutput
,status
, andlines
.- The helper functions we mentioned earlier (such as
assert_failure
, which is used in this test) use these variables to determine whether to pass or fail a given test.
- Here,
assert_failure
checks to make sure the last command which was run (i.e.run rbenv
) had a non-zero exit code. - If the command failed, the test passes. If command succeeded, the test fails.
I would call this a “sad-path test”. When building our testing harness, we not only want to test what happens when things go right (i.e. the “happy-path”), but also what happens when things go wrong. This gives us confidence that our code will work as expected in all scenarios, not just the good ones.
We can also test edge cases which are neither happy paths nor sad paths. I’ve heard these referred to as “alternate paths”. They represent uses of a command which aren’t exactly the primary use case, but aren’t exactly “failures” either.
This test implies that running the command rbenv
by itself, with no arguments, is considered “sad-path”. The rbenv
command needs you to pass it the name of another command, before it can do anything. For example, if you give it the versions
command by running rbenv versions
, RBENV knows that you want to see a list of all the Ruby versions which are installed on your system. But by itself, the rbenv
command does nothing, and attempting to run it by itself would be considered a user error.
There is also a 2nd assertion below the first one:
assert_line 0 "$(rbenv---version)"
This assertion states that the 1st line of the printed output should be equal to the output of the rbenv --version
command (the indexing here is 0-based). So when the user runs rbenv
without any arguments, the first line of printed output they should see is the version number for their RBENV installation. I try this on my machine, and it works as expected:
$ rbenv
rbenv 1.2.0-16-gc4395e5
Usage: rbenv <command> [<args>]
Some useful rbenv commands are:
commands List all available rbenv commands
local Set or show the local application-specific Ruby version
global Set or show the global Ruby version
shell Set or show the shell-specific Ruby version
install Install a Ruby version using ruby-build
uninstall Uninstall a specific Ruby version
rehash Rehash rbenv shims (run this after installing executables)
version Show the current Ruby version and its origin
versions List installed Ruby versions
which Display the full path to an executable
whence List all Ruby versions that contain the given executable
See `rbenv help <command>' for information on a specific command.
For full documentation, see: https://github.com/rbenv/rbenv#readme
Here we can see that the first line of printed output is rbenv 1.2.0-16-gc4395e5
.
Command Substitution
The "$( ... )"
syntax in our 2nd assertion above is known as command substitution, and will come up a lot in our walk-through. It’s similar to parameter expansion, in that it resolves to whatever is inside the left and right delimiters (here, parentheses instead of curly braces). But instead of outputting a variable along with some optional modifiers (as with parameter expansion), it outputs the result of running the command inside the parens. Let’s do a few quick experiments here.
Experiment: command substitution
Directly in my terminal, I run the following:
$ current_dir="$(pwd)"
$ echo "$current_dir"
/Users/myusername/Workspace/OpenSource/
$ current_dir_contents="$(ls "$current_dir" )"
$ echo "$current_dir_contents"
Make
bats
impostorsguides.github.io
rbenv
rubinius
...
Here we create two shell variables:
- one named
current_dir
, containing the output of thepwd
command. - the other named
current_dir_contents
, containing the contents of the directory whose name is stored in thecurrent_dir
variable (a.k.a. some directories I have in my~/Workspace/OpenSource
directory).
Now that we’ve finished reading our first BATS test, let’s write one of our own.
Experiment: writing our own BATS test
I create a file named bar.bash
, inside the same test/
folder as rbenv.bats
, which defines a shell function named my_echo
:
#!/usr/bin/env bash
my_echo() {
echo "Hi"
}
I create another file called foo.bats
, also in the same directory, with the following content:
#!/usr/bin/env bats
load bar
@test "prints 'Hey yourself! when it's supposed to" {
run my_echo "Hey"
assert_success "Hey yourself!"
}
When I try to run it with the bats
command, I get:
$ bats foo.bats
✗ prints 'Hey yourself! when it's supposed to
(in test file foo.bats, line 8)
`assert_success "Hey yourself!"' failed with status 127
/var/folders/tn/wks_g5zj6sv_6hh0lk6_6gl80000gp/T/bats.85070.src: line 8: assert_success: command not found
1 test, 1 failure
The thing to zero in on here is assert_success: command not found
.
We’re getting this error because we’re missing the assert_success
command. That’s because assert_success
is a test-helper
function, not a BATS function. To access this command, we need to load test_helper
, just like the regular test files do.
I update the test file to look like the following:
#!/usr/bin/env bats
load test_helper
load bar
@test "prints 'Hey yourself! when it's supposed to" {
run my_echo "Hey"
assert_success "Hey yourself!"
}
I then run it again:
$ bats foo.bats
✗ prints 'Hey yourself! when it's supposed to
(from function `assert_equal' in file test_helper.bash, line 65,
from function `assert_output' in file test_helper.bash, line 74,
from function `assert_success' in file test_helper.bash, line 49,
in test file foo.bats, line 8)
`assert_success "Hey yourself!"' failed
expected: Hey yourself!
actual: Hi
1 test, 1 failure
Now we see the following error:
expected: Hey yourself!
actual: Hi
We’ve run the test, and verified that it fails for the correct reason (unexpected output). This gives us confidence that our test fails when it’s supposed to.
We can then write the functionality that we expect to make the test pass, and re-run the test. In our case, that just means updating the my_echo
function inside bar.bash
to actually print what it’s supposed to:
#!/usr/bin/env bash
my_echo() {
echo "Hey yourself!"
}
When I re-run the test, we get:
$ bats test/foo.bats
✓ prints 'Hey yourself! when it's supposed to
1 test, 0 failures
That’s an example of testing a “happy-path” scenario.
Next, I want to test a “sad-path” scenario. I write a 2nd test in my .bats
file to check that it fails when I pass an invalid argument:
#!/usr/bin/env bats
load test_helper
load bar
@test "prints 'Hey yourself! when it's supposed to" {
run my_echo "Hey"
assert_success "Hey yourself!"
}
@test "fails if the input is not 'Hey'" {
run my_echo "Ahoy"
assert_failure "I don't understand 'Ahoy'"
}
When I re-run it:
$ bats foo.bats
✓ prints 'Hey yourself! when it's supposed to
✗ fails if the input is not 'Hey'
(from function `flunk' in file test_helper.bash, line 42,
from function `assert_failure' in file test_helper.bash, line 55,
in test file foo.bats, line 13)
`assert_failure "I don't understand 'Ahoy'"' failed
expected failed exit status
2 tests, 1 failure
We see a ✗
character instead of a ✓
character next to the test description. We also see which assertion failed:
`assert_failure "I don't understand 'Ahoy'"' failed
Lastly, we see expected failed exit status
, which tells us why assert_failure
failed.
To make this “sad-path” test pass, we can update our function to the following:
#!/usr/bin/env bash
my_echo() {
if [ "$1" != "Hey" ]; then
echo "I don't understand '$1'"
exit 1;
fi
echo "Hey yourself!"
}
Now, when we re-run the test, we see:
$ bats foo.bats
✓ prints 'Hey yourself! when it's supposed to
✓ fails if the input is not 'Hey'
2 tests, 0 failures
Great, that’s a (very preliminary) introduction to writing our own BATS test. We’ll see lots more BATS syntax in the subsequent tests.
Aside- Test-Driven Development
In our first test, we expected to see “Hey yourself!” printed to the screen, but we actually saw “Hi”. I wrote the test this way intentionally, to demonstrate the concept of “Red-Green-Refactor”, which comes from the world of test-driven development (or ‘TDD’ for short). We start by writing a test for the functionality that we want to test, before we write the functionality itself.
If we really wanted to be strict about our TDD practice, our sequence of steps would have been even more granular:
- We run the test without the
load bar
line, and see a failure related to a missingmy_echo
function. - We run the test with an empty implementation of
my_echo
(i.e. no function body), and see a failure related to empty output ofmy_echo
. - We run the test with an incorrect implementation of
my_echo
, and see a failure related to incorrect output ofmy_echo
. - Then and only then, we write the correct implementation of
my_echo
and see our test pass.
At first, writing tests like this is slower than just writing the code itself and forgetting about tests. But over time, as an application starts to grow in size, it actually becomes faster to use TDD. This is because you need to spend more and more time ensuring that the features you just finished writing didn’t break previous features.
Without automated tests, at a certain point you end up giving up manual tests entirely, in favor of relying on your user base to report errors to you. That’s not a great user experience, and I’m comfortable claiming that it’s not great engineering practice either.
Next test:
@test "invalid command" {
run rbenv does-not-exist
assert_failure
assert_output "rbenv: no such command \`does-not-exist'"
}
This test covers the sad-path case of when a user tries to run a command that RBENV doesn’t recognize. We do the following:
- run a fake RBENV command called
rbenv does-not-exist
, - call our
assert_failure
helper function to ensure that the previous command failed, and - check that the output which was printed to
stdout
contained the linerbenv: no such command 'does-not-exist'
.
I try this on my machine as well:
$ rbenv foo
rbenv: no such command `foo'
Looks good!
Next test is:
@test "default RBENV_ROOT" {
RBENV_ROOT="" HOME=/home/mislav run rbenv root
assert_success
assert_output "/home/mislav/.rbenv"
}
Here we call root
(a real RBENV command) because we want to test how rbenv
responds to a known-valid command (unlike the previous test, which tested a known-invalid command). We picked the root
command in particular because its implementation is only a single line of code, so it allows us to accomplish this goal with minimal risk.
The test does the following:
- passes an empty value for
RBENV_ROOT
and an arbitrary but unsurprising value forHOME
as environment variables, and - asserts that:
- the command succeeded, and
- that the printed output included the
.rbenv/
directory, prepended with the value we set forHOME
.
Judging by the environment variables (RBENV_ROOT
and HOME
) which are passed to the run rbenv root
command, this test appears to cover the behavior beginning at this line of code. But rather than skip ahead to analyze what this line of code does, let’s punt on that until we look at the code for the command itself.
Next test:
@test "inherited RBENV_ROOT" {
RBENV_ROOT=/opt/rbenv run rbenv root
assert_success
assert_output "/opt/rbenv"
}
This test is similar to the previous test, except this time we’re testing the else
branch instead of the if
branch. We set a non-empty value for RBENV_ROOT
and assert that that value is used as the output for the root
command. We leave HOME
blank this time, because HOME
is only needed to help construct RBENV_ROOT
if RBENV_ROOT
doesn’t already exist.
Next test:
@test "default RBENV_DIR" {
run rbenv echo RBENV_DIR
assert_output "$(pwd)"
}
Here we appear to be testing this block of code. We assert that, if no prior value has been set for RBENV_DIR
, we set it equal to the value of the shell’s PWD
environment variable (which is also the same as the output of the pwd
shell command, which is what “$(pwd)” resolves to here).
Note that we won’t be able to run rbenv echo
in our shell unless we manually update our PATH
environment variable to include RBENV’s test/libexec/
directory. The test_helper
file does this for us when we run our test, but if we’re not running a test then we have to do this ourselves.
Next test:
@test "inherited RBENV_DIR" {
dir="${BATS_TMPDIR}/myproject"
mkdir -p "$dir"
RBENV_DIR="$dir" run rbenv echo RBENV_DIR
assert_output "$dir"
}
This test covers the same block of code as the previous test, except this time we’re testing the else
branch instead of the if
branch:
- We create a variable named
dir
and set it equal toBATS_TMPDIR
with “/myproject” appended to the end. - We then create a directory whose name is the value of our
dir
variable.- The
-p
flag ensures that any intermediate directories in between our current one and/myproject
are also created, if they don’t already exist.
- The
- We set the
RBENV_DIR
env var equal to this directory. - We then run
rbenv echo RBENV_DIR
. - Lastly, we assert that the command printed the value that we specified for the
RBENV_DIR
env var, since that’s the env var that we passed torbenv echo
.- In other words, we assert that the block of code that we’re testing didn’t modify the value of
RBENV_DIR
in any way.
- In other words, we assert that the block of code that we’re testing didn’t modify the value of
Next test:
@test "invalid RBENV_DIR" {
dir="${BATS_TMPDIR}/does-not-exist"
assert [ ! -d "$dir" ]
RBENV_DIR="$dir" run rbenv echo RBENV_DIR
assert_failure
assert_output "rbenv: cannot change working directory to \`$dir'"
}
Here we’re testing the same block of logic as the last test, but this time we’re testing a different edge case.
Inside that block’s else
branch, we try to cd
into the directory specified by RBENV_DIR
. As of this point, the value of RBENV_DIR
is not known to be a valid directory, so this may or may not work.
If it does work, we reset RBENV_DIR
to be equal to our current directory. But if it fails, we abort and print the error message rbenv: cannot change working directory to '$dir'
. That’s the edge case we’re testing- when the navigation into the specified directory fails, the command fails and the expected error message is printed to stderr
.
Why do we reset RBENV_DIR
? We’ll analyze that in depth later, but the short explanation is that we want to remove and possible ..
syntax from it, i.e. we want to “canonicalize” it.
Next test:
@test "adds its own libexec to PATH" {
run rbenv echo "PATH"
assert_success "${BATS_TEST_DIRNAME%/*}/libexec:$PATH"
}
After some digging, I discovered that this test covers this line of code. We can prove this by running the test with this line of code in-place, and then re-running it with the code commented-out, observing that the test fails when the line is commented out.
We will dive into what this line of code does when we start reading the code for rbenv
itself. But from the description of this test (adds its own libexec to PATH
), we can deduce that the libexec/
folder contains commands that we’ll want to execute from the terminal.
Remember that PATH
is the list of folders which UNIX checks when we give it a command to execute. By adding more folders to PATH
(such as libexec/
), we’ll have access to more commands.
Next test is:
@test "adds plugin bin dirs to PATH" {
mkdir -p "$RBENV_ROOT"/plugins/ruby-build/bin
mkdir -p "$RBENV_ROOT"/plugins/rbenv-each/bin
run rbenv echo -F: "PATH"
assert_success
assert_line 0 "${BATS_TEST_DIRNAME%/*}/libexec"
assert_line 1 "${RBENV_ROOT}/plugins/ruby-build/bin"
assert_line 2 "${RBENV_ROOT}/plugins/rbenv-each/bin"
}
This test covers the 4-line block of code starting here. Inside the test, we create two directories, one named ruby-build
and one named rbenv-each
. From the name of the directory ( plugins/
), we can assume that this is where RBENV plugins are stored, so we can deduce that creating these two sub-directories means we’re creating two RBENV plugins for the purposes of our test. Since there’s no additional setup (such as creating files inside of those directories), we can assume that’s all the setup required, in order to make our test think these plugins actually exist.
We then call run rbenv echo -F: "PATH"
, which tells RBENV to echo
$PATH
. We pass the -F:
flag to tell rbenv echo
to use “:” as a separator. This will cause each item in $PATH
to print on its own line. We could have called run rbenv echo "PATH"
without the -F:
flag, but then our entire PATH
will print on one line, which will make it really hard to call assert_line 0
etc. further down in the code.
Lastly, we assert that the command was successful, and that the first item in $PATH
is the value prepended to $PATH
here, and that the next two items in $PATH
are the paths to the two plugins that we “installed” when we ran mkdir
twice at the start of our test.
Next test:
@test "RBENV_HOOK_PATH preserves value from environment" {
RBENV_HOOK_PATH=/my/hook/path:/other/hooks run rbenv echo -F: "RBENV_HOOK_PATH"
assert_success
assert_line 0 "/my/hook/path"
assert_line 1 "/other/hooks"
assert_line 2 "${RBENV_ROOT}/rbenv.d"
}
This test covers this line of code. It takes any previously-set value of RBENV_HOOK_PATH
, and adds ${RBENV_ROOT}/rbenv.d
to the end of that value.
To test this, we set the value of RBENV_HOOK_PATH
so that it includes two hard-coded paths, /my/hook/path
and /other/hooks
. We then run rbenv echo
on this env var, again telling rbenv echo
to use :
as a separator via the -F
flag. We assert that the command was successful and that our two paths are printed, followed by ${RBENV_ROOT}/rbenv.d
.
We can increase our confidence that this test covers the above line of code by simply commenting out that line in the rbenv
command, and seeing whether the test fails:
# RBENV_HOOK_PATH="${RBENV_HOOK_PATH}:${RBENV_ROOT}/rbenv.d"
When I re-run the test, I get:
$ bats rbenv.bats
✓ blank invocation
✓ invalid command
✓ default RBENV_ROOT
✓ inherited RBENV_ROOT
✓ default RBENV_DIR
✓ inherited RBENV_DIR
✓ invalid RBENV_DIR
✓ adds its own libexec to PATH
✓ adds plugin bin dirs to PATH
✗ RBENV_HOOK_PATH preserves value from environment
(from function `assert_equal' in file test_helper.bash, line 65,
from function `assert_line' in file test_helper.bash, line 79,
in test file rbenv.bats, line 69)
`assert_line 2 "${RBENV_ROOT}/rbenv.d"' failed
expected: TEST_DIR/root/rbenv.d
actual: /Users/myusername/Workspace/OpenSource/rbenv/rbenv.d
✗ RBENV_HOOK_PATH includes rbenv built-in plugins
(from function `assert_equal' in file test_helper.bash, line 65,
from function `assert_output' in file test_helper.bash, line 74,
from function `assert_success' in file test_helper.bash, line 49,
in test file rbenv.bats, line 75)
`assert_success "${RBENV_ROOT}/rbenv.d:${BATS_TEST_DIRNAME%/*}/rbenv.d:/usr/local/etc/rbenv.d:/etc/rbenv.d:/usr/lib/rbenv/hooks"' failed
expected: TEST_DIR/root/rbenv.d:/Users/myusername/Workspace/OpenSource/rbenv/rbenv.d:/usr/local/etc/rbenv.d:/etc/rbenv.d:/usr/lib/rbenv/hooks
actual: /Users/myusername/Workspace/OpenSource/rbenv/rbenv.d:/usr/local/etc/rbenv.d:/etc/rbenv.d:/usr/lib/rbenv/hooks
11 tests, 2 failures
The above 2 failures include the test we’re currently examining as well as the next one, meaning that test also covers this same line of code.
Last test:
@test "RBENV_HOOK_PATH includes rbenv built-in plugins" {
unset RBENV_HOOK_PATH
run rbenv echo "RBENV_HOOK_PATH"
assert_success "${RBENV_ROOT}/rbenv.d:${BATS_TEST_DIRNAME%/*}/rbenv.d:/usr/local/etc/rbenv.d:/etc/rbenv.d:/usr/lib/rbenv/hooks"
}
Here we do the following:
- unset
RBENV_HOOK_PATH
, - run the command,
- assert that it was successful, and
- assert that the printed output references the following directories, delimited by the
:
character:${RBENV_ROOT}/rbenv.d
${BATS_TEST_DIRNAME%/*}/rbenv.d
/usr/local/etc/rbenv.d
/etc/rbenv.d
/usr/lib/rbenv/hooks
This test covers the block of code here. We can see that the order of the above directories matches the order in which they’re added to RBENV_HOOK_PATH
by the code:
- This line of code adds
${RBENV_ROOT}/rbenv.d
to the front ofRBENV_HOOK_PATH
. It would also add any previously-set value ofRBENV_HOOK_PATH
before${RBENV_ROOT}/rbenv.d
if we had previously set such a value, but we didn’t in this test. - This block of code adds
${BATS_TEST_DIRNAME%/*}/rbenv.d
toRBENV_HOOK_PATH
. - Lastly, this block of code adds
/usr/local/etc/rbenv.d
,/etc/rbenv.d
, and/usr/lib/rbenv/hooks
toRBENV_HOOK_PATH
.
That’s all for the rbenv
command’s tests. Let’s move onto the code.