26 May 2013

GeeCON #5 The Feedback

Last week the fifth edition of GeeCON took place. GeeCON is my favourite conference and I attended it every year since my first time in 2010. This year I successfully spread the word and brought some friends with me to Kraków. The conference was awesome as expected and I learned a lot of things. At the end of the conference I was asked at least three times to make sure I provide proper feedback to the organizers. Being an organizer of community events myself, I appreciate open feedback from attendees. After fulfilling my duty in completing the GeeCON satisfaction survey I want to comment on several aspects of GeeCON without any specific order of items.

PKP trainLocation
I was told that GeeCON will stay in Kraków for the next event as well. This might have several reasons, like a better venue or preferences of the organizers but I heard that it is because the international speakers like Kraków more than Poznan. Content matters, so it is paramount for GeeCON to attract world class speakers - and it does - but the conference is for the community, the Polish Java community to be precise. Different locations of GeeCON give people around the country a better opportunity to attend the conference, at least every second year. Travelling to Poznan was more cumbersome for me than travelling to Kraków, but I accepted it without question.

Venue
Compared to Kraków, the venue in Poznan was better suited for socializing. In the evening, people just exited the cinema and walked to the next place in the old city for dinner, allowing groups that formed during the conference to stay together without much hassle. In Kraków, everyone needed to go to the city centre either by bus, taxi or his/her own car, which made it more difficult to stay together in groups.

Schedule (Paper)
Reducing the conference booklet to a single page was a good idea to safe money. First I was surprised when I did not see the nice booklet in my bag but then I was happy that it was much smaller and saved space in my pocket. But then it was not possible to select talks during the conference because the abstracts of the talks was missing.

Conference App
Devoxx has a great Conference Schedule app which I liked a lot. As far as I know it is possible to customize it for other conferences, so maybe you (organizers) want to have a look. It would provide the abstracts of the sessions as well which is handy from time to time.

Conference Bag
I love the conference bags issued by GeeCON. The cloth bags are useful long after the conference, and the material is very durable. Usually my wife takes them from me as soon as I return from the conference and I never see them again. (I managed to hide one of the previous three bags for my personal usage, but do not tell her.) But this time the cloth felt thin and the black design was not suitable for daily usage. I know that "the colour is not important as long as it is black" but my wife did not even look at it a second time, neither did I.

#geecon - Y U NO WiFiWi-Fi
The lack of Wi-Fi seems to be an usual problem on conferences. As local attendees just use their smart-phones, the load on Wi-Fi should be smaller than years ago. Still there were times when I wished for a working Wi-Fi at GeeCON, especially when I wanted to tweet something. On the other hand I lived fine without checking my emails all day. Nevertheless Wi-Fi should be working in all rooms of the conference all the time. Unreliable connections are just frustrating.

Sessions
Some sessions had a title that was creating wrong expectations. Probably it was the speaker's right to choose a title he or she saw fit for the talk, still I wished the titles of sessions would reflect the content. When a session was mainly about applying a tool or framework, then the name of that tool or framework should be part of the title of that session. It happened that I would switch session in the first minutes after realizing that my expectations of the presentation would not be met. Beside that I was happy with most sessions that I attended. I learned some new things and I agreed to most things that I heard. Some presentations were important and every developer should see them (unless he or she is already familiar with the topic and even then refreshing one's memory never hurts).

Selection of Topics
One organizer told me that this year they grouped the sessions into tracks because this was a main complaint from attendees of earlier versions. It was clearly visible, there was a Scala track, a NoSQL track, probably a mobile and a Cloud track and these tracks reflected the current popularity of topics. I visit conferences to enjoy a wide range of different sessions and I definitely missed more presentations on code quality and testing. Maybe there were less submissions in these areas because they were less popular.

"School Bell"
Between the sessions I had interesting chats with other attendees but then I was late for the next session. Maybe you (organizers) could install some audible sign that the next session is going to start in 30 seconds. This does not need to be something special, just a small bell that I would hear during the break.

Code instead of Beer?
The GeeCON beer sprint did not make me happy. I am not fond of beer and people did not end in the same place. After two days without any coding I felt itchy to write some code. A Coding Dojo styled evening would be a nice alternative to the beer sprint, of course people would still be able to drink beer there as well. Maybe we can make something up for next GeeCON?

