#region License Information /* HeuristicLab * Copyright (C) 2002-2011 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 HeuristicLab.Clients.Hive; using HeuristicLab.Services.Hive.Tests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; using DT = HeuristicLab.Services.Hive.DataTransfer; namespace HeuristicLab.Services.Hive.Tests { [TestClass] public class ServiceTests { // use the mock service locator to modify service properties (such as current user) private static MockServiceLocator mockServiceLocator; [ClassInitialize] public static void MyClassInitialize(TestContext testContext) { mockServiceLocator = new MockServiceLocator(ServiceLocator.Instance); ServiceLocator.Instance = mockServiceLocator; } private HeuristicLab.Services.Hive.ServiceContracts.IHiveService GetLocalService() { return new HeuristicLab.Services.Hive.HiveService(); } [TestMethod] public void TestJobs() { var service = GetLocalService(); // create hive experiment DT.Job experiment = new DT.Job() { Name = "TestExperiment", Description = "" }; // create task DT.Task job = new DT.Task() { CoresNeeded = 1, MemoryNeeded = 0, Priority = 0 }; job.State = DT.TaskState.Offline; job.StateLog.Add(new DT.StateLog { State = DT.TaskState.Offline, DateTime = DateTime.Now }); DT.TaskData jobData = new DT.TaskData() { //Data = PersistenceUtil.Serialize(new MockJob(500, true)) Data = new byte[10000] }; // delete plugin first (otherwise the system would not allow it because of the same hash code var hash = new byte[] { 1, 2, 3 }; var p = service.GetPluginByHash(hash); if (p != null) service.DeletePlugin(p.Id); // 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.DateCreated = DateTime.Now; plugin1.Hash = hash; 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 DT.Slave(); slave.Id = Guid.NewGuid(); slave.Name = "TestSlave"; slave.Memory = 1024; slave.Cores = 4; slave.CpuSpeed = 2800; slave.OperatingSystem = "Windows 3.11"; slave.CpuArchitecture = DT.CpuArchitecture.x64; // add slave service.AddSlave(slave); // add hive experiment experiment.Id = service.AddJob(experiment); // add task job.JobId = experiment.Id; job.Id = service.AddTask(job, jobData, new List { slave.Id }); // test task DT.Task jobLoaded = service.GetTask(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(DT.TaskState.Waiting, jobLoaded.State); Assert.IsTrue(job.PluginsNeededIds.SequenceEqual(jobLoaded.PluginsNeededIds)); Assert.AreEqual(job.JobId, jobLoaded.JobId); DT.TaskData jobDataLoaded = service.GetTaskData(job.Id); Assert.AreEqual(job.Id, jobDataLoaded.TaskId); Assert.IsTrue(jobData.Data.SequenceEqual(jobDataLoaded.Data)); // test hive experiment DT.Job experimentLoaded = service.GetJob(experiment.Id); Assert.AreEqual(experiment.Id, experimentLoaded.Id); Assert.AreEqual(experiment.Name, experimentLoaded.Name); Assert.AreEqual(experiment.Description, experimentLoaded.Description); // test assigned ressources var actions = service.Heartbeat(new DT.Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 4, FreeMemory = 1024, JobProgress = new Dictionary() }); Assert.AreEqual(1, actions.Count); Assert.AreEqual(MessageContainer.MessageType.CalculateTask, actions[0].Message); Assert.AreEqual(job.Id, actions[0].TaskId); jobLoaded = service.GetTask(job.Id); Assert.AreEqual(TaskState.Transferring, jobLoaded.State); // slave is responsible for updating state to 'Calculating' service.UpdateTaskState(jobLoaded.Id, DT.TaskState.Calculating, slave.Id, null, null); // send progress var progress = new Dictionary(); progress.Add(job.Id, new TimeSpan(1, 5, 10, 20, 30)); actions = service.Heartbeat(new DT.Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 3, FreeMemory = 1024, JobProgress = progress }); Assert.AreEqual(0, actions.Count); // the task should be in state 'Calculating' now jobLoaded = service.GetTask(job.Id); Assert.AreEqual(TaskState.Calculating, jobLoaded.State); Assert.AreEqual(new TimeSpan(1, 5, 10, 20, 30), jobLoaded.ExecutionTime); // test if the task is returned for the resource var jobsBySlave = service.GetTasksByResourceId(slave.Id); Assert.AreEqual(1, jobsBySlave.Count()); Assert.AreEqual(job.Id, jobsBySlave.Single().Id); // set it to finished service.UpdateTaskState(jobLoaded.Id, DT.TaskState.Finished, slave.Id, null, null); // test if the task is returned for the resource (it should not be) var jobsBySlave2 = service.GetTasksByResourceId(slave.Id); Assert.AreEqual(0, jobsBySlave2.Count()); // set task waiting again service.UpdateTaskState(job.Id, DT.TaskState.Waiting, null, null, string.Empty); // get task again actions = service.Heartbeat(new DT.Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 4, FreeMemory = 1024, JobProgress = new Dictionary() }); Assert.AreEqual(1, actions.Count); Assert.AreEqual(MessageContainer.MessageType.CalculateTask, actions[0].Message); Assert.AreEqual(job.Id, actions[0].TaskId); // create downtime which should make slave unavailable for calculation Guid downtimeId = service.AddDowntime(new DT.Downtime { ResourceId = slave.Id, StartDate = DateTime.Now - TimeSpan.FromMinutes(1), EndDate = DateTime.Now + TimeSpan.FromMinutes(1), Recurring = false }); progress.Clear(); progress.Add(job.Id, new TimeSpan(1, 5, 10, 20, 30)); actions = service.Heartbeat(new DT.Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 4, FreeMemory = 1024, JobProgress = new Dictionary() }); Assert.AreEqual(1, actions.Count); Assert.AreEqual(MessageContainer.MessageType.PauseAll, actions[0].Message); Assert.AreEqual(Guid.Empty, actions[0].TaskId); service.DeleteDowntime(downtimeId); // delete service.DeleteJob(experiment.Id); Assert.AreEqual(null, service.GetJob(experiment.Id)); Assert.AreEqual(null, service.GetTask(job.Id)); Assert.AreEqual(null, service.GetTaskData(job.Id)); // send another heartbeat with the deleted task; the server should command the abortion of the task actions = service.Heartbeat(new DT.Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 3, FreeMemory = 1024, JobProgress = progress }); Assert.AreEqual(1, actions.Count); Assert.AreEqual(MessageContainer.MessageType.AbortTask, actions[0].Message); Assert.AreEqual(job.Id, actions[0].TaskId); // delete slave service.DeleteSlave(slave.Id); } [TestMethod] public void TestParentJobs() { var service = GetLocalService(); // create hive experiment DT.Job experiment = new DT.Job() { Name = "TestExperiment", Description = "" }; // create parent task DT.Task parentJob = new DT.Task() { CoresNeeded = 1, MemoryNeeded = 0, Priority = 0, IsParentTask = true, FinishWhenChildJobsFinished = true }; parentJob.State = DT.TaskState.Offline; parentJob.StateLog.Add(new DT.StateLog { State = DT.TaskState.Offline, DateTime = DateTime.Now }); DT.TaskData parentJobData = new DT.TaskData() { Data = new byte[0] }; // create child task DT.Task childJob = new DT.Task() { CoresNeeded = 1, MemoryNeeded = 0, Priority = 0 }; childJob.State = DT.TaskState.Offline; childJob.StateLog.Add(new DT.StateLog { State = DT.TaskState.Offline, DateTime = DateTime.Now }); DT.TaskData childJobData = new DT.TaskData() { Data = new byte[1000] }; // create slave DT.Slave slave = new DT.Slave(); slave.Id = Guid.NewGuid(); slave.Name = "TestSlave"; slave.Memory = 1024; slave.Cores = 4; slave.CpuSpeed = 2800; slave.OperatingSystem = "Windows 3.11"; slave.CpuArchitecture = DT.CpuArchitecture.x64; // add slave service.AddSlave(slave); // add hive experiment experiment.Id = service.AddJob(experiment); // add parent task parentJob.JobId = experiment.Id; parentJob.Id = service.AddTask(parentJob, parentJobData, new List { slave.Id }); // add child task childJob.JobId = experiment.Id; childJob.Id = service.AddChildTask(parentJob.Id, childJob, childJobData); childJob.ParentTaskId = parentJob.Id; // test child task var childJobLoaded = service.GetTask(childJob.Id); Assert.AreEqual(childJob.ParentTaskId, childJobLoaded.ParentTaskId); Assert.AreEqual(childJob.JobId, childJobLoaded.JobId); Assert.AreEqual(DT.TaskState.Waiting, childJobLoaded.State); Assert.AreEqual(false, childJobLoaded.FinishWhenChildJobsFinished); Assert.AreEqual(false, childJobLoaded.IsParentTask); // test parent task var parentJobLoaded = service.GetTask(parentJob.Id); Assert.AreEqual(parentJob.JobId, parentJobLoaded.JobId); Assert.AreEqual(TaskState.Waiting, parentJobLoaded.State); Assert.AreEqual(true, parentJobLoaded.FinishWhenChildJobsFinished); Assert.AreEqual(true, parentJobLoaded.IsParentTask); // test heartbeat var actions = service.Heartbeat(new DT.Heartbeat() { SlaveId = slave.Id, AssignJob = true, FreeCores = 4, FreeMemory = 1024, JobProgress = new Dictionary() }); Assert.AreEqual(1, actions.Count); // only the child task should be assigned Assert.AreEqual(MessageContainer.MessageType.CalculateTask, actions[0].Message); Assert.AreEqual(childJob.Id, actions[0].TaskId); // lifecycle - let it process one server-heartbeat; the parent task must NOT be set to finished service.TriggerEventManager(true); parentJobLoaded = service.GetTask(parentJob.Id); Assert.AreEqual(TaskState.Waiting, parentJobLoaded.State); // set child task to finished childJobLoaded = service.UpdateTaskState(childJobLoaded.Id, DT.TaskState.Finished, slave.Id, null, null); // lifecycle - let it process one server-heartbeat; this should set the parent task to finished service.TriggerEventManager(true); // test if parent task is finished parentJobLoaded = service.GetTask(parentJob.Id); Assert.AreEqual(TaskState.Finished, parentJobLoaded.State); // delete experiment service.DeleteJob(experiment.Id); Assert.AreEqual(null, service.GetTask(parentJob.Id)); Assert.AreEqual(null, service.GetTask(childJob.Id)); service.DeleteSlave(slave.Id); } [TestMethod] public void TestHiveExperimentPermissions() { var service = GetLocalService(); mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId1); // create hive experiment DT.Job e1 = new DT.Job() { Name = "TestExperiment", Description = "" }; e1.Id = service.AddJob(e1); var e1loaded = service.GetJob(e1.Id); Assert.AreEqual(Permission.Full, e1loaded.Permission); var allExp = service.GetJobs(); Assert.AreEqual(1, allExp.Count(x => x.Id == e1.Id)); // change to user2 mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId2); try { e1loaded = service.GetJob(e1.Id); Assert.Fail("Access should not be possible"); } catch { /* ok, cool */ } allExp = service.GetJobs(); Assert.AreEqual(0, allExp.Count(x => x.Id == e1.Id)); // user2 should not be able to grant permissions try { service.GrantPermission(e1.Id, MockUserManager.MockUserId2, DT.Permission.Read); Assert.Fail("Should not be possible to grant permission due to missing permission for User2"); } catch { /* ok, cool */ } // switch back to user1 (owner) and grant user2 permissions mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId1); service.GrantPermission(e1.Id, MockUserManager.MockUserId2, DT.Permission.Read); // back to user2 mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId2); e1loaded = service.GetJob(e1.Id); Assert.AreEqual(Permission.Read, e1loaded.Permission); allExp = service.GetJobs(); Assert.AreEqual(1, allExp.Count(x => x.Id == e1.Id)); // user2 should still not be able to grant permissions try { service.GrantPermission(e1.Id, MockUserManager.MockUserId2, DT.Permission.Read); Assert.Fail("Should not be possible to grant permission due to missing permission for User2"); } catch { /* ok, cool */ } // back to user1 mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId1); service.GrantPermission(e1.Id, MockUserManager.MockUserId2, DT.Permission.Full); // back to user2 mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId2); e1loaded = service.GetJob(e1.Id); Assert.AreEqual(Permission.Full, e1loaded.Permission); allExp = service.GetJobs(); Assert.AreEqual(1, allExp.Count(x => x.Id == e1.Id)); // grant rights to user3, now this should be possible due to full permissions service.GrantPermission(e1.Id, MockUserManager.MockUserId3, DT.Permission.Read); // back to user1 and revoke rights for user2 mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId1); service.RevokePermission(e1.Id, MockUserManager.MockUserId2); // back to user2 mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId2); try { e1loaded = service.GetJob(e1.Id); Assert.Fail("Access should not be possible"); } catch { /* ok, cool */ } allExp = service.GetJobs(); Assert.AreEqual(0, allExp.Count(x => x.Id == e1.Id)); // back to user1 mockServiceLocator.SetCurrentUserId(MockUserManager.MockUserId1); service.DeleteJob(e1.Id); } } }