Skip to content

Commit 55cca9b

Browse files
MikhailKalynMikhail Kalyn
andauthored
[VisualTesting] Fix max length of dump name, add possible to create dump subfolders (#96)
* Added possible to create subfolders for dump * Migrated image format and maximum length of full file name path into settings.json * Add tests for VisualTesting Co-authored-by: Mikhail Kalyn <[email protected]>
1 parent ccd3702 commit 55cca9b

File tree

12 files changed

+282
-24
lines changed

12 files changed

+282
-24
lines changed

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Aquality.Selenium.Core.xml

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Configurations/IVisualizationConfiguration.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
namespace Aquality.Selenium.Core.Configurations
1+
using Aquality.Selenium.Core.Visualization;
2+
3+
namespace Aquality.Selenium.Core.Configurations
24
{
35
/// <summary>
46
/// Represents visualization configuration, used for image comparison.
57
/// </summary>
68
public interface IVisualizationConfiguration
79
{
10+
/// <summary>
11+
/// Image format for comparison.
12+
/// </summary>
13+
ImageFormat ImageFormat { get; }
14+
15+
/// <summary>
16+
/// Maximum length of full file name with path for image comparison.
17+
/// </summary>
18+
int MaxFullFileNameLength { get; }
19+
820
/// <summary>
921
/// Default threshold used for image comparison.
1022
/// </summary>

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Configurations/VisualizationConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Aquality.Selenium.Core.Utilities;
2+
using Aquality.Selenium.Core.Visualization;
23
using System.IO;
34

45
namespace Aquality.Selenium.Core.Configurations
@@ -20,6 +21,10 @@ public VisualizationConfiguration(ISettingsFile settingsFile)
2021
this.settingsFile = settingsFile;
2122
}
2223

24+
public virtual ImageFormat ImageFormat => ImageFormat.Parse(settingsFile.GetValueOrDefault(".visualization.imageExtension", ".png"));
25+
26+
public int MaxFullFileNameLength => settingsFile.GetValueOrDefault(".visualization.maxFullFileNameLength", 255);
27+
2328
public float DefaultThreshold => settingsFile.GetValueOrDefault(".visualization.defaultThreshold", 0.012f);
2429

2530
public int ComparisonWidth => settingsFile.GetValueOrDefault(".visualization.comparisonWidth", 16);

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Resources/Localization/be.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"loc.el.visual.getdifference.withthreshold": "Параўноўваем малюнак элемента з малюнкам памеру [{0}] з парогам адчувальнасці [{1}]",
3030
"loc.el.visual.difference.value": "Адрозненне паміж цяперашнім і пададзеным малюнкамі складае [{0}]",
3131
"loc.form.dump.save": "Захоўваем дамп формы з іменем [{0}]",
32+
"loc.form.dump.exceededdumpname": "Перавышаны ліміт даўжыні імені дампа. Канечны шлях: [{0}]",
3233
"loc.form.dump.imagenotsaved": "Не ўдалося захаваць малюнак элемента [{0}]: {1}",
3334
"loc.form.dump.compare": "Параўноўваем элементы формы з дампам [{0}]",
3435
"loc.form.dump.elementnotfound": "Элемент [{0}], знойдзены ў дампе, не быў знойдзены на форме",

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Resources/Localization/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"loc.el.visual.getdifference.withthreshold": "Comparing element's image to image of size [{0}] with threshold [{1}]",
3030
"loc.el.visual.difference.value": "The difference between the current and the given images is [{0}]",
3131
"loc.form.dump.save": "Saving dump of the form with name [{0}]",
32+
"loc.form.dump.exceededdumpname": "Dump name length exceeded. Final path: [{0}]",
3233
"loc.form.dump.imagenotsaved": "Failed to save image of the element [{0}]: {1}",
3334
"loc.form.dump.compare": "Comparing elements of the form to dump [{0}]",
3435
"loc.form.dump.elementnotfound": "Element [{0}] found in the dump but was not found on form",

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Resources/Localization/ru.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"loc.el.visual.getdifference.withthreshold": "Сравниваем изображение элемента с изображением размера [{0}] с уровнем шума [{1}]",
3030
"loc.el.visual.difference.value": "Отличие текущего изображения от предоставленного составляет [{0}]",
3131
"loc.form.dump.save": "Сохраняем дамп формы с именем [{0}]",
32+
"loc.form.dump.exceededdumpname": "Превышен лимит длины имени дампа. Конечный путь: [{0}]",
3233
"loc.form.dump.imagenotsaved": "Не удалось сохранить изображение элемента [{0}]: {1}",
3334
"loc.form.dump.compare": "Сравниваем элементы формы с дампом [{0}]",
3435
"loc.form.dump.elementnotfound": "Элемент [{0}], найденный в дампе, не был найден на форме",

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Resources/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"isEnabled": false
1818
},
1919
"visualization": {
20+
"imageExtension": ".png",
21+
"maxFullFileNameLength": 255,
2022
"defaultThreshold": 0.012,
2123
"comparisonWidth": 16,
2224
"comparisonHeight": 16,

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Visualization/DumpManager.cs

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
using System.Globalization;
88
using System.IO;
99
using System.Linq;
10+
using System.Text;
1011

1112
namespace Aquality.Selenium.Core.Visualization
1213
{
1314
public class DumpManager<T> : IDumpManager where T : IElement
1415
{
15-
private const string ImageFormat = ".png";
1616
public DumpManager(IDictionary<string, T> elementsForVisualization, string formName, IVisualizationConfiguration visualizationConfiguration, ILocalizedLogger localizedLogger)
1717
{
1818
ElementsForVisualization = elementsForVisualization;
@@ -29,6 +29,10 @@ public DumpManager(IDictionary<string, T> elementsForVisualization, string formN
2929

3030
protected ILocalizedLogger LocalizedLogger { get; }
3131

32+
protected ImageFormat ImageFormat => VisualizationConfiguration.ImageFormat;
33+
34+
protected int MaxFullFileNameLength => VisualizationConfiguration.MaxFullFileNameLength;
35+
3236
protected string DumpsDirectory => VisualizationConfiguration.PathToDumps;
3337

3438
public virtual float Compare(string dumpName = null)
@@ -39,10 +43,10 @@ public virtual float Compare(string dumpName = null)
3943
{
4044
throw new InvalidOperationException($"Dump directory [{directory.FullName}] does not exist.");
4145
}
42-
var imageFiles = directory.GetFiles($"*{ImageFormat}");
46+
var imageFiles = directory.GetFiles($"*{ImageFormat.Extension}");
4347
if (imageFiles.Length == 0)
4448
{
45-
throw new InvalidOperationException($"Dump directory [{directory.FullName}] does not contain any [*{ImageFormat}] files.");
49+
throw new InvalidOperationException($"Dump directory [{directory.FullName}] does not contain any [*{ImageFormat.Extension}] files.");
4650
}
4751
var existingElements = FilterElementsForVisualization().ToDictionary(el => el.Key, el => el.Value);
4852
var countOfUnproceededElements = existingElements.Count;
@@ -51,7 +55,7 @@ public virtual float Compare(string dumpName = null)
5155
var absentOnFormElementNames = new List<string>();
5256
foreach (var imageFile in imageFiles)
5357
{
54-
var key = imageFile.Name.Replace(ImageFormat, string.Empty);
58+
var key = imageFile.Name.Replace(ImageFormat.Extension, string.Empty);
5559
if (!existingElements.ContainsKey(key))
5660
{
5761
LocalizedLogger.Warn("loc.form.dump.elementnotfound", key);
@@ -93,7 +97,7 @@ public virtual void Save(string dumpName = null)
9397
{
9498
try
9599
{
96-
element.Value.Visual.Image.Save(Path.Combine(directory.FullName, $"{element.Key}.png"));
100+
element.Value.Visual.Image.Save(Path.Combine(directory.FullName, $"{element.Key}{ImageFormat.Extension}"), ImageFormat.Format);
97101
}
98102
catch (Exception e)
99103
{
@@ -133,18 +137,43 @@ protected virtual DirectoryInfo CleanUpAndGetDumpDirectory(string dumpName = nul
133137
return dirInfo;
134138
}
135139

140+
protected virtual int GetMaxNameLengthOfDumpElements() => ElementsForVisualization.Max(element => element.Key == null ? 0 : element.Key.Length);
141+
136142
protected virtual DirectoryInfo GetDumpDirectory(string dumpName = null)
137143
{
138-
const int maxNameLenght = 40;
144+
// get the maximum length of the name among the form elements for the dump
145+
var maxNameLengthOfDumpElements = GetMaxNameLengthOfDumpElements() + ImageFormat.Extension.Length;
146+
147+
// get array of subfolders in dump name
148+
var dumpSubfoldersNames = (dumpName ?? FormName).Split('\\');
149+
150+
// get invalid characters that can not be in folder name
139151
var invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
140-
var name = dumpName ?? FormName;
141-
foreach (var character in invalid)
152+
153+
// create new dump name without invalid chars for each subfolder
154+
var validDumpName = new StringBuilder();
155+
foreach (string folderName in dumpSubfoldersNames)
156+
{
157+
string folderNameCopy = folderName;
158+
foreach (var character in invalid)
159+
{
160+
folderNameCopy = folderNameCopy.Replace(character, ' ');
161+
}
162+
validDumpName.Append($"{folderNameCopy}\\");
163+
}
164+
var validDumpNameString = validDumpName.ToString();
165+
166+
// create full dump path
167+
var fullDumpPath = Path.Combine(DumpsDirectory, validDumpNameString);
168+
169+
// cut off the excess length and log warn message
170+
if (fullDumpPath.Length + maxNameLengthOfDumpElements > MaxFullFileNameLength)
142171
{
143-
name = name.Replace(character, ' ');
172+
validDumpNameString = validDumpNameString.Substring(0, MaxFullFileNameLength - Path.GetFullPath(DumpsDirectory).Length - maxNameLengthOfDumpElements);
173+
LocalizedLogger.Warn("loc.form.dump.exceededdumpname", validDumpNameString);
144174
}
145-
name = name.Length > maxNameLenght ? name.Substring(0, maxNameLenght) : name;
146175

147-
return new DirectoryInfo(Path.Combine(DumpsDirectory, name));
176+
return new DirectoryInfo(Path.Combine(DumpsDirectory, validDumpNameString));
148177
}
149178
}
150179
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using DrawingImageFormat = System.Drawing.Imaging.ImageFormat;
3+
4+
namespace Aquality.Selenium.Core.Visualization
5+
{
6+
public sealed class ImageFormat
7+
{
8+
public string Extension { get; }
9+
public DrawingImageFormat Format { get; }
10+
11+
public static readonly ImageFormat Bmp = new ImageFormat(DrawingImageFormat.Bmp);
12+
public static readonly ImageFormat Emf = new ImageFormat(DrawingImageFormat.Emf);
13+
public static readonly ImageFormat Exif = new ImageFormat(DrawingImageFormat.Exif);
14+
public static readonly ImageFormat Gif = new ImageFormat(DrawingImageFormat.Gif);
15+
public static readonly ImageFormat Icon = new ImageFormat(".ico", DrawingImageFormat.Icon);
16+
public static readonly ImageFormat Jpg = new ImageFormat(".jpg", DrawingImageFormat.Jpeg);
17+
public static readonly ImageFormat Jpeg = new ImageFormat(DrawingImageFormat.Jpeg);
18+
public static readonly ImageFormat MemoryBmp = new ImageFormat(DrawingImageFormat.MemoryBmp);
19+
public static readonly ImageFormat Png = new ImageFormat(DrawingImageFormat.Png);
20+
public static readonly ImageFormat Tif = new ImageFormat(".tif", DrawingImageFormat.Tiff);
21+
public static readonly ImageFormat Tiff = new ImageFormat(DrawingImageFormat.Tiff);
22+
public static readonly ImageFormat Wmf = new ImageFormat(DrawingImageFormat.Wmf);
23+
24+
private ImageFormat(DrawingImageFormat format) : this($".{format.ToString().ToLower()}", format) { }
25+
private ImageFormat(string extension, DrawingImageFormat format)
26+
{
27+
Extension = extension;
28+
Format = format;
29+
}
30+
31+
/// <summary>
32+
/// Parse an image format from string representation to ImageFormat
33+
/// </summary>
34+
/// <param name="format">String representation of image format</param>
35+
/// <returns>ImageFormat version of string representation</returns>
36+
public static ImageFormat Parse(string format)
37+
{
38+
switch (format.ToLower())
39+
{
40+
case ".bmp":
41+
return Bmp;
42+
case ".emf":
43+
return Emf;
44+
case ".exif":
45+
return Exif;
46+
case ".gif":
47+
return Gif;
48+
case ".ico":
49+
return Icon;
50+
case ".jpg":
51+
return Jpg;
52+
case ".jpeg":
53+
return Jpeg;
54+
case ".memorybmp":
55+
return MemoryBmp;
56+
case ".png":
57+
return Png;
58+
case ".tif":
59+
return Tif;
60+
case ".tiff":
61+
return Tiff;
62+
case ".wmf":
63+
return Wmf;
64+
default:
65+
throw new NotSupportedException($"Unknown <{format}> extension for image file");
66+
}
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)