Friend of GeeCON 2013Open Spaces
Open Spaces was like a cherry on a top the awesome GeeCON experience. I met cool people and made some new friends. Unfortunately it is not easy to keep in touch with people if you only know their first name. I would like to see a list of all registered attendees, just their full names and/or Twitter handles, so I could look up people I talked to and get in touch again.

Dear GeeCON organizers! Again you did a great job, thank you very much.

8 May 2013

XSLTunit Ant Support

some antsLast year I did the Prime Factors Kata in XSLT. Using XSLTunit I created a test case that applied the template to a number to calculate its prime factors. I focused on the coding problem and ignored the testing infrastructure, calling the XSLT processor from the command line and looking into the generated XML test result to see if any assert had failed. When I felt like playing with XSLT again, I first had to fix the infrastructure and make it ready for Continuous Integration.

Ant Support
CI tools like Jenkins are able to call any script, but my first idea for build automation is always Ant. I have some history with Ant but most likely I use it because I am an old school Java developer ;-). Fortunately Ant has build-in XSLT support which I can use to apply the XSLTunit template.
<property name="test_prefix" value="tst_" />
<property name="test_result_suffix" value=".test_result.xml" />

<!-- apply style to XMLs in suite -->
<xslt basedir="${suite}" destdir="${suite}" style="${suite}/${style}">
  <include name="*.xml" />
  <exclude name="*${test_result_suffix}" />
</xslt>

<!-- apply test -->
<xslt basedir="${suite}" destdir="${suite}" style="${suite}/${test_prefix}${style}"
      extension="${test_result_suffix}">
  <include name="*.xsl" />
  <exclude name="${test_prefix}*.xsl" />
</xslt>

<!-- create readable HTML test report -->
<xslt basedir="${suite}" destdir="${suite}" style="${basedir}/lib/xsltunit_report.xsl">
  <include name="*${test_result_suffix}" />
  <param name="testname" expression="${style}" />
</xslt>
My approach contains a lot of "convention over configuration". For example I assume that each template ${style} is located inside its own folder ${suite}. The given Ant target first transforms sample data with the template under test for manual review, then applies the test case to the template and finally generates a readable report out of the test result. The first and last step is optional but useful during development. In the end the test result is checked for the text "failed" which shows a failed assertion.
<dirname property="suite" file="${testResult}" />
<loadfile property="test_failed" srcFile="${testResult}" />
<fail message="Test ${suite} failed!">
  <condition>
    <contains string="${test_failed}"
              substring="outcome=&quot;failed&quot;" />
  </condition>
</fail>
Testing and checking the result is done for all templates in the source folder ${src} by using <foreach> from Ant-Contrib.
<foreach target="-testFolder" param="foreach">
  <path>
    <fileset dir="${src}">
      <include name="**/*.xsl" />
      <exclude name="**/${test_prefix}*.xsl" />
    </fileset>
  </path>
</foreach>

<foreach target="-verifyResult" param="foreach">
  <path>
    <fileset dir="${src}">
      <include name="**/*${test_result_suffix}" />
    </fileset>
  </path>
</foreach>
The complete build.xml is here.

FizzBuzz Kata
After the unit tests were executed by Continuous Integration for each commit, I went for some XSLT exercise. Unfortunately adding the Ant support had taken too much of my scheduled learning time and I had to work with the smallest kata available, the FizzBuzz question: Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz". I wrote the following template by following a few XSLTunit tests.
<xsl:template match="value" mode="fizzbuzz">
  <xsl:choose>
    <xsl:when test="number(current()) mod 15 = 0">FizzBuzz</xsl:when>
    <xsl:when test="number(current()) mod 3 = 0">Fizz</xsl:when>
    <xsl:when test="number(current()) mod 5 = 0">Buzz</xsl:when>
    <xsl:otherwise><xsl:value-of select="." /></xsl:otherwise>
  </xsl:choose>
</xsl:template>
To solve the complete question I applied the template to the values of 1 till 100,
<numbers>
  <number><value>1</value></number>
  <number><value>2</value></number>
  <number><value>3</value></number>
  <number><value>4</value></number>
  ...
  <number><value>100</value></number>
</numbers>
which generated a HTML file with the answer of the FizzBuzz question.
  • for 1 say 1
  • for 2 say 2
  • for 3 say Fizz
  • for 4 say 4
  • ...
  • for 100 say Buzz
