writing a battle runner for robocode

As part of setting up acceptance testing for my robocode robot, I need to be able to run a battle from each acceptance test that I write and I therefore need to create some code that will facilitate this. I've got an idea about how I want this to take shape:

Battle - a class that holds convention led battle information from a name provided by the acceptance test
BattleRunner - a class that will run a particular battle
BattleReader - a class that will read the results of a given battle
BattleResult - a class representing an individual robot's result in the battle

So I start as usual with a specification:

    public class when_creating_a_battle
    {
        Establish context = () => battleName = "mk77askdnoasid98asd";

        Because of = () => battle = new Battle(battleName);

        It should_contain_the_battle_name_it_was_created_with = () =>
            battle.BattleName.ShouldEqual(battleName);
        
        static Battle battle;
        static string battleName;
    }

This is very straight forward:

    public class Battle
    {
        public Battle(string battleName)
        {
            BattleName = battleName;
        }

        public string BattleName { get; private set; }
    }

I now want this battle file to derive the battle file name by convention from the battle name provided - I've made the BattleFile property a System.Uri and I assert it contains the relevant parts :

    public class when_creating_a_battle
    {
        Establish context = () =>
        {
            battleName = "mk77askdnoasid98asd";

            expectedPartOfTheBattleFile = string.Format("{0}.battle", battleName);
        };

        Because of = () => battle = new Battle(battleName);

        It should_contain_the_battle_name_it_was_created_with = () =>
            battle.BattleName.ShouldEqual(battleName);

        It should_by_convention_create_the_file_name_representing_this_battle = () =>
            battle.BattleFile.LocalPath.ShouldContain(expectedPartOfTheBattleFile);

        It should_use_the_known_battle_files_location_when_creating_the_battle_file = () =>
            battle.BattleFile.LocalPath.ShouldContain(BattleConstants.BattleFilesLocation);

        static Battle battle;
        static string battleName;
        static string expectedPartOfTheBattleFile;
    }

And again, straightforward implementation:

    public class Battle
    {
        public Battle(string battleName)
        {
            BattleName = battleName;
            BattleFile = new Uri( 
                string.Format(@"{0}{1}.battle",
                    BattleConstants.BattleFilesLocation,
                    battleName));
        }

        public string BattleName { get; private set; }

        public Uri BattleFile { get; private set; }
    }

    public static class BattleConstants
    {
        public const string BattleFilesLocation = 
           @"C:\dev\Projects\RoboCodeWars\TeamSlayer\Specifications\AcceptanceSpecs\battles\";
    }

I now want to do the same for the results file location:

    public class when_creating_a_battle
    {
        Establish context = () =>
        {
            battleName = "mk77askdnoasid98asd";

            expectedPartOfTheBattleFile = string.Format("{0}.battle", battleName);
            expectedPartOfTheResultFile = string.Format("{0}.result", battleName);
        };

        Because of = () => battle = new Battle(battleName);

        It should_contain_the_battle_name_it_was_created_with = () =>
            battle.BattleName.ShouldEqual(battleName);

        It should_by_convention_create_the_file_name_representing_this_battle = () =>
            battle.BattleFile.LocalPath.ShouldContain(expectedPartOfTheBattleFile);

        It should_use_the_known_battle_files_location_when_creating_the_battle_file = () =>
            battle.BattleFile.LocalPath.ShouldContain(BattleConstants.BattleFilesLocation);

        It should_by_convention_create_the_file_name_representing_the_result = () =>
            battle.BattleResultFile.LocalPath.ShouldContain(expectedPartOfTheResultFile);

        It should_use_the_known_battle_results_file_location_for_the_results_file = () =>
            battle.BattleResultFile.LocalPath
               .ShouldContain(BattleConstants.BattleResultsLocation);

        static Battle battle;
        static string battleName;
        static string expectedPartOfTheBattleFile;
        static string expectedPartOfTheResultFile;
    }

The implementation is:

    public class Battle
    {
        public Battle(string battleName)
        {
            BattleName = battleName;
            BattleFile = new Uri( 
                string.Format(@"{0}{1}.battle",
                              BattleConstants.BattleFilesLocation,
                              battleName));
            BattleResultFile = new Uri(
                string.Format(@"{0}{1}.result",
                              BattleConstants.BattleResultsLocation,
                              battleName));
        }

        public string BattleName { get; private set; }

        public Uri BattleFile { get; private set; }

        public Uri BattleResultFile { get; private set; }
    }

