Exemple #1
0
        public void MaxRectsAlgorithmTestSingleSquarePackSixteenSquaresIntoLarge4x4Square()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = Enumerable.Repeat(new PPRect(0, 0, 64, 64), 16);

            //Act
            var result = algo.PlaceRects(256, 256, rects);

            //Assert
            Assert.AreNotEqual(null, result);
            Assert.AreEqual(256, result.Width);
            Assert.AreEqual(256, result.Height);
            Assert.AreEqual(true, TestUtil.IsPackingResultValid(result));
            Assert.AreEqual(16, result.Rects.Count());

            List <PPRect> expectedSuperset = new List <PPRect>();

            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    expectedSuperset.Add(new PPRect(i * 64, j * 64, i * 64 + 64, j * 64 + 64));
                }
            }

            CollectionAssert.IsSubsetOf(result.Rects.ToList(), expectedSuperset);
        }
Exemple #2
0
        public void MaxRectsAlgorithmTestSingleSquarePackFourSquaresIntoLarge2x2Square()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = Enumerable.Repeat(new PPRect(0, 0, 64, 64), 4);

            //Act
            var result = algo.PlaceRects(128, 128, rects);

            //Assert
            Assert.AreNotEqual(null, result);
            Assert.AreEqual(128, result.Width);
            Assert.AreEqual(128, result.Height);
            Assert.AreEqual(true, TestUtil.IsPackingResultValid(result));
            Assert.AreEqual(4, result.Rects.Count());

            List <PPRect> expectedSuperset = new List <PPRect>()
            {
                new PPRect(0, 0, 64, 64),
                new PPRect(0, 64, 64, 128),
                new PPRect(64, 0, 128, 64),
                new PPRect(64, 64, 128, 128)
            };

            CollectionAssert.IsSubsetOf(result.Rects.ToList(), expectedSuperset);
        }
Exemple #3
0
        public void MaxRectsAlgorithmTestNullInput()
        {
            //Arrange
            var algo = new MaximalRectanglesAlgorithm();
            IEnumerable <PPRect> rects = null;

            //Act
            algo.PlaceRects(63, 63, rects);
        }
Exemple #4
0
        public void MaxRectsAlgorithmTestSingleSquareNoFitInBothDirections()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = Enumerable.Repeat(new PPRect(0, 0, 64, 64), 1);

            //Act
            var result = algo.PlaceRects(63, 63, rects);

            //Assert
            Assert.AreEqual(null, result);
        }
Exemple #5
0
        public void MaxRectsAlgorithmTestSingleSquarePackSeventeenSquaresIntoLarge4x4Square()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = Enumerable.Repeat(new PPRect(0, 0, 64, 64), 17);

            //Assert
            var result = algo.PlaceRects(256, 256, rects);

            //Act
            Assert.AreEqual(null, result);
        }
Exemple #6
0
        public void MaxRectsAlgorithmTestSingleSquareFitSideBySideHorizontally()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = Enumerable.Repeat(new PPRect(0, 0, 64, 64), 10);

            //Act
            var result = algo.PlaceRects(1000, 64, rects);

            //Assert
            Assert.AreNotEqual(null, result);
            Assert.AreEqual(1000, result.Width);
            Assert.AreEqual(64, result.Height);
            Assert.AreEqual(true, TestUtil.IsPackingResultValid(result));
            CollectionAssert.AreEqual(Enumerable.Range(0, 10).Select(i => new PPRect(i * 64, 0, i * 64 + 64, 64)).ToList(), result.Rects.ToList());
        }
Exemple #7
0
        public void MaxRectsAlgorithmTestSingleSquareExactFit()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = Enumerable.Repeat(new PPRect(0, 0, 64, 64), 1);

            //Act
            var result = algo.PlaceRects(64, 64, rects);

            //Assert
            Assert.AreNotEqual(null, result);
            Assert.AreEqual(64, result.Width);
            Assert.AreEqual(64, result.Height);
            Assert.AreEqual(true, TestUtil.IsPackingResultValid(result));
            CollectionAssert.AreEqual(rects.ToList(), result.Rects.ToList());
        }
Exemple #8
0
        public void MaxRectsAlgorithmTestTwoDistinctRectsThatDoesNotFit()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = new List <PPRect>
            {
                new PPRect(0, 0, 32, 32),
                new PPRect(0, 0, 32, 32),
                new PPRect(0, 0, 64, 64),
                new PPRect(0, 0, 300, 300),
                new PPRect(0, 0, 512, 512)
            };

            //Act
            var result = algo.PlaceRects(256, 256, rects);

            //Assert
            Assert.AreEqual(null, result);
        }
