#region License Information /* HeuristicLab * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Threading; using HeuristicLab.Services.Hive.Common; using HeuristicLab.Services.Hive.Common.DataTransfer; using HeuristicLab.Services.Hive.Common.ServiceContracts; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace HeuristicLab.Services.Hive.Tests { using DT = HeuristicLab.Services.Hive.Common.DataTransfer; [TestClass] public class ServiceTests { [ClassInitialize] public static void MyClassInitialize(TestContext testContext) { PluginLoader.pluginAssemblies.Any(); ServiceLocator.Instance = new MockServiceLocator(ServiceLocator.Instance); } private IHiveService GetLocalService() { return new HiveService(); } [TestMethod] public void TestJobs() { var service = GetLocalService(); // create hive experiment DT.HiveExperiment experiment = new DT.HiveExperiment() { Name = "TestExperiment", Description = "" }; // create job DT.Job job = new DT.Job() { CoresNeeded = 1, MemoryNeeded = 0, Priority = 0 }; job.SetState(JobState.Offline); DT.JobData jobData = new DT.JobData() { //Data = PersistenceUtil.Serialize(new MockJob(500, true)) Data = new byte[10000] }; // create plugin DT.Plugin plugin1 = new DT.Plugin(); plugin1.Name = "Tests.MyPlugin"; plugin1.Version = new Version("1.0.0.0"); plugin1.UserId = Guid.Empty; plugin1.IsLocal = true; plugin1.DateCreated = DateTime.Now; DT.PluginData pluginData1 = new DT.PluginData(); pluginData1.FileName = "Tests.MyPlugin-1.0.dll"; pluginData1.Data = new byte[] { 0, 1, 2, 3, 4, 5 }; plugin1.Id = service.AddPlugin(plugin1, new List { pluginData1 }); pluginData1.PluginId = plugin1.Id; // add plugin job.PluginsNeededIds.Add(plugin1.Id); // create slave DT.Slave slave = new Slave(); slave.Id = Guid.NewGuid(); slave.Name = "TestSlave"; slave.Memory = 1024; slave.Cores = 4; slave.CpuSpeed = 2800; slave.OperatingSystem = "Windows 3.11"; slave.CpuArchitecture = CpuArchitecture.x64; // add slave service.AddSlave(slave); // add job job.Id = service.AddJob(job, jobData, new List { slave.Id }); experiment.RootJobId = job.Id; // add hive experiment experiment.Id = service.AddHiveExperiment(experiment); // test job DT.Job jobLoaded = service.GetJob(job.Id); Assert.AreEqual(job.Id, jobLoaded.Id); Assert.AreEqual(job.CoresNeeded, jobLoaded.CoresNeeded); Assert.AreEqual(job.MemoryNeeded, jobLoaded.MemoryNeeded); Assert.AreEqual(job.Priority, jobLoaded.Priority); Assert.AreEqual(JobState.Waiting, jobLoaded.State); Assert.IsTrue(job.PluginsNeededIds.SequenceEqual(jobLoaded.PluginsNeededIds)); DT.JobData jobDataLoaded = service.GetJobData(job.Id); Assert.AreEqual(job.Id, jobDataLoaded.JobId); Assert.IsTrue(jobData.Data.SequenceEqual(jobDataLoaded.Data)); // test hive experiment DT.HiveExperiment experimentLoaded = service.GetHiveExperiment(experiment.Id); Assert.AreEqual(experiment.Id, experimentLoaded.Id); Assert.AreEqual(experiment.Name, experimentLoaded.Name); Assert.AreEqual(experiment.Description, experimentLoaded.Description); Assert.AreEqual(experiment.RootJobId, experimentLoaded.RootJobId); // test assigned ressources var actions = service.Heartbeat(new Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 4, FreeMemory = 1024, JobProgress = new Dictionary() }); Assert.AreEqual(1, actions.Count); Assert.AreEqual(MessageContainer.MessageType.CalculateJob, actions[0].Message); Assert.AreEqual(job.Id, actions[0].JobId); jobLoaded = service.GetJob(job.Id); Assert.AreEqual(JobState.Transferring, jobLoaded.State); // send progress var progress = new Dictionary(); progress.Add(job.Id, new TimeSpan(1, 5, 10, 20, 30)); actions = service.Heartbeat(new Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 3, FreeMemory = 1024, JobProgress = progress }); Assert.AreEqual(0, actions.Count); // the job should be in state 'Calculating' now jobLoaded = service.GetJob(job.Id); Assert.AreEqual(JobState.Calculating, jobLoaded.State); Assert.AreEqual(new TimeSpan(1, 5, 10, 20, 30), jobLoaded.ExecutionTime.Value); // delete service.DeleteHiveExperiment(experiment.Id); Assert.AreEqual(null, service.GetHiveExperiment(experiment.Id)); Assert.AreEqual(null, service.GetJob(job.Id)); Assert.AreEqual(null, service.GetJobData(job.Id)); // send another heartbeat with the deleted job; the server should command the abortion of the job actions = service.Heartbeat(new Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 3, FreeMemory = 1024, JobProgress = progress }); Assert.AreEqual(1, actions.Count); Assert.AreEqual(MessageContainer.MessageType.AbortJob, actions[0].Message); Assert.AreEqual(job.Id, actions[0].JobId); // delete slave service.DeleteSlave(slave.Id); } List jobs = new List(); [TestMethod] public void TestParentJobs() { var service = GetLocalService(); // create hive experiment DT.HiveExperiment experiment = new DT.HiveExperiment() { Name = "TestExperiment", Description = "" }; // create parent job DT.Job parentJob = new DT.Job() { CoresNeeded = 1, MemoryNeeded = 0, Priority = 0, IsParentJob = true, FinishWhenChildJobsFinished = true }; parentJob.SetState(JobState.Offline); DT.JobData parentJobData = new DT.JobData() { Data = new byte[0] }; // create child job DT.Job childJob = new DT.Job() { CoresNeeded = 1, MemoryNeeded = 0, Priority = 0 }; parentJob.SetState(JobState.Offline); DT.JobData childJobData = new DT.JobData() { Data = new byte[1000] }; // create slave DT.Slave slave = new Slave(); slave.Id = Guid.NewGuid(); slave.Name = "TestSlave"; slave.Memory = 1024; slave.Cores = 4; slave.CpuSpeed = 2800; slave.OperatingSystem = "Windows 3.11"; slave.CpuArchitecture = CpuArchitecture.x64; // add slave service.AddSlave(slave); // add parent job parentJob.Id = service.AddJob(parentJob, parentJobData, new List { slave.Id }); experiment.RootJobId = parentJob.Id; // add child job childJob.Id = service.AddChildJob(parentJob.Id, childJob, childJobData); childJob.ParentJobId = parentJob.Id; // add hive experiment experiment.Id = service.AddHiveExperiment(experiment); // test child job var childJobLoaded = service.GetJob(childJob.Id); Assert.AreEqual(childJob.ParentJobId, childJobLoaded.ParentJobId); Assert.AreEqual(JobState.Waiting, childJobLoaded.State); Assert.AreEqual(false, childJobLoaded.FinishWhenChildJobsFinished); Assert.AreEqual(false, childJobLoaded.IsParentJob); // test parent job var parentJobLoaded = service.GetJob(parentJob.Id); Assert.AreEqual(JobState.Waiting, parentJobLoaded.State); Assert.AreEqual(true, parentJobLoaded.FinishWhenChildJobsFinished); Assert.AreEqual(true, parentJobLoaded.IsParentJob); // test heartbeat var actions = service.Heartbeat(new Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 4, FreeMemory = 1024, JobProgress = new Dictionary() }); Assert.AreEqual(1, actions.Count); // only the child job should be assigned Assert.AreEqual(MessageContainer.MessageType.CalculateJob, actions[0].Message); Assert.AreEqual(childJob.Id, actions[0].JobId); // lifecycle - let it process one server-heartbeat; the parent job must NOT be set to finished var lifecycleManager = new LifecycleManager(); lifecycleManager.Start(); Thread.Sleep(1000); lifecycleManager.Stop(); parentJobLoaded = service.GetJob(parentJob.Id); Assert.AreEqual(JobState.Waiting, parentJobLoaded.State); // set child job to finished childJobLoaded.SetState(JobState.Finished, slave.Id, ""); service.UpdateJob(childJobLoaded); // lifecycle - let it process one server-heartbeat; this should set the parent job to finished lifecycleManager.Start(); Thread.Sleep(1000); lifecycleManager.Stop(); // test if parent job is finished parentJobLoaded = service.GetJob(parentJob.Id); Assert.AreEqual(JobState.Finished, parentJobLoaded.State); // delete experiment service.DeleteHiveExperiment(experiment.Id); Assert.AreEqual(null, service.GetJob(parentJob.Id)); Assert.AreEqual(null, service.GetJob(childJob.Id)); service.DeleteSlave(slave.Id); } //[TestMethod] //public void TestHeartbeats() { // var service = GetLocalService(); // // check if group already exists and delete // var existingSlaveGroup = service.GetSlaveGroups().SingleOrDefault(g => g.Name == "TestGroup"); // if (existingSlaveGroup != null) { // var slavesToDelete = service.GetSlaves().Where(s => s.ParentResourceId == existingSlaveGroup.Id); // foreach (var slave in slavesToDelete) service.DeleteSlave(slave.Id); // service.DeleteSlaveGroup(existingSlaveGroup.Id); // } // Guid groupId = service.AddSlaveGroup(new SlaveGroup() { Name = "TestGroup", Description = "Used for unit tests" }); // // create slaves // var slaves = new List(); // for (int i = 0; i < 1; i++) { // DT.Slave slave = new DT.Slave() { // Cores = 2, // Memory = 4096, // Name = "Slave " + i, // IsAllowedToCalculate = true, // SlaveState = SlaveState.Idle, // CpuSpeed = 2800, // FreeCores = 2, // FreeMemory = 3000 // }; // // check if slave with this name already exists and delete // var existingSlave = service.GetSlaves().Where(s => s.Name == slave.Name).SingleOrDefault(); // if (existingSlave != null) service.DeleteSlave(existingSlave.Id); // slave.Id = service.AddSlave(slave); // service.AddResourceToGroup(groupId, slave.Id); // slaves.Add(slave); // } // // create jobs with different group, they should not be assigned // existingSlaveGroup = service.GetSlaveGroups().SingleOrDefault(g => g.Name == "DummyGroup"); // if (existingSlaveGroup != null) service.DeleteSlaveGroup(existingSlaveGroup.Id); // Guid dummyGroupId = service.AddSlaveGroup(new SlaveGroup() { Name = "DummyGroup", Description = "Used for unit tests; jobs from this group shall not be calculated" }); // // create dummy jobs // var dummyJobs = new List(); // for (int i = 0; i < 2; i++) { // Job job = new Job() { // CoresNeeded = 1, MemoryNeeded = 0 // }; // JobData jobData = new JobData() { Data = PersistenceUtil.Serialize(new MockJob(500, false)) }; // job.Id = service.AddJob(job, jobData, new List { dummyGroupId }); // dummyJobs.Add(job); // } // // create jobs // for (int i = 0; i < 2; i++) { // Job job = new Job() { // CoresNeeded = 1, MemoryNeeded = 0 // }; // JobData jobData = new JobData() { Data = PersistenceUtil.Serialize(new MockJob(500, false)) }; // job.Id = service.AddJob(job, jobData, new List { groupId }); // jobs.Add(job); // } // // send heartbeats // foreach (var slave in slaves) { // new Thread(new ParameterizedThreadStart(RunSlaveThread)).Start(slave); // } // IEnumerable lightweightJobs; // do { // Thread.Sleep(500); // lightweightJobs = service.GetLightweightJobs(jobs.Select(x => x.Id)); // } while (!lightweightJobs.All(x => x.State == JobState.Finished)); // // delete slaves // foreach (var slave in slaves) { // service.DeleteSlave(slave.Id); // Assert.AreEqual(null, service.GetSlave(slave.Id)); // } // // delete groups // service.DeleteSlaveGroup(groupId); // service.DeleteSlaveGroup(dummyGroupId); // // delete jobs // foreach (var job in jobs) { // service.DeleteJob(job.Id); // } // // delete dummy jobs // foreach (var job in dummyJobs) { // service.DeleteJob(job.Id); // } //} //public void RunSlaveThread(object slaveobj) { // try { // var service = GetLocalService(); // Slave slave = (Slave)slaveobj; // int freeCores = slave.Cores.Value; // for (int i = 0; i < 10; i++) { // var messages = service.Heartbeat(new Heartbeat() { SlaveId = slave.Id, FreeMemory = 2423, FreeCores = freeCores, JobProgress = new Dictionary() }); // if (messages.Count == 0) { // Debug.WriteLine("No job available"); // return; // no more jobs // } // Debug.WriteLine("Messages: {0}", string.Join(", ", messages.Select(m => m.Message))); // Assert.IsTrue(messages.Where(x => x.Message == MessageContainer.MessageType.AbortJob).Count() == 0); // Assert.IsTrue(messages.Where(x => x.Message == MessageContainer.MessageType.SayHello).Count() == 0); // Assert.IsTrue(messages.Where(x => x.Message == MessageContainer.MessageType.PauseJob).Count() == 0); // var calculateJobMessage = messages.Where(x => x.Message == MessageContainer.MessageType.CalculateJob).SingleOrDefault(); // if (calculateJobMessage != null) { // if (!jobs.Select(j => j.Id).Contains(calculateJobMessage.JobId)) // Assert.Fail("Got job which was not assigned to the slavegroup"); // Debug.WriteLine("Job available, calculating"); // Job job = service.GetJob(calculateJobMessage.JobId); // JobData jobData = service.GetJobData(job.Id); // IJob deserializedJob = PersistenceUtil.Deserialize(jobData.Data); // deserializedJob.Start(); // job.SetState(JobState.Finished); // jobs.Where(x => x.Id == job.Id).Single().SetState(JobState.Finished); // jobData.Data = PersistenceUtil.Serialize(deserializedJob); // service.UpdateJobData(job, jobData); // Debug.WriteLine("finished calculating"); // } // } // } // catch (Exception e) { // Assert.Fail(e.Message, e); // } //} } }