Tuesday, July 05, 2011

How to get instant updates of configuration file of your testing framework from svn repository

Hi.

Here is one more pattern intended for operating with the latest versions of configuration files. That was originated from the following. The project under testing behaves pretty much agile so to hold such agile things up to date we want to extract the relevant configs from some centralized storage that is easy to maintain.
To support such functionality I prepared small utility class to communicate with svn repository. The pattern looks like this:

P.S. - you should use svnkit in order to keep the code working.

package test.svn.client;

import java.io.File;

import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

public class Communicator {
 
 static final String syncDest = "/Storage";
 
 SVNUpdateClient uc;
 SVNURL repositoryURL;
 
 public Communicator(String user, String password, String repository) throws SVNException{
  repositoryURL = SVNURL.parseURIEncoded(repository);
  DAVRepositoryFactory.setup();
  SVNRepository svnRepository = SVNRepositoryFactoryImpl.create(repositoryURL);
  ISVNAuthenticationManager manager = SVNWCUtil.createDefaultAuthenticationManager(user, password);
  svnRepository.setAuthenticationManager(manager);
  SVNClientManager cm = SVNClientManager.newInstance();
  cm.setAuthenticationManager(manager);
  uc = cm.getUpdateClient();
 }
 
 public void syncUp(String segment) throws SVNException{
  uc.doCheckout(repositoryURL.appendPath(segment, true), new File(syncDest), SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNDepth.INFINITY, true);
  System.out.println("Done");
 }
 
 public String getResourceFolder(){
  return syncDest;
 }
 
 /**
  * Just to check how it really works :)
  * @param arg
  * @throws SVNException
  */
 public static void main(String[] arg) throws SVNException{
  Communicator driver = new Communicator("secret", "secret", "https://my.svnserver.fake/trunk");
  driver.syncUp("/resources/config");
 }
}

Then we should write the support for fetching the certain values from the resource files

package com.somefake.pkg;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;

import com.somefake.pkg.TAE;

public class ResourceProvider {
 
 private static final Communicator resourceActualyzer = new Communicator("testuser", "testpassword", "https://fakesvnserver.com/trunk");
 
 private static Locale currentLocale = Locale.RU;
 
 private static final HashMap<Locale, Properties> map = new HashMap<Locale, Properties>();
 
 
 static{
  TipsteryTestCase.logger.info("Initializing SVN client...");
  try {
   HashSet<String> localeRu = new HashSet<String>();
   HashSet<String> localeEn = new HashSet<String>();
   localeRu.add(resourceActualyzer.getResourceFolder() + "/Resources_ru.xml");
   localeRu.add(resourceActualyzer.getResourceFolder() + "/jsResources_ru.xml");
   localeEn.add(resourceActualyzer.getResourceFolder() + "/Resources.xml");
   localeEn.add(resourceActualyzer.getResourceFolder() + "/jsResources.xml");
   resourceActualyzer.syncUp("/resources");
   map.put(Locale.RU, loadProperties(localeRu));
   map.put(Locale.EN, loadProperties(localeEn));
  } catch (Exception e) {
   resourceActualyzer.setSuccessful(false, e);
   TipsteryTestCase.logger.error("Error while loading resources.", e);
  }
 }
 
 private static Properties loadProperties(HashSet<String> sourceFiles) throws InvalidPropertiesFormatException, FileNotFoundException, IOException{
  Properties stagingProperties = new Properties();
  for(String sourceFile: sourceFiles){
   stagingProperties.loadFromXML(new FileInputStream(sourceFile));
  }
  return stagingProperties;
 }
 
 public static String getProperty(String key) throws TAE{
  if(!resourceActualyzer.isSuccessful()){
   String message = "Looks like you had problems with " 
     + "resource files syncronization\n"
     + "Check if you set valid credentials and specified valid resource file names";
   TipsteryTestCase.error(message);
   resourceActualyzer.getException().printStackTrace();
   throw new TAE(message);
  }
  return map.get(currentLocale).getProperty(key);
 }
 
 public static void setLocale(Locale newLocale){
  currentLocale = newLocale;
 }
 
 /**
  * Just to check if it works
  * @param arg
  * @throws TAE 
  */
 public static void main(String[] arg) throws TAE{
  System.out.println(ResourceProvider.getProperty("some.property"));
  ResourceProvider.setLocale(Locale.EN);
  System.out.println(ResourceProvider.getProperty("some.property"));
 }
}

Locale here means just the enum, and do not curse me for the approach to the errors handling.