Exemple #9
0
        public void MaxRectsAlgorithmTestEmptyInput()
        {
            //Arrange
            var algo  = new MaximalRectanglesAlgorithm();
            var rects = Enumerable.Empty <PPRect>();

            //Act
            var result = algo.PlaceRects(63, 63, rects);

            //Assert
            Assert.AreNotEqual(null, result);
            Assert.AreEqual(63, result.Width);
            Assert.AreEqual(63, result.Height);
            int actualWidth  = result.Rects.Any() ? result.Rects.Max(x => x.Right) : 0;
            int actualHeight = result.Rects.Any() ? result.Rects.Max(x => x.Bottom) : 0;

            Assert.AreEqual(0, actualWidth);
            Assert.AreEqual(0, actualHeight);
            Assert.AreEqual(0, result.Rects?.Count() ?? 0);
        }
Exemple #10
0
        public void MaxRectsAlgorithmTestOutperformsGuillotine()
        {
            //Arrange (both with PreserveOrderImageSorter)
            var maxRectsAlgo   = new MaximalRectanglesAlgorithm(new PreserveOrderImageSorter());
            var guillotineAlgo = new GuillotinePlacementAlgorithm(new BestAreaFitFreeRectangleExtractor(), new LongerAxisGuillotineFreeRectangleSplitter(), null, null,
                                                                  null, new PreserveOrderImageSorter());

            var rects = new List <PPRect>()
            {
                new PPRect(0, 0, 50, 10),
                new PPRect(0, 0, 10, 100)
            };

            //Act
            var result  = maxRectsAlgo.PlaceRects(90, 100, rects);
            var result2 = guillotineAlgo.PlaceRects(90, 100, rects);

            //Assert
            Assert.AreNotEqual(null, result);
            Assert.AreEqual(null, result2);
        }
        /// <summary>
        /// Accepts additional parameters and runs the tests inside the given class
        /// </summary>
        /// <typeparam name="T">The type of class that contains the tests</typeparam>
        private static void RunTests <T>()
        {
            if (typeof(T) != typeof(IPlacementAlgorithm) &&
                typeof(T) != typeof(IMinimumBoundingBoxFinder))
            {
                throw new NotSupportedException();
            }

            string str = "";

            IImageSorter        selectedImageSorter        = null;
            IPlacementAlgorithm selectedPlacementAlgorithm = null;

            //Ask the user to select an image sorter that will parametrize the placement algorithm / minimum bounding box finder
            while (true)
            {
                do
                {
                    Console.WriteLine("Select image sorter to be used: ");
                    Console.WriteLine($"\tEnter 'a' to select {typeof(ByHeightAndWidthImageSorter).Name}");
                    Console.WriteLine($"\tEnter 'b' to select {typeof(ByHeightAndWidthImageSorterDesc).Name}");
                    Console.WriteLine($"\tEnter 'c' to select {typeof(PreserveOrderImageSorter).Name}");
                } while ((str = Console.ReadLine()).Length != 1);

                switch (str[0])
                {
                case 'a':
                    selectedImageSorter = new ByHeightAndWidthImageSorter();
                    break;

                case 'b':
                    selectedImageSorter = new ByHeightAndWidthImageSorterDesc();
                    break;

                case 'c':
                    selectedImageSorter = new PreserveOrderImageSorter();
                    break;

                default:
                    continue;
                }

                break;
            }

            //If the users wants to benchmark Minimum bounding box finders
            //  then ask the user to select an placement algorithm that will parametrize tminimum bounding box finders
            if (typeof(T) == typeof(IMinimumBoundingBoxFinder))
            {
                while (true)
                {
                    do
                    {
                        Console.WriteLine("Select placement algorithm to be used: ");
                        Console.WriteLine($"\tEnter 'a' to select {typeof(BLAlgorithmPacker).Name}");
                        Console.WriteLine($"\tEnter 'b' to select {typeof(SkylineAlgorithm).Name}");
                        Console.WriteLine($"\tEnter 'c' to select {typeof(MaximalRectanglesAlgorithm).Name}");
                    } while ((str = Console.ReadLine()).Length != 1);

                    switch (str[0])
                    {
                    case 'a':
                        selectedPlacementAlgorithm = new BLAlgorithmPacker();
                        break;

                    case 'b':
                        selectedPlacementAlgorithm = new SkylineAlgorithm();
                        break;

                    case 'c':
                        selectedPlacementAlgorithm = new MaximalRectanglesAlgorithm();
                        break;

                    default:
                        continue;
                    }

                    break;
                }
            }

            bool isItSquaresTest = true;
            int  numOfRects      = 0;
            int  seed            = 0;

            //Ask the user to select test type
            //Currently, two types of tests are implemented (differing in the input sequence)
            // 1) Input sequence consisting of n squares with sizes 1x1, ..., nxn (given in random order)
            // 2) Input sequence consisting of n rectangles with random (but upper-bounded) sizes (given in random order)
            while (true)
            {
                str = "";
                do
                {
                    Console.WriteLine("Select test type:");
                    Console.WriteLine("\tEnter 'a' to perform test with squares of sizes 1x1, 2x2, ..., nxn");
                    Console.WriteLine("\tEnter 'b' to perform test with n rectangles of random sizes");
                } while ((str = Console.ReadLine()).Length != 1);

                switch (str[0])
                {
                case 'b':
                case 'a':

                    isItSquaresTest = str[0] == 'a';

                    //Ask the user to enter number of rectangles/squares
                    while (true)
                    {
                        Console.WriteLine("\tEnter number of 'n' - rectangles(squares)");
                        str = Console.ReadLine();
                        if (Int32.TryParse(str, out numOfRects))
                        {
                            if (numOfRects > 0)
                            {
                                break;
                            }
                        }
                    }

                    //If it is the random rectangles test then ask the user to enter the random seed
                    //  The seed will be user to construct the random generator used to generate the random rectangle sizes
                    if (!isItSquaresTest)
                    {
                        while (true)
                        {
                            Console.WriteLine("\tEnter seed");
                            str = Console.ReadLine();
                            if (Int32.TryParse(str, out seed))
                            {
                                break;
                            }
                        }
                    }

                    break;

                default:
                    continue;
                }
                break;
            }

            //Now when the user has selected the type of test and all the parameters
            // It is time to actaully run the tests. But because we need to test types
            // that are contained within plugins and also because BenchmarkDotNet (in a standard scenario)
            // requires to use (static) attribute [Benchmark] on the methods that should be called
            // we have decided to generate the benchmark class (with benchmark methods) dynamically at runtime
            // and then compile the generated class and pass the type of this class to the BenchmarkRunner

            string assemblyName = Path.GetRandomFileName();

            //Assemblies referenced by the assembly that will be generated at runtime
            //For each type that will be used in the generated test, an assembly containing
            //the used type has to be loaded
            var references = new List <MetadataReference>()
            {
                MetadataReference.CreateFromFile(typeof(BenchmarkDotNet.Attributes.BenchmarkAttribute).Assembly.Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("netstandard")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("mscorlib")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Unity.Container")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Unity.Abstractions")).Location),
                MetadataReference.CreateFromFile(typeof(IMinimumBoundingBoxFinder).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(TestUtil).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(CancellationToken).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(UnityContainerExtensions).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Random).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.Collections.Concurrent.ConcurrentDictionary <string, int>).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(ValueTuple <long, long>).Assembly.Location)
            };

            //Create same IoC here and in the generated class
            //This is because inside this class we try if (using the same IoC container with same registrations)
            //a given type could be resolved and if it does not, we exclude benchmark for this type in the generated class
            using UnityContainer IoC = new UnityContainer();
            IoC.RegisterFactory <IImageSorter>(_ => selectedImageSorter);

            if (selectedPlacementAlgorithm != null)
            {
                IoC.RegisterFactory <IPlacementAlgorithm>(_ => selectedPlacementAlgorithm);
            }

            //The generated source
            var benchmarkClassTemplate = new StringBuilder();

            //Using statements
            benchmarkClassTemplate.AppendLine("using System;");
            benchmarkClassTemplate.AppendLine("using Unity;");
            benchmarkClassTemplate.AppendLine("using System.Linq;");
            benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.MBBF;");
            benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement;");
            benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Sorting;");
            benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement.Skyline;");
            benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement.Guillotine;");
            benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement.MaximalRectangles;");
            benchmarkClassTemplate.AppendLine("using System.Collections.Generic;");
            benchmarkClassTemplate.AppendLine("using System.Threading;");
            benchmarkClassTemplate.AppendLine("using PaunPacker.Tests;");

            //Beginning of the class
            benchmarkClassTemplate.AppendLine($"public class {typeof(T).Name}Benchmarks");
            benchmarkClassTemplate.AppendLine("{");
            //Member declarations
            benchmarkClassTemplate.AppendLine(@"
                private static UnityContainer IoC { get; set; }
                private static Random rnd;
                private static List<PaunPacker.Core.Types.PPRect> randomRects;
                static System.Collections.Concurrent.ConcurrentDictionary<string, ValueTuple<long, long>> results;
");
            benchmarkClassTemplate.AppendLine($"\n\tstatic {typeof(T).Name}Benchmarks()");
            benchmarkClassTemplate.AppendLine("\t{");

            //Constructor code
            benchmarkClassTemplate.AppendLine($@"
                IoC = new UnityContainer();
                IoC.RegisterInstance<IImageSorter>(new {selectedImageSorter.GetType().Name}());

                results = new System.Collections.Concurrent.ConcurrentDictionary<string, ValueTuple<long, long>>();

                rnd = new Random(" + seed + @");
                randomRects = new List<PaunPacker.Core.Types.PPRect>(" + numOfRects + @");
                
                int dimensionBound = Int32.MaxValue / " + numOfRects + @";
                dimensionBound = dimensionBound > 2049 ? 2049 : dimensionBound;

                for (int i = 0; i < " + numOfRects + @"; i++)
                {
                    randomRects.Add(new PaunPacker.Core.Types.PPRect(0, 0, rnd.Next(1, dimensionBound), rnd.Next(1, dimensionBound)));
                }
");

            if (selectedPlacementAlgorithm != null)
            {
                benchmarkClassTemplate.AppendLine($"IoC.RegisterInstance<IPlacementAlgorithm>(new { selectedPlacementAlgorithm.GetType().Name }());");
            }

            benchmarkClassTemplate.AppendLine("\t}");
            //Constructor ends here

            if (isItSquaresTest)
            {
                benchmarkClassTemplate.AppendLine($@"
                [BenchmarkDotNet.Attributes.ParamsSourceAttribute(nameof(ValuesForN))]
                public int N;
                public IEnumerable<int> ValuesForN => System.Linq.Enumerable.Range(1, {numOfRects});
");
            }

            int numOfTests = 0;

            // We are testing minimum bounding box finders, so generate tests for them
            if (selectedPlacementAlgorithm != null)
            {
                foreach (var x in MinimumBoundingBoxFinderTypes)
                {
                    try
                    {
                        UnityContainerExtensions.Resolve(IoC, Type.GetType(x.AssemblyQualifiedName), null);
                        references.Add(MetadataReference.CreateFromFile(x.Assembly.Location));
                        benchmarkClassTemplate.AppendLine("\t[BenchmarkDotNet.Attributes.BenchmarkAttribute]");
                        benchmarkClassTemplate.AppendLine($"\tpublic void Test{x.Name}()");
                        benchmarkClassTemplate.AppendLine("{");
                        benchmarkClassTemplate.AppendLine($"\tvar x = Type.GetType(\"{x.AssemblyQualifiedName}\");");
                        benchmarkClassTemplate.AppendLine($"\tvar res = (UnityContainerExtensions.Resolve(IoC, x, null) as IMinimumBoundingBoxFinder).FindMinimumBoundingBox(TestUtil.Shuffle(" + (isItSquaresTest ? "TestUtil.GetIncreasingSquares(N)" : $"randomRects") + "), CancellationToken.None);");
                        benchmarkClassTemplate.AppendLine($"results.AddOrUpdate(\"Test{x.Name}\", (res.Width * res.Height, 1), (key, old) => ((long)(old.Item1 + (double)(res.Width * res.Height - old.Item1) / (double)(old.Item2 + 1)), old.Item2 + 1));");
                        benchmarkClassTemplate.AppendLine("}");
                        benchmarkClassTemplate.AppendLine();
                        numOfTests++;
                    }
                    catch (ResolutionFailedException)
                    {
                        //Do not add benchmark for types that could not be resolved (for example types that are extensible by other plugins
                        // via plugin view ...). These types will simply not be benchmarked.
                    }
                }
            }
            else
            {
                foreach (var x in PlacementAlgorithmTypes)
                {
                    try
                    {
                        UnityContainerExtensions.Resolve(IoC, Type.GetType(x.AssemblyQualifiedName), null);
                        references.Add(MetadataReference.CreateFromFile(x.Assembly.Location));
                        benchmarkClassTemplate.AppendLine("\t[BenchmarkDotNet.Attributes.BenchmarkAttribute]");
                        benchmarkClassTemplate.AppendLine($"\tpublic void Test{x.Name}()");
                        benchmarkClassTemplate.AppendLine("{");
                        benchmarkClassTemplate.AppendLine($"\tvar x = Type.GetType(\"{x.AssemblyQualifiedName}\");");
                        benchmarkClassTemplate.AppendLine($"\tvar res = (UnityContainerExtensions.Resolve(IoC, x, null) as IPlacementAlgorithm).PlaceRects(Int32.MaxValue, Int32.MaxValue, TestUtil.Shuffle(" + (isItSquaresTest ? "TestUtil.GetIncreasingSquares(N)" : $"randomRects") + "));");
                        benchmarkClassTemplate.AppendLine("long actualW = res.Rects.Max(y => y.Right); long actualH = res.Rects.Max(y => y.Bottom);");
                        benchmarkClassTemplate.AppendLine($"checked {{ results.AddOrUpdate(\"Test{x.Name}\", (actualW * actualH, 1), (key, old) => ((long)(old.Item1 + (double)(actualW * actualH - old.Item1) / (double)(old.Item2 + 1)), old.Item2 + 1));");
                        benchmarkClassTemplate.AppendLine("}}");
                        benchmarkClassTemplate.AppendLine();
                        numOfTests++;
                    }
                    catch (ResolutionFailedException)
                    {
                        //Do not add benchmark for types that could not be resolved (for example types that are extensible by other plugins
                        // via plugin view ...). These types will simply not be benchmarked.
                    }
                }
            }

            //Global cleanup method
            //Because BenchmarkDotNet does not allow to report benchmark methods return values
            //We had to "hack" around it by using these two methods
            //And save the method results inside a file and then (when showing the report) load it from the file
            //The GlobalCleanup should not be measured as a part of the benchmark
            //However we still need to somewhere remember the return value of the methods
            //Because writing to file in the benchmark method itself would totally kill the performance, we have decided to store it in dictionary
            //Which still causes some test distortion but it should be OK (all the tests use it so their base-line is just shifted)
            benchmarkClassTemplate.AppendLine(@"

            [BenchmarkDotNet.Attributes.GlobalCleanup]
            public void GlobalCleanup()
            {
                //We want to call it only once, not after every [Benchmark] method
                if (results.Count() == " + numOfTests + @")
                {
                    using (var sw = new System.IO.StreamWriter(""results.txt"", true))
                    {
                        //foreach (var r in randomRects) //for debug purposes
                        //    sw.WriteLine(r.Width + ""x"" + r.Height);
                        foreach (var res in results)
                            sw.WriteLine(res.Key + "";"" + res.Value.Item1 + "";"" + res.Value.Item1 + "";"" + res.Value.Item2);
                    }
                    results.Clear();
                }
            }
");

            benchmarkClassTemplate.AppendLine("}");
            //Benchmark class ends here

            Console.WriteLine("Please wait ... (Compiling the Benchmarks)");

            //Create syntax tree and compile it
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(benchmarkClassTemplate.ToString());

            CSharpCompilation compilation = CSharpCompilation.Create(
                assemblyName,
                syntaxTrees: new[] { syntaxTree },
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release));

            //Emit the CIL into memory stream
            using var ms = new MemoryStream();
            EmitResult result = compilation.Emit(ms);

            if (!result.Success)
            {
                IEnumerable <Microsoft.CodeAnalysis.Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                                                                                                    diagnostic.IsWarningAsError ||
                                                                                                    diagnostic.Severity == DiagnosticSeverity.Error);

                foreach (Microsoft.CodeAnalysis.Diagnostic diagnostic in failures)
                {
                    Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                }
            }
            else
            {
                ms.Seek(0, SeekOrigin.Begin);
                Assembly assembly = Assembly.Load(ms.ToArray());
                Type     type     = assembly.GetType($"{typeof(T).Name}Benchmarks");
                Console.WriteLine("Starting the Benchmarks");
                var x = BenchmarkDotNet.Running.BenchmarkRunner.Run(type, new Config());
                Console.WriteLine("Done");
            }
        }