(Source at Bitbucket.)

4 May 2013

Unifying Social-Media Contacts

Contacts cubedA group of colleagues is collecting stories about successful automation and asked me to share some of my experiences to help raise awareness of the topic. Here is one of my stories. (It targets a broader audience than my usual, software development related posts, so it is more verbose in explaining things.)

Background
I use several social applications like Facebook or Twitter. Recently I needed to get started with LinkedIn to connect with some people that were not using any other services like Xing or Google+. I registered for LinkedIn and got connected. Of course some of my friends also had a profile on LinkedIn and I started to look for them. I had more than 300 contacts spread among all major platforms with a certain degree of duplication, so working through my contacts on each platform was cumbersome. I needed a consolidated, unified and complete list of all my contacts to search for them one after another.

Automation Automation Automation
I am a developer so my first choice was to create some kind of script. (A script is just a tiny application but the word "script" implies a rougher state, something less polished and sort of unfinished.) I did not bother to look for tools that consolidate social media contacts. I am sure there are tools available that collect contacts from Facebook and LinkedIn, but there are so many platforms, these tools will never be exhaustive. I started with Twitter's REST API which worked great. But not all platforms offered such a API and some of these APIs seemed overly complex to me (read OAuth). I just wanted a quick (and dirty ;-) way to collect my contacts, not a complete, JSON and XML consuming monster application and I dropped the idea. I needed a different approach.

Selenium to the Rescue
Using the browser I was able to display and navigate my contacts on each platform with a few mouse clicks. So I decided to automate my browser. I chose Selenium because it is a powerful tool built to test web applications. Getting started with Java and Selenium WebDriver was easy and I quickly created a prototype for Twitter. It executed the following steps:
public void collectNames() {
   openFirstPage();
   iteratePages();
}
It opened Twitter's following page, https://twitter.com/following, waited for the browser to finish loading
protected void openFirstPage() {
   String friendsPage = getFriendsPageUrl();
   driver.get(friendsPage);
   Thread.sleep(WAIT_MS);
}
and collected the names of my Twitter friends by extracting the text formatted with a certain CSS class. (CSS is an annotation that is used in web development to style the appearance of text in the browser. Selenium is able to select elements of a web page based on their style.)
private void iteratePages() {
   boolean hasNextPage = true;
   while (hasNextPage) {
      scrapFriendNames();
      hasNextPage = openNextPage();
   }
}
protected void scrapFriendNames() {
   By cssSelector = By.cssSelector(getFriendsSelector());
   List<WebElement> nameElements = driver.findElements(cssSelector);
   for (WebElement e : nameElements) {
      addName(e.getText());
   }
}
Extending to Other Platforms
The only difference between platforms were the URLs of the contacts pages (getFriendsPageUrl()) and the (CSS) styles of the names of the contacts displayed (getFriendsSelector()), e.g. Google+ used "div.MN.MQ.abJ" where Facebook used ".fsl.fwb.fcb". As long as a social network displayed my contacts as a list of one or more pages, the code could handle it. This approach worked for Facebook, GitHub, Goodreads, Google+, LinkedIn, Twitter, Vimeo, Xing and even IBM Connections.

Limits of Automation
I did not touch authentication, but relied on the cookies stored in my browser to be used. Selenium uses its own browser profile, so I had to point its location with my own one.
String PROFILE =
   "C:\\Users\\codecop\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\8izc0dg4.default";

private void initDriver() {
   webDriver = new FirefoxDriver(new FirefoxProfile(new File(PROFILE)));
}
To use this approach I had to make sure I had logged in all of the social platforms recently and accepted their cookies. Afterwards, when Selenium used Firefox to browse to Facebook, it would send these cookies and receive my contacts page immediately.

Lessons Learned
Let me repeat the key points from this experience:
Automatic
  • Automation might already be worth for one time, repetitive tasks.
  • Browser automation tools like Selenium or Watir are useful not only for testing web applications but also for automating any interaction with web sites.
  • When automating web interaction, keep it simple. The simpler the criteria for navigating and extracting content is, the less likely it is to break when small things change on the web.
  • Start with a rough prototype for some of the actions you want to automate. A little bit of automation is better than none. You can always come back and continue if needed.
  • Do not push the automation too far. Some things are easier to automate than others. Manual preparation up front is an option.