You will no doubt have used to go build, go run type tools because you've been
writing go code but it's worth doing a little review of the layout of
the go workspace because sometimes it can be a little bit confusing. And
also, there are times when you really don't need to use the entire go
workspace layout
If we take another little program I've written here. It is very simple.
It prints out, hello world and a couple of different ways and imports, a
couple of modules. I've created a couple of packages one called EE Cummings, after the
poet, who wrote everything in lower case and one called shouter which does everything in
uppercase. And if we take a look at the shelter module,
which is
Here, it's very simple. It just takes a string and upper
cases it. And if we look at the ear, Cummings module, what it
does, is it lower case is something and then removes all the punctuation from
it. So, very simple layout. Now, if we take a look here in
the source code for this,
we will find that there is a source directory and within the source
directory I've got three directories, you Cummings program
name and shelter and of course program name you could use whatever you want as a name for your
program I just made it clear by saying program name and it's in that program named
directory that there is a file which
contains package means. So that's the that name program. They will
be the one used for the executable for your program and the
other files in there.
Packages. So for example, within your Cummings there's small dog, go. And if we take a
look at small dog go, it says e comings of the top. So that's that's
all the things that are part of. That package are in the directory. With the same name,
the source of all this is this
current directory. I'm in here.
And if we look at gopath, I've set it to be that directory
and the go path is essentially the root of your workspace where you're
going to be writing, go code. And it's the one thing you probably need to
set because you need to it is used by all the go build tools to then find
other bits of code and underneath gopath it expects to
find a source directory and within the source directory expects to find other
directories which are either programs you're going to build or packages. In this case we've got
one program
And those packages, if you think about, I'd go path, followed
by sauce, followed by something, the
something. Let's suppose we lists a shelter. That last part will be
what you use when you import that particular
package. So you I just say shouter and it knows to look in gopath Source,
shelter. And if you have a more complex structure, obviously, you can have many slashes in
there, multiple directories. And so we can just
We can build this program by saying, I want you to build program name that
it will look in Source program name. And hopefully there's a main dog go
and it would create its created a simple program there, which I can build, so which
runs like that. So, one of the things you're expected to do, when you write go
code, is to write tests for your packages and I hadn't actually written
any tests for this shelter thing. Although, it's rather simple, but it'll be good to write a
unit test. Then we can run it. And obviously, the way in which unit test working
go, is you have if
I'll typically with the same name with underscore test, appended to it and that way the
go test is able to find the tests and execute them. So, what I've done
is, I've actually put together a little test package here, it is called shout underscore test.
It's going to test this function. This shelter package,
it's within the same package. And what I've done is I've written myself a little
function which can test that shout function of
imported the testing package and then all I'm doing is I have a test string and I'm
Using a regular expression to look for any lowercase characters or to make sure there's nothing lower case
left. And and I'm shouting a string and the shout is within
the shelter package, so I don't know, chefs a sheltered or shout because I'm inside the package.
So this should be all uppercase. And then I just use matched ring
and if we just check that, there's a lot of different functions of the
Radix package, some of them work on bite, slices on the work on strings. This particular
one works on a string, so this is quite a simple one. It returns
a Boolean if it matches.
So in the case that we find some lower case we should say, oops, I should say
found no case we found some lowercase and then to run that we can just
go go test and then the name of the package and it runs it and
luckily it all works correctly. The
test tool will look for the test files
within the packages that are in Go part. So you can specify
them on the go test command line and it will then run through all of them. So, typically you'd
have a whole load of packages here,
Which you would run through. So that works great for simple unit test. But one of the classic
questions is how I tested everything and the go test tool that actually
has a - cover option, which gives you coverage information about your
unit test. So excuse the unit test and then can tell you, whether it actually touched all of
the functions on all the lines in your code. And just to demonstrate
that, I'm going to leave my unit tests as it is. But I've added an extra function called shout
really loud. And what it does. Is it shouts? And put an exclamation mark on the end to make it
more impressive and I'm really
As for it. Now if I then go in and I run go test -
cover on the shelter, what is going to do is run all the unit tests and give me a report.
So I do that. You can see only 50% of the statements have been covered. So it's showing me that
I, you know, I've missed out some information here about this function here
has not been tested and I should, I should write some test for it. So that coverage
output on test is nice from a reporting perspective, but if you want, you want to dig in
and see what has not been tested, and there are some extra ways to do that. So,
If you look at this go test, I got here. I've got go test and I've now instead of saying just - cover, I've said
- cover profile and given a file name. Well, that's going to do is it's going to
write out a file containing information about the coverage. So I'm going to run
that it's going to do the same thing in the same reporting output but I've now got a file called coverage
dot out and I can do a couple of things with that. There's a go tool
called cover.
And that will if I just run it like that with - Funk passing in the name of that file, it's
going to tell me which functions I have not got coverage for. You can see obviously that
shout really loud as God 0% and if I wasn't obvious, I could
dig in a lot more deeply. Now, the way to dig in deeply is to
use - HTML. So if I change this to HTML,
it's going to fire up my browser and it's going to color the
Lines within that actual code, whether they were covered or not. So I can
immediately see that
This thing has not been executed. And for anything, where you have complex if source,
which are the functionalities can be, extremely useful for figuring out, exactly, which part
of the package or writing has not been covered by the tests,
despite the fact that concurrency is incredibly easy and go and you don't really use
mutexes or semaphores or some of the things. It's still possible to create
data races and luckily go itself has a race
detection mechanism which can be run if you're running into weird problems,
Got a very simple program here which is actually the example from the golang blog that illustrates
how this operates. So the program is is fairly simple. What it does? Is
it it starts a timer using after Funk. So it'll call a
function after some amount of time. Here's the function and what the
function does is it resets the timer for sometime in the future
after printing out, the time it is now in fact the Delta
from when this program started and the actual time is some
randomly generated times.
So it just going to for about five seconds. It's going to step through random
printing of the time, and we can run that I've been run it using
go run like this. This is an alternative way of doing bills, right? I don't have to
build this within the structure. This is just in the current directory. Go run will build
it and execute in one. Go very useful for small programs and if I do
that it's going to start printing out times up to about five
seconds. Now occasionally this program will crash within
with a nil pointer and it
Be very occasionally, so it's not obvious. What's wrong with this
particular program? It looks actually like pretty reasonable code. So, to find out what
we can do is, we can do go, run. Now we can say - Race. You can
also put - Race on go test. So that during the test Suite, you can look for data races
and this will run the same program. And immediately it says, by the way, there's a data
race going on in here. So let's just stop this and it's telling
us that there's a go routine, go routine, number
six, which is
Rated this line. So just go to line 18, this line here.
Is having a problem with a go routine at
line 19. So what's happening is this after Funk actually starts to go
routine and it's actually resetting this value T
in the middle of T being used. And so the fact there's, there's a race here between these
two different things because of the, the random amounts of time. And so this is this is a
big problem and this would show up occasionally and it's actually
quite difficult to diagnose there because you may not be obvious that that
Timer is being accessed from one, go routine or another. And so the
data race tests can detect this type of thing and other sorts of
synchronization problems with the program. And it gives a fairly clear, output of where to look
in your, in your program. For the error, you've generated.
If you've written any reasonably sized program in go, then you probably have had to
include some sort of version string and some kind of way of finding out what the version of
the program is at that moment. And this creates a bit of a conundrum because
often the version string might have
Been generated from something in Gate or something external that decides on it, yet, you needed to
be in the program and there are various ways of dealing with this problem. You
could dynamically generate a go program, that gets compiled and that GoPro and could have the
version number in it or you could do some hideous things in a make file was said to adjust
things but there's actually another rather neat way, which is to actually pass
information to the go Linker and you can actually step into a
variable inside the go Linker directly. So I'm gonna show you how to do that. I'm going to go back to my
little program that shouts and
Riley says hello world and I'm going to add
a flag to this. I'm going to say, do they want to know the version?
so, if they ask for the version in,
By default it won't get it.
And then pause the flags and then if there's
also the version, then what we'll do is we'll do this.
This is version and let's just say it's like that
and version is some
Variable. And then we will exit.
so, that should
Build that right?
Okay, set the version. So we'll just set this as a string here version
equals 1.0.0.
And build it and run it. There it is. If I say yes version. There you go. I got
the version number. So, how do I set one 2020 from the outside the program?
Well, there's a couple of ways to do this and obviously, I could do some said things and adjust it,
but actually an easier way is when I'm building it is to set.
LD Flags.
I can go into here like this and I can say I'm going to set this
variable sorry main dot version which is I mean package means of this is the
version and I'm going to set it to 2.00.
And so there's my program and now I'm to do 0 to 0 so that's actually
very easy to build into the workflow and I was able to do that with go run and other things
but it's very simple to build in a version number here and it could be
something that came from get log that gives you the latest hash you're on or a version
generated by some external tool and it requires no
tricks and no changing to the tool is just done with LD flax and so LD
flag actually gets passed to the go Linker and this works nicely whether you're
using C girl.
Or not. So there's no there's no worries about that and you can easily pass in a version number like this. You can
change anything the format here. You notice main that's the package
name if it were, if there was a package path, if it was food / main, you could do
that. And then just the name of the variable. You want to change. If it's a
string you can set it just like that.