Setting up Jenkins, GoogleTest, & Mercurial (with a local repository)
Over the past couple days, I've been trying out GoogleTest and attempting to get it working with Jenkins for continuous integration. I wanted to have the GoogleTest suite run during the Jenkins build and report the results to Jenkins. I also wanted Jenkins to tie in with Mercurial commits for a seamless, automated system. I suppose it wouldn't be Continuous Integration without it, right? My inexperience with all three of the technologies led to a few pitfalls, which is my reason for this post...
I wanted to get a working solution with a couple basic GoogleTest tests. As a quick start, I found Lee Francis Wilhelmsen's blog post and simply downloaded his solution (found in his "closing" section). As he describes, I had to download the gtest code and link his projects to it. In my case, I downloaded the gtest code to "C:\Projects\gtest-1.7.0". Then, for each project in his solution, I opened the project properties and set "Additional Include Directories" to "C:\Projects\gtest-1.7.0\include;"
The other thing I changed was the post-build event. He added "$(TargetDir)$(TargetFileName)" to the test project's post-build event. This causes the tests to be run every time the code is built. This has two repercussions. The first is that you have errors thrown in Visual Studio. The second, and more important of the two, is that when Jenkins uses MSBuild to build the project, a failing test causes a failing build. So, the Jenkins job fails as it should but for the wrong reason. The code itself (minus tests) may build just fine. I would rather have the Jenkins job complete the build and run the tests so it can show me those results. The job will still fail, but then I will be able to see which test cause the problem.
Next, I created a Mercurial repository on my Jenkins "server". I should note that this caused me some grief. Things may have gone more smoothly if I had installed Mercurial on a different machine and added a webserver to interface with it. In many areas, a URL would have been easier than a local filepath. That said, I didn't want a webserver in the mix yet.
To install that, go to "Manage Jenkins" > "Manage Plugins".
Click the "Available" tab, and find the Mercurial plugin. If you're using an xUnit framework that doesn't output native jUnit XML, you may also want to install the xUnit plugin while you're here.
After installation, the plugin must be configured (Manage Jenkins > Configure System). My config looked like this:
I then created a job in Jenkins:
The first steps was telling Mercurial where to find the repository. It didn't like UNC paths, so I ended up with the syntax below.
Again, Jenkins will process native jUnit XML as shown above. If you don't have native output, try the xUnit plugin.
'wget' is not recognized as an internal or external command, operable program or batch file
Ok, so I added it the the Windows path variable. Same error. I then added the full path to the wget executable, and got this error:
The system cannot find the path specified.
warning: incoming.jenkins hook exited with status 1
After much time wasted, I realized what would probably have been obvious to any savvy Mercurial user: The post-commit hook runs on the machine doing the push. In other words, it was running the commands on my machine, not the server. The above errors came about because wget was installed on the server but not on my machine.
So, because I didn't want all of our Mercurial users to have to install wget, I went looking for an alternative already built into Windows. I found this question on StackOverflow. The answer from Ubeogesh yielded the best start using PowerShell from the command line. Because I was only issuing a get request, not downloading a file, I opted for the DownloadData method rather than DownloadFile.
This worked beatifully. A commit to the Mercurial repository fired off the hook, and Jenkins started a build. The only thing left was the fact that the powershell command spewed out a bunch of numbers in the command window
I found this StackOverflow question which showed how to suppress the output. The resulting hgrc file looked like this:
Notice that the commit & incoming hooks are essentially just firing a quick GET request to Jenkins. Jenkins sees that & cross-references the requested URL with the URL entered in the job config. If they match, it starts the job.
I wanted to get a working solution with a couple basic GoogleTest tests. As a quick start, I found Lee Francis Wilhelmsen's blog post and simply downloaded his solution (found in his "closing" section). As he describes, I had to download the gtest code and link his projects to it. In my case, I downloaded the gtest code to "C:\Projects\gtest-1.7.0". Then, for each project in his solution, I opened the project properties and set "Additional Include Directories" to "C:\Projects\gtest-1.7.0\include;"
The other thing I changed was the post-build event. He added "$(TargetDir)$(TargetFileName)" to the test project's post-build event. This causes the tests to be run every time the code is built. This has two repercussions. The first is that you have errors thrown in Visual Studio. The second, and more important of the two, is that when Jenkins uses MSBuild to build the project, a failing test causes a failing build. So, the Jenkins job fails as it should but for the wrong reason. The code itself (minus tests) may build just fine. I would rather have the Jenkins job complete the build and run the tests so it can show me those results. The job will still fail, but then I will be able to see which test cause the problem.
Next, I created a Mercurial repository on my Jenkins "server". I should note that this caused me some grief. Things may have gone more smoothly if I had installed Mercurial on a different machine and added a webserver to interface with it. In many areas, a URL would have been easier than a local filepath. That said, I didn't want a webserver in the mix yet.
Configuring the Jenkins job
After committing the code to Mercurial, I needed to hook it up to Jenkins. Jenkins has the ability to poll a repository on a schedule, but that creates a lot of needless traffic & processing. I noticed that the Jenkins Mercurial plugin home page had an example of using a Mercurial post-commit hook to trigger the Jenkins build. Perfect.To install that, go to "Manage Jenkins" > "Manage Plugins".
Click the "Available" tab, and find the Mercurial plugin. If you're using an xUnit framework that doesn't output native jUnit XML, you may also want to install the xUnit plugin while you're here.
After installation, the plugin must be configured (Manage Jenkins > Configure System). My config looked like this:
I then created a job in Jenkins:
The first steps was telling Mercurial where to find the repository. It didn't like UNC paths, so I ended up with the syntax below.
The job also needs to have polling enabled, but notice the blank schedule box. We don't want it to poll on a schedule, only when triggered by the Mercurial post-commit hook (more on that in a second).
The above settings will allow the Mercurial plugin to pull the latest code from the repository into the job workspace. The batch commands below build the code and run the tests respectively. Notice that the second command has an output flag which creates the results.xml file.
The next step is processing that results.xml file:
The Post-Commit Hook
With the job configuration complete, I added a post commit hook on the server side Mercurial repository (in the .hg\hgrc file found in the repository root - hidden on Linux systems). This looked easy in the Mercurial plugin example. But, there was one problem: their example used wget. Not a problem, I found Wget for Windows and installed it on the server. Then I got this error:'wget' is not recognized as an internal or external command, operable program or batch file
Ok, so I added it the the Windows path variable. Same error. I then added the full path to the wget executable, and got this error:
The system cannot find the path specified.
warning: incoming.jenkins hook exited with status 1
After much time wasted, I realized what would probably have been obvious to any savvy Mercurial user: The post-commit hook runs on the machine doing the push. In other words, it was running the commands on my machine, not the server. The above errors came about because wget was installed on the server but not on my machine.
So, because I didn't want all of our Mercurial users to have to install wget, I went looking for an alternative already built into Windows. I found this question on StackOverflow. The answer from Ubeogesh yielded the best start using PowerShell from the command line. Because I was only issuing a get request, not downloading a file, I opted for the DownloadData method rather than DownloadFile.
This worked beatifully. A commit to the Mercurial repository fired off the hook, and Jenkins started a build. The only thing left was the fact that the powershell command spewed out a bunch of numbers in the command window
I found this StackOverflow question which showed how to suppress the output. The resulting hgrc file looked like this:
Notice that the commit & incoming hooks are essentially just firing a quick GET request to Jenkins. Jenkins sees that & cross-references the requested URL with the URL entered in the job config. If they match, it starts the job.
Conclusion
I have noticed the following in my Jenkins job console output which will need to be remedied:ERROR: Workspace reports paths.default as C:\Repositories\RunGoogleTest
which looks different than file:///C:/Repositories/RunGoogleTest
so falling back to fresh clone rather than incremental update
Other than that, it seems to be working quite nicely.
Comments
Post a Comment