Now I want to move onto the battle runner and having already spiked this at an earlier stage, I know I need to prepare data for starting a process. So I'll begin with introducing an interface for a process runner. I start with a new specification:

    public class when_the_battle_runner_runs_a_battle
    {
        Establish context = () =>
        {
            processRunner = MockRepository.GenerateStub<IProcessRunner>();
            battleRunner = new BattleRunner(processRunner);

            string battleName = "p4389y78asdj";
            battle = new Battle(battleName);
        };

        Because of = () => battleRunner.Run(battle);

        It should_tell_the_process_runner_to_run_a_new_battle_process = () =>
            processRunner.AssertWasCalled(p => p.RunProcess(Arg<ProcessStartInfo>.Is.NotNull));

        static BattleRunner battleRunner;
        static IProcessRunner processRunner;
        static Battle battle;
    }

Implementation:

    public interface IProcessRunner
    {
        void RunProcess(ProcessStartInfo processStartInfo);
    }

    public class BattleRunner
    {
        readonly IProcessRunner processRunner;

        public BattleRunner(IProcessRunner processRunner)
        {
            this.processRunner = processRunner;
        }

        public void Run(Battle battle)
        {
            processRunner.RunProcess(new ProcessStartInfo());
        }
    }

Now I want to record the information that is sent in on that process start information, so I stub the method and use the Do() syntax to record (this is my personal preference and hope to blog about my thoughts on Do(), WhenCalled() and GetArgumentsForCallsMadeOn() at a later date ).

    public class when_the_battle_runner_runs_a_battle
    {
        Establish context = () =>
        {
            processRunner = MockRepository.GenerateStub<IProcessRunner>();
            processRunner.Stub(p => p.RunProcess(null))
                         .IgnoreArguments().Do(RecordProcessStartInfoUsedInCall);
            battleRunner = new BattleRunner(processRunner);

            string battleName = "p4389y78asdj";
            battle = new Battle(battleName);
        };

        static Action<ProcessStartInfo> RecordProcessStartInfoUsedInCall = s => 
            processStartInformation = s;

        Because of = () => battleRunner.Run(battle);

        It should_tell_the_process_runner_to_run_a_new_battle_process = () =>
            processRunner.AssertWasCalled(p => p.RunProcess(Arg<ProcessStartInfo>.Is.NotNull));

        It should_include_the_known_battle_runner_batch_file_in_the_start_arguments = () =>
            processStartInformation.FileName
                .ShouldEqual(BattleConstants.BattleFilesLocation + "run_battle.bat");

        It should_set_the_working_directory_for_robocode = () =>
            processStartInformation.WorkingDirectory
                .ShouldEqual(BattleConstants.RobocodeWorkingDirectory);

        It should_include_the_battle_in_the_start_arguments = () =>
            processStartInformation.Arguments.ShouldContain("-battle " + battle.BattleFile.LocalPath);

        It should_include_the_results_in_the_start_arguments = () =>
            processStartInformation.Arguments
                .ShouldContain("-results " + battle.BattleResultFile.LocalPath);

        static BattleRunner battleRunner;
        static IProcessRunner processRunner;
        static Battle battle;
        static ProcessStartInfo processStartInformation;
    }

And below is the working code so far. I'll continue the development of the battle classes in my next robocode post.

    public class BattleRunner
    {
        readonly IProcessRunner processRunner;

        public BattleRunner(IProcessRunner processRunner)
        {
            this.processRunner = processRunner;
        }

        public void Run(Battle battle)
        {
            var startInformation = new ProcessStartInfo();
            startInformation.FileName = BattleConstants.BattleFilesLocation + "run_battle.bat";
            startInformation.WorkingDirectory = BattleConstants.RobocodeWorkingDirectory;
            startInformation.Arguments = string.Format("-battle {0} -results {1}",
                                    battle.BattleFile.LocalPath,
                                    battle.BattleResultFile.LocalPath);

            processRunner.RunProcess(startInformation);
        }
    }



0 comments: