diff --git a/Automation/UnitTestCategories.cs b/Automation/UnitTestCategories.cs index fffa1a2d..b673a060 100644 --- a/Automation/UnitTestCategories.cs +++ b/Automation/UnitTestCategories.cs @@ -5,6 +5,7 @@ public static class UnitTestCategories { public const string ManualOnly = "ManualOnly"; public const string HouseholdsWithTransportation = "HouseholdsWithTransportation"; public const string HouseholdTest = "HouseholdTest"; + public const string HouseholdTemplateTest = "HouseholdTemplateTest"; public const string HousetypeTests = "HousetypeTests"; public const string CalcOptionTests = "CalcOptionTests"; public const string LongTest5 = "LongTest5"; diff --git a/Common/Logger.cs b/Common/Logger.cs index 2d5d4133..4b1aebf7 100644 --- a/Common/Logger.cs +++ b/Common/Logger.cs @@ -367,7 +367,7 @@ private void ReportString([JetBrains.Annotations.NotNull] string message, Severi } if (severity <= Threshold) { - if (OutputHelper!= null) { + if (OutputHelper != null && !Config.OutputToConsole) { OutputHelper.WriteLine(message); } else { diff --git a/Common/WorkingDir.cs b/Common/WorkingDir.cs index 0f5daa4f..cc0e955b 100644 --- a/Common/WorkingDir.cs +++ b/Common/WorkingDir.cs @@ -148,6 +148,7 @@ public void CleanUp(int numberOfFilesToTolerate = 0, bool throwAllErrors = true) [JetBrains.Annotations.NotNull] private static string InitializeWorkingDirectory([JetBrains.Annotations.NotNull] string testname, bool useRamdisk) { var baseWorkingDir = DetermineBaseWorkingDir(useRamdisk); + testname = ReplaceInvalidChars(testname); var resultdir = Path.Combine(baseWorkingDir, testname); try { if (Directory.Exists(resultdir)) { @@ -173,6 +174,25 @@ private static string InitializeWorkingDirectory([JetBrains.Annotations.NotNull] return resultdir; } + /// + /// Replaces all characters that are not allowed in file names with a fixed replacement character. + /// + /// The file name to adjust + /// The character to use instead of illegal characters + /// The adjusted file name without any illegal characters + public static string ReplaceInvalidChars(string filename, char replacementChar = '_') + { + foreach (char c in Path.GetInvalidFileNameChars()) + { + if (c == replacementChar) + { + throw new LPGException("Used an illegal character as replacementChar: '" + replacementChar + "'"); + } + filename = filename.Replace(c, replacementChar); + } + return filename; + } + /// /// Determines the base directory for any working directories used for tests. /// diff --git a/Directory.Build.props b/Directory.Build.props index 9d14262a..d8cf5895 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 10.7.0.6 - 10.7.0.6 + 10.8.0.1 + 10.8.0.1 - \ No newline at end of file + diff --git a/SimulationEngine.Tests/CalculationTests.cs b/SimulationEngine.Tests/CalculationTests.cs index ccca7a26..6832981d 100644 --- a/SimulationEngine.Tests/CalculationTests.cs +++ b/SimulationEngine.Tests/CalculationTests.cs @@ -34,6 +34,7 @@ namespace SimulationEngine.Tests { public enum TestDuration { + ThreeDays, OneMonth, ThreeMonths, TwelveMonths @@ -52,6 +53,7 @@ public static HouseCreationAndCalculationJob PrepareExistingHouseForTesting([Jet public static HouseCreationAndCalculationJob PrepareNewHouseForHouseholdTesting(Simulator sim, string guid, TestDuration duration) { + var hj = new HouseCreationAndCalculationJob { CalcSpec = JsonCalcSpecification.MakeDefaultsForTesting() @@ -86,17 +88,22 @@ private static void SetEndDate(TestDuration duration, HouseCreationAndCalculatio if (hj.CalcSpec == null) { throw new LPGException("was null"); } - if (duration == TestDuration.OneMonth) { - hj.CalcSpec.EndDate = new DateTime(2020, 2, 1); - } - else if (duration == TestDuration.ThreeMonths) { - hj.CalcSpec.EndDate = new DateTime(2020, 4, 1); - } - else if (duration == TestDuration.TwelveMonths) { - hj.CalcSpec.EndDate = new DateTime(2020, 12, 31); - } - else { - throw new LPGException("Unkown duration"); + switch (duration) + { + case TestDuration.ThreeDays: + hj.CalcSpec.EndDate = new DateTime(2020, 1, 4); + break; + case TestDuration.OneMonth: + hj.CalcSpec.EndDate = new DateTime(2020, 2, 1); + break; + case TestDuration.ThreeMonths: + hj.CalcSpec.EndDate = new DateTime(2020, 4, 1); + break; + case TestDuration.TwelveMonths: + hj.CalcSpec.EndDate = new DateTime(2020, 12, 31); + break; + default: + throw new LPGException("Unkown duration"); } } @@ -215,7 +222,7 @@ public static void CheckForResultfile(string wd, CalcOption option) { var peakWorkingSet = Process.GetCurrentProcess().PeakWorkingSet64; - const long memoryCap = 1024l * 1024l * 2000l * 2l; + const long memoryCap = 1024L * 1024L * 2000L * 2L; peakWorkingSet.Should().BeLessThan(memoryCap); GC.Collect(); GC.WaitForPendingFinalizers(); @@ -370,7 +377,7 @@ public static void CheckForResultfile(string wd, CalcOption option) //} } - public static void RunSingleHouse(Func makeHj, Action checkResults, + public static void RunSingleHouse(Func makeHj, Action checkResults, bool skipcleaning = false) { Logger.Get().StartCollectingAllMessages(); @@ -384,14 +391,64 @@ public static void RunSingleHouse(Func + /// Generates one household from a household template and simulates them. + /// + /// A unique name of the calling test for setting up a working directory + /// The Guid of the household template to use + /// Duration for which the household should be simulated + public static void GenerateAndSimulateHHFromTemplate(string testID, StrGuid hhTemplateGuid, TestDuration duration) + { + Logger.Get().StartCollectingAllMessages(); + // set up a working directory + using var wd = new WorkingDir(testID); + wd.SkipCleaning = false; + // get a database connection + using var db = new DatabaseSetup(testID); + Directory.SetCurrentDirectory(wd.WorkingDirectory); + Simulator sim; + try + { + sim = new Simulator(db.ConnectionString); + } + catch (FormatException) + { + // during tests infrequent problems occur when loading and parsing dates from the database + // --> save the database in another folder + string backupPath = Path.Combine(WorkingDir.DetermineBaseWorkingDir(true), "InvalidDatetime_DB_Backup"); + string dbFilename = Path.GetFileName(db.FileName); + string destinationFile = Path.Combine(backupPath, dbFilename); + Directory.CreateDirectory(backupPath); + File.Copy(db.FileName, destinationFile); + throw; + } + // find the household template to use + var hhTemplate = sim.HouseholdTemplates.FindByGuid(hhTemplateGuid); + hhTemplate.Should().NotBeNull(); + Logger.Info("Testing generation and simulation of a household using household template \"" + hhTemplate.Name + "\""); + // generate one household + hhTemplate.Count = 1; + var households = hhTemplate.GenerateHouseholds(sim, false, new List(), new List()); + households.Should().HaveCount(1); + var household = households[0]; + // prepare and execute the house job + var hj = HouseJobCalcPreparer.PrepareNewHouseForHouseholdTesting(sim, household.Guid.StrVal, duration); + if (hj.CalcSpec?.CalcOptions == null) { throw new LPGException("No CalcOptions were set"); } + hj.CalcSpec.EnableIdlemode = true; + hj.CalcSpec.DefaultForOutputFiles = OutputFileDefault.OnlySums; + var houseGenerator = new HouseGenerator(); + houseGenerator.ProcessSingleHouseJob(hj, sim); + } } @@ -813,6 +870,85 @@ public void GenerateSystematicHouseholdTestsWithTransport() sw.Close(); } + /// + /// Tests generation of households using a household template. Generates 100 households for each + /// template, and simulates each household for 3 days. + /// + /// Guid of the template to test + /// Name of the template to test + /// Repetition of this test for the same template + /// Each call of this method only handles a single household + [Theory] + [MemberData(nameof(GetEachHHTemplate100Times))] + [Trait(UnitTestCategories.Category, UnitTestCategories.HouseholdTemplateTest)] + public void SystematicHouseholdTemplateTestShort(StrGuid hhTemplateGuid, string hhTemplateName, int repetition) + { + string testID = "CalculationTests.SystematicHouseholdTemplateTestShort." + hhTemplateName + "." + repetition; + HouseJobTestHelper.GenerateAndSimulateHHFromTemplate(testID, hhTemplateGuid, TestDuration.ThreeDays); + } + + /// + /// Tests generation of households using a household template. Generates 3 households for each + /// template, and simulates each household for one year. + /// + /// Guid of the template to test + /// Name of the template to test + /// Repetition of this test for the same template + /// Each call of this method only handles a single household + [Theory] + [MemberData(nameof(GetEachHHTemplate3Times))] + [Trait(UnitTestCategories.Category, UnitTestCategories.HouseholdTemplateTest)] + public void SystematicHouseholdTemplateTestLong(StrGuid hhTemplateGuid, string hhTemplateName, int repetition) + { + string testID = "CalculationTests.SystematicHouseholdTemplateTestLong." + hhTemplateName + "." + repetition; + HouseJobTestHelper.GenerateAndSimulateHHFromTemplate(testID, hhTemplateGuid, TestDuration.TwelveMonths); + } + + /// + /// Creates an enumerable of all household template guids and names, repeating each template 100 times. + /// + /// An of household template guids and names + public static IEnumerable GetEachHHTemplate100Times() + { + const string testname = "CalculationTests.GetEachHHTemplate100Times"; + return CreateTemplateList(testname, 100); + } + + /// + /// Creates an enumerable of all household template guids and names, repeating each template 3 times. + /// + /// An of household template guids and names + public static IEnumerable GetEachHHTemplate3Times() + { + const string testname = "CalculationTests.GetEachHHTemplate3Times"; + return CreateTemplateList(testname, 3); + } + + /// + /// Creates an of the guids and names of all household templates, including every template multiple times. + /// Can be used to create test cases for template tests, where each template should be used to generate multiple households. + /// + /// The name of the test, for creation of a unique database copy + /// How often each template will be repeated + /// An of object arrays, each including name and guid of one household template and the + /// repetition index (how often this template has been repeated already) + /// The return type needs to be an of object arrays for the testing framework. + public static IEnumerable CreateTemplateList(string testname, int repetitionCount = 1) + { + // configure output to console because the instance of class CalculationTests has not been + // created yet and therefore no TestOutputHelper is available + var outputToConsole = Config.OutputToConsole; + Config.OutputToConsole = true; + // get a database connection + using var db = new DatabaseSetup(testname); + var sim = new Simulator(db.ConnectionString); + // reset OutputToConsole, so that the actual tests might use TestOutputHelpers + Config.OutputToConsole = outputToConsole; + // return each template as often as specified, together with the index + return sim.HouseholdTemplates.Items.SelectMany( + template => Enumerable.Range(0, repetitionCount).Select(i => new object[] { template.Guid, template.Name, i })); + } + [Fact] [Trait(UnitTestCategories.Category, UnitTestCategories.ManualOnly)] public void GenerateHouseTypeTests() @@ -1183,4 +1319,4 @@ public SingleCalculationTests([JetBrains.Annotations.NotNull] ITestOutputHelper { } } -} \ No newline at end of file +} diff --git a/SimulationEngineLib/PythonGenerator.cs b/SimulationEngineLib/PythonGenerator.cs index b6774538..09ebfff5 100644 --- a/SimulationEngineLib/PythonGenerator.cs +++ b/SimulationEngineLib/PythonGenerator.cs @@ -57,7 +57,7 @@ public void MakePythonData([JetBrains.Annotations.NotNull] string connectionStri //sw.WriteLine("from dataclasses import dataclass, field"); //sw.WriteLine("from dataclasses_json import dataclass_json # type: ignore"); //sw.WriteLine("from typing import List, Optional, Any, Dict"); - sw.WriteLine("from lpgpythonbindings import *"); + sw.WriteLine("from pylpg.lpgpythonbindings import *"); //sw.WriteLine("from enum import Enum"); sw.WriteLine(); WriteNames(sim.LoadTypes.Items.Select(x => (DBBase)x).ToList(), sw, "LoadTypes"); diff --git a/VersionInfo.cs b/VersionInfo.cs index 68a7214c..23fa6b8e 100644 --- a/VersionInfo.cs +++ b/VersionInfo.cs @@ -1,3 +1,3 @@ -[assembly: AssemblyVersion("10.7.0.6")] +[assembly: AssemblyVersion("10.8.0.1")] -[assembly: AssemblyFileVersion("10.7.0.6")] \ No newline at end of file +[assembly: AssemblyFileVersion("10.8.0.1")] diff --git a/copynewToPython.cmd b/copynewToPython.cmd index 89d17e5f..da211ce4 100644 --- a/copynewToPython.cmd +++ b/copynewToPython.cmd @@ -28,7 +28,7 @@ pause REM create pylpg -set "releasedirectory=C:\LPGReleaseMakerResults\LPGReleases\releases10.7.0" +set "releasedirectory=C:\LPGReleaseMakerResults\LPGReleases\releases10.8.0" set "pylpgdirectory=C:\LPGPythonBindings\pylpg\" cd %releasedirectory%\net48