diff --git a/DistFiles/localization/en/Bloom.xlf b/DistFiles/localization/en/Bloom.xlf
index 2c8c919ea5b4..46c45f3f3f44 100644
--- a/DistFiles/localization/en/Bloom.xlf
+++ b/DistFiles/localization/en/Bloom.xlf
@@ -2117,6 +2117,11 @@
Bloom had trouble saving a page. Please click Details below and report this to us. Then quit Bloom, run it again, and check to see if the page you just edited is missing anything. Sorry!ID: Errors.CouldNotSavePage
+
+ You are running Bloom {0}
+ ID: Errors.CurrentRunningVersion
+ The placeholder {0} represents the current version of Bloom. For example, {0} might be replaced with "4.3"
+ This might be caused by Windows Defender "Controlled Folder Access" or some other virus protection.ID: Errors.DefenderFolderProtection
@@ -2131,6 +2136,11 @@
Your computer denied Bloom access to the book. You may need technical help in setting the operating system permissions for this file.ID: Errors.DeniedAccess
+
+ Upgrade to the latest Bloom (requires Internet connection)
+ ID: Errors.DownloadLatestVersion
+ This is a link that will take them to the Downloads (Installers) web page
+ There was a problem selecting the book. Restarting Bloom may fix the problem. If not, please click the 'Details' button and report the problem to the Bloom Developers.ID: Errors.ErrorSelecting
@@ -2141,12 +2151,28 @@
ID: Errors.ErrorUpdatingOLD TEXT (before 3.9, added spaces): There was a problem updating the book. Restarting Bloom may fix the problem. If not, please click the 'Details' button and report the problem to the Bloom Developers.
+
+ This book requires Bloom {0} or greater because it uses the following features:
+ ID: Errors.FeatureRequiresNewerVersionPlural
+ The placeholder {0} represents the required version of Bloom. For example, {0} might be replaced with "4.4"
+ A bulleted list of features (in English) will follow underneath this text
+
+
+ This book requires Bloom {0} or greater because it uses the feature \"{1}\".
+ ID: Errors.FeatureRequiresNewerVersionSingular
+ The placeholder {0} represents the required version of Bloom. For example, {0} might be replaced with "4.4"
+ The placeholder {1} represents a short description of the feature For example, {1} might be replaced with "Whole Text Audio"
+ {0} requires a newer version of Bloom. Download the latest version of Bloom from {1}ID: Errors.NeedNewerVersion{0} will get the name of the book, {1} will give a link to open the Bloom Library Web page.OLD TEXT (before 3.9, removed period): {0} requires a newer version of Bloom. Download the latest version of Bloom from {1}.
+
+ "This book needs a new version of Bloom.
+ ID: Errors.NewVersionNeededHeader
+ Bloom had a problem deleting this file: {0}ID: Errors.ProblemDeletingFile
diff --git a/src/BloomExe/BloomExe.csproj b/src/BloomExe/BloomExe.csproj
index e0db90fb0954..d5e0e05f0a07 100644
--- a/src/BloomExe/BloomExe.csproj
+++ b/src/BloomExe/BloomExe.csproj
@@ -299,6 +299,7 @@
+
diff --git a/src/BloomExe/Book/Book.cs b/src/BloomExe/Book/Book.cs
index 2e57a8821fb7..3e858f0a7271 100644
--- a/src/BloomExe/Book/Book.cs
+++ b/src/BloomExe/Book/Book.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
@@ -565,7 +565,7 @@ private HtmlDom GetBookDomWithStyleSheets(params string[] cssFileNames)
private HtmlDom GetErrorDom(string extraMessages="")
{
var builder = new StringBuilder();
- builder.Append("");
+ builder.Append("");
if(_storage != null)
{
@@ -578,14 +578,17 @@ private HtmlDom GetErrorDom(string extraMessages="")
// often GetBrokenBookRecommendation and FatalErrorDescription both come from _storage.ErrorMessagesHtml.
// Try not to say the same thing twice.
- if (!builder.ToString().Contains(FatalErrorDescription))
+ if (FatalErrorDescription != null && !builder.ToString().Contains(FatalErrorDescription))
builder.Append(FatalErrorDescription);
builder.Append("
"+ WebUtility.HtmlEncode(extraMessages)+"
");
- var message = LocalizationManager.GetString("Errors.ReportThisProblemButton", "Report this problem to Bloom Support");
- builder.AppendFormat(
- "");
+ if (_storage.ErrorAllowsReporting)
+ {
+ var message = LocalizationManager.GetString("Errors.ReportThisProblemButton", "Report this problem to Bloom Support");
+ builder.AppendFormat(
+ "");
+ }
builder.Append("");
@@ -719,6 +722,7 @@ public virtual HtmlDom GetPreviewHtmlFileForWholeBook()
{
return GetErrorDom(_storage.GetValidateErrors());
}
+
var previewDom= GetBookDomWithStyleSheets("previewMode.css", "origami.css");
AddCreationTypeAttribute(previewDom);
diff --git a/src/BloomExe/Book/BookStorage.cs b/src/BloomExe/Book/BookStorage.cs
index b01b24bc39d2..72b4e99fceda 100644
--- a/src/BloomExe/Book/BookStorage.cs
+++ b/src/BloomExe/Book/BookStorage.cs
@@ -53,6 +53,7 @@ public interface IBookStorage
HtmlDom MakeDomRelocatable(HtmlDom dom);
string SaveHtml(HtmlDom bookDom);
void SetBookName(string name);
+ string GetHtmlMessageIfFeatureIncompatibility();
string GetValidateErrors();
void CheckBook(IProgress progress,string pathToFolderOfReplacementImages = null);
void UpdateBookFileAndFolderName(CollectionSettings settings);
@@ -65,6 +66,7 @@ public interface IBookStorage
BookInfo BookInfo { get; set; }
string NormalBaseForRelativepaths { get; }
string InitialLoadErrors { get; }
+ bool ErrorAllowsReporting { get; }
void UpdateSupportFiles();
void Update(string fileName, string factoryPath = "");
string Duplicate();
@@ -101,8 +103,10 @@ public class BookStorage : IBookStorage
public event EventHandler FolderPathChanged;
+
// Returns any errors reported while loading the book (during 'expensive initialization').
public string InitialLoadErrors { get; private set; }
+ public bool ErrorAllowsReporting { get; private set; } // True if we want to display a Report to Bloom Support button
public BookInfo BookInfo
{
@@ -217,9 +221,9 @@ public bool TryGetPremadeThumbnail(string fileName, out Image image)
public HtmlDom Dom { get; private set; }
- public static string GetHtmlMessageIfVersionIsIncompatibleWithThisBloom(HtmlDom dom,string path)
+ public static string GetHtmlMessageIfVersionIsIncompatibleWithThisBloom(HtmlDom dom, string path)
{
- var versionString = dom.GetMetaValue("BloomFormatVersion", "").Trim();
+ var versionString = dom.GetMetaValue("BloomFormatVersion", "").Trim();
if (string.IsNullOrEmpty(versionString))
return "";// "This file lacks the following required element: ";
@@ -230,15 +234,70 @@ public static string GetHtmlMessageIfVersionIsIncompatibleWithThisBloom(HtmlDom
if (versionFloat > float.Parse(kBloomFormatVersion, CultureInfo.InvariantCulture))
{
var msg = LocalizationManager.GetString("Errors.NeedNewerVersion",
- "{0} requires a newer version of Bloom. Download the latest version of Bloom from {1}","{0} will get the name of the book, {1} will give a link to open the Bloom Library Web page.");
+ "{0} requires a newer version of Bloom. Download the latest version of Bloom from {1}", "{0} will get the name of the book, {1} will give a link to open the Bloom Library Web page.");
msg = string.Format(msg, path, string.Format("BloomLibrary.org", UrlLookup.LookupUrl(UrlType.LibrarySite)));
- msg += string.Format(". (Format {0} vs. {1})",versionString, kBloomFormatVersion);
+ msg += string.Format(". (Format {0} vs. {1})", versionString, kBloomFormatVersion);
return msg;
}
return null;
}
+ // Returns HTML with error message for any features that this book contains which cannot be opened by this version of Bloom.
+ // Note that although we don't allow the user to open the book (because if this version opens and saves the book, it will cause major problems for a later version of Bloom),
+ // here isn't actually any corruption or malformed data or anything particularly wrong with the book storage. So, we need to handle these kind of errors differently than validation errors.
+ public string GetHtmlMessageIfFeatureIncompatibility()
+ {
+ // Check if there are any features in this file format (which is readable), but which won't be supported (and have effects bad enough to warrant blocking opening) in this version.
+ string featureVersionRequirementJson = Dom.GetMetaValue("FeatureRequirement", "");
+ if (String.IsNullOrEmpty(featureVersionRequirementJson))
+ {
+ return "";
+ }
+ VersionRequirement[] featureVersionRequirementList = (VersionRequirement[])JsonConvert.DeserializeObject(featureVersionRequirementJson, typeof(VersionRequirement[]));
+
+ if (featureVersionRequirementList != null && featureVersionRequirementList.Length >= 1)
+ {
+ string currentBloomDesktopVersion = typeof(BookStorage).Assembly?.GetName()?.Version?.ToString() ?? "1.0";
+ var breakingFeatureRequirements = featureVersionRequirementList.Where(x => VersionComparer.IsLessThanVersion(currentBloomDesktopVersion, x.BloomDesktopMinVersion));
+
+ // Note: even though versionRequirements is guaranated non-empty by now, the ones that actually break our current version of Bloom DESKTOP could be empty.
+ if (breakingFeatureRequirements.Count() >= 1)
+ {
+ string messageNewVersionNeededHeader = LocalizationManager.GetString("Errors.NewVersionNeededHeader", "This book needs a new version of Bloom.");
+ string messageCurrentRunningVersion = String.Format(LocalizationManager.GetString("Errors.CurrentRunningVersion", "You are running Bloom {0}"), currentBloomDesktopVersion);
+ string messageDownloadLatestVersion = LocalizationManager.GetString("Errors.DownloadLatestVersion", "Upgrade to the latest Bloom (requires Internet connection)");
+
+ string messageFeatureRequiresNewerVersion;
+ if (breakingFeatureRequirements.Count() == 1)
+ {
+ var requirement = breakingFeatureRequirements.First();
+ messageFeatureRequiresNewerVersion = String.Format(LocalizationManager.GetString("Errors.FeatureRequiresNewerVersionSingular", "This book requires Bloom {0} or greater because it uses the feature \"{1}\"."), requirement.BloomDesktopMinVersion, requirement.FeaturePhrase) + " ";
+ }
+ else
+ {
+ var sortedRequirements = breakingFeatureRequirements.OrderByDescending(x => x.BloomDesktopMinVersion, new VersionComparer());
+ var highestVersionRequired = sortedRequirements.First().BloomDesktopMinVersion;
+
+ messageFeatureRequiresNewerVersion = String.Format(LocalizationManager.GetString("Errors.FeatureRequiresNewerVersionPlural", "This book requires Bloom {0} or greater because it uses the following features:"), highestVersionRequired);
+
+ string listItemsHtml = String.Join("", sortedRequirements.Select(x => $"
" +
+ $"{messageDownloadLatestVersion}"; // Enhance: is there a market-specific version of Bloom Library? If so, ideal to link to it somehow.
+
+ return message;
+ }
+ }
+
+ return "";
+ }
+
+
public bool GetLooksOk()
{
return RobustFile.Exists(PathToExistingHtml) && String.IsNullOrEmpty(ErrorMessagesHtml);
@@ -259,6 +318,20 @@ public void Save()
Dom.UpdateMetaElement("BloomFormatVersion", kBloomFormatVersion);
}
BookInfo.FormatVersion = kBloomFormatVersion;
+
+ VersionRequirement[] requiredVersions = GetRequiredVersions(Dom);
+ if (requiredVersions != null && requiredVersions.Length >= 1)
+ {
+ string json = JsonConvert.SerializeObject(requiredVersions);
+ Dom.UpdateMetaElement("FeatureRequirement", json);
+ }
+ else
+ {
+ // TODO: Unit test for this if possible
+ // Might be necessary if you duplicated a book, or modified a book such that it no longer needs this
+ Dom.RemoveMetaElement("FeatureRequirement");
+ }
+
var watch = Stopwatch.StartNew();
string tempPath = SaveHtml(Dom);
watch.Stop();
@@ -294,6 +367,26 @@ public void Save()
BookInfo.Save();
}
+ // Determines which features will have serious breaking effects if not opened in the proper version of any relevant Bloom products
+ // Note: This should include not only BloomDesktop considerations, but needs to insert enough information for things like BloomReader to be able to figure it out too
+ public static VersionRequirement[] GetRequiredVersions(HtmlDom dom)
+ {
+ var reqList = new List();
+
+ if (dom.DoesContainNarrationAudioRecordedUsingWholeTextBox())
+ {
+ reqList.Add(new VersionRequirement()
+ {
+ FeatureId = "wholeTextBoxAudio",
+ FeaturePhrase = "Whole Text Box Audio",
+ BloomDesktopMinVersion = "4.4",
+ BloomReaderMinVersion = "1.0"
+ });
+ }
+
+ return reqList.OrderByDescending(x => x.BloomDesktopMinVersion, new VersionComparer()).ToArray();
+ }
+
public const string BackupFilename = "bookhtml.bak"; // need to know this in BookCollection too.
private string GetBackupFilePath()
@@ -1014,6 +1107,7 @@ private void ExpensiveInitialization(bool forSelectedBook = false)
else
{
ErrorMessagesHtml = WebUtility.HtmlEncode(error.Message);
+ ErrorAllowsReporting = true;
Logger.WriteEvent("*** ERROR in " + PathToExistingHtml);
Logger.WriteEvent("*** ERROR: " + error.Message.Replace("{", "{{").Replace("}", "}}"));
return;
@@ -1065,11 +1159,25 @@ private void ExpensiveInitialization(bool forSelectedBook = false)
ErrorMessagesHtml = incompatibleVersionMessage;
Logger.WriteEvent("*** ERROR: " + incompatibleVersionMessage);
_errorAlreadyContainsInstructions = true;
+ ErrorAllowsReporting = true;
return;
}
else
{
- Logger.WriteEvent("BookStorage Loading Dom from {0}", PathToExistingHtml);
+ var incompatibleFeatureMessage = GetHtmlMessageIfFeatureIncompatibility();
+ if (!String.IsNullOrWhiteSpace(incompatibleFeatureMessage))
+ {
+ ErrorMessagesHtml = incompatibleFeatureMessage;
+ Logger.WriteEvent("*** ERROR: " + incompatibleFeatureMessage);
+ _errorAlreadyContainsInstructions = true;
+ ErrorAllowsReporting = false; // This doesn't any corruption or bugs in the code, so no reporting button needed.
+ return;
+ }
+
+ else
+ {
+ Logger.WriteEvent("BookStorage Loading Dom from {0}", PathToExistingHtml);
+ }
}
}
@@ -1143,6 +1251,7 @@ private void ProcessAccessDeniedError(UnauthorizedAccessException error)
message += " " + String.Format(seeAlso, "" + helpUrl + "");
ErrorMessagesHtml = message;
_errorAlreadyContainsInstructions = true;
+ ErrorAllowsReporting = true;
}
///
@@ -1187,6 +1296,7 @@ public void UpdateSupportFiles()
catch (Exception error)
{
ErrorMessagesHtml = WebUtility.HtmlEncode(error.Message);
+ ErrorAllowsReporting = true;
}
CopyBrandingFiles();
diff --git a/src/BloomExe/Book/HtmlDom.cs b/src/BloomExe/Book/HtmlDom.cs
index f55c51a51a43..07df6d61f534 100644
--- a/src/BloomExe/Book/HtmlDom.cs
+++ b/src/BloomExe/Book/HtmlDom.cs
@@ -1825,6 +1825,12 @@ public static XmlNodeList SelectAudioSentenceElementsWithRecordingMd5(XmlElement
return element.SafeSelectNodes("descendant-or-self::node()[contains(@class,'audio-sentence') and @recordingmd5]");
}
+ public bool DoesContainNarrationAudioRecordedUsingWholeTextBox()
+ {
+ var nodes = _dom.SafeSelectNodes("//*[@data-audiorecordingmode='TextBox']");
+ return nodes?.Count >= 1;
+ }
+
public static bool IsImgOrSomethingWithBackgroundImage(XmlElement element)
{
return element.SelectNodes("self::img | self::*[contains(@style,'background-image')]").Count == 1;
diff --git a/src/BloomExe/Book/VersionRequirements.cs b/src/BloomExe/Book/VersionRequirements.cs
new file mode 100644
index 000000000000..71b5ab6f8f32
--- /dev/null
+++ b/src/BloomExe/Book/VersionRequirements.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+
+namespace Bloom.Book
+{
+ public struct VersionRequirement
+ {
+ public string BloomDesktopMinVersion { get; set; }
+ public string BloomReaderMinVersion { get; set; }
+ public string FeatureId { get; set; }
+ public string FeaturePhrase { get; set; }
+ }
+
+ public class VersionComparer : IComparer
+ {
+ int IComparer.Compare(T version1, T version2)
+ {
+ string version1Str = version1.ToString();
+ string version2Str = version2.ToString();
+
+ return VersionComparer.CompareToVersion(version1Str, version2Str);
+ }
+ }
+
+ public class VersionComparer
+ {
+ public static bool IsLessThanVersion(string version1, string version2)
+ {
+ if (version1 == null || version2 == null)
+ {
+ return false;
+ }
+
+ return CompareToVersion(version1, version2) < 0;
+ }
+
+ public static int CompareToVersion(string version1, string version2)
+ {
+ // I have no idea what CompareTo is supposed to return... it should return "false" for all the boolean comparisons you ask about a null, but that doesn't make sense in CompareTo land.
+ Debug.Assert(version1 != null, "version1 must not be null");
+ Debug.Assert(version2 != null, "version2 must not be null");
+
+ if (version1 == version2)
+ {
+ // Equal
+ return 0;
+ }
+
+ int nextIndexToProcess1 = 0;
+ int nextIndexToProcess2 = 0;
+ do
+ {
+ int? nextPart1 = ExtractNextPartOfVersionNumber(version1, ref nextIndexToProcess1);
+ int? nextPart2 = ExtractNextPartOfVersionNumber(version2, ref nextIndexToProcess2);
+
+ // If they are null, I basically treat it the same as if they didn't exist.
+ if (nextPart1 == null)
+ {
+ if (nextPart2 == null)
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else if (nextPart2 == null)
+ {
+ return 1;
+ }
+ else
+ {
+ if (nextPart1 < nextPart2)
+ {
+ return -1;
+ }
+ else if (nextPart1 > nextPart2)
+ {
+ return 1;
+ }
+ else
+ {
+ // They are equal... instead of returning 0, let's look at the next part
+ // (which we do by doing nothing right now, and just contining on to the next iteration.)
+ }
+ }
+ } while (nextIndexToProcess1 < version1.Length && nextIndexToProcess2 < version2.Length);
+
+ if (nextIndexToProcess1 >= version1.Length)
+ {
+ if (nextIndexToProcess2 == version2.Length)
+ {
+ // They both ended at the same time without reporting any difference.
+ // Therefore they are completley requal.
+ return 0;
+ }
+ else
+ {
+ // Version 1 ended before Version 2.
+ // Consider this x.y vs. x.y.z which we could treat as x.y.0 vs. x.y.z and we should return "less than"
+
+ // Check: if the rest of Version 2 is just "0" or "0.0" or so on... these are basically meaningless which would mean these strings are equal despite not being identical.
+ string remainder = version2.Substring(nextIndexToProcess2);
+ string significantDigits = remainder.Replace(".", "").Replace("0", "");
+ if (significantDigits.Trim().Length == 0)
+ {
+ return 0;
+ }
+
+ return -1;
+ }
+ }
+ else
+ {
+ // Version 1 has not ended, but Version 2 must've
+ // x.y.z vs x.y (x.y.0)
+ Debug.Assert(nextIndexToProcess2 >= version2.Length, "Code expects Version2 string to have ended but it has not.");
+
+ string remainder = version1.Substring(nextIndexToProcess2);
+ string significantDigits = remainder.Replace(".", "").Replace("0", "");
+ if (significantDigits.Trim().Length == 0)
+ {
+ return 0;
+ }
+
+ return 1;
+ }
+ }
+
+ // Extracts a version number of form x.y[.z][.w] etc.
+ internal static int? ExtractNextPartOfVersionNumber(string version, ref int index)
+ {
+ int limit1 = version.IndexOf('.', index);
+ string nextPart;
+ if (limit1 < 0)
+ {
+ nextPart = version.Substring(index);
+ index = version.Length;
+ }
+ else
+ {
+ nextPart = version.Substring(index, limit1 - index);
+ index = limit1 + 1;
+ }
+
+ int nextPartInt;
+ if (int.TryParse(nextPart, out nextPartInt))
+ {
+ return nextPartInt;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/BloomTests/BloomTests.csproj b/src/BloomTests/BloomTests.csproj
index dfd8938dd8e0..a1692c28e1f3 100644
--- a/src/BloomTests/BloomTests.csproj
+++ b/src/BloomTests/BloomTests.csproj
@@ -174,6 +174,7 @@
+
diff --git a/src/BloomTests/Book/BookStorageTests.cs b/src/BloomTests/Book/BookStorageTests.cs
index 5dd7b76c8b6f..271b1d2faa8a 100644
--- a/src/BloomTests/Book/BookStorageTests.cs
+++ b/src/BloomTests/Book/BookStorageTests.cs
@@ -71,6 +71,17 @@ public void Save_BookHadEditStyleSheet_NowHasPreviewAndBase()
AssertThatXmlIn.HtmlFile(_bookPath).HasSpecifiedNumberOfMatchesForXpath("//link[contains(@href, 'basePage')]", 1);
AssertThatXmlIn.HtmlFile(_bookPath).HasSpecifiedNumberOfMatchesForXpath("//link[contains(@href, 'preview')]", 1);
}
+ [Test]
+ public void Save_BookHadNarrationAudioRecordedByWholeTextBox_AddsFeatureRequirementMetadata()
+ {
+ // Enhance: need an example in the future to test the result if two are generated. But right now this is the only feature that generates it.
+ GetInitialStorageWithCustomHtml("
");
+ AssertThatXmlIn.HtmlFile(_bookPath).HasSpecifiedNumberOfMatchesForXpath("//meta[@name='FeatureRequirement']", 1);
+
+ // Note: No need to HTML-encode the XPath. The comparison will automatically figure that out (I guess by decoding the encoding version)
+ string expectedContent = "[{\"BloomDesktopMinVersion\":\"4.4\",\"BloomReaderMinVersion\":\"1.0\",\"FeatureId\":\"wholeTextBoxAudio\",\"FeaturePhrase\":\"Whole Text Box Audio\"}]";
+ AssertThatXmlIn.HtmlFile(_bookPath).HasSpecifiedNumberOfMatchesForXpath($"//meta[@content='{expectedContent}']", 1);
+ }
[Test]
public void CleanupUnusedVideoFiles_BookHadUnusedVideo_VideosRemoved()
@@ -378,7 +389,27 @@ public void ValidateBook_ReportsInvalidHtml()
var result = storage.ValidateBook(storage.PathToExistingHtml);
Assert.IsTrue(result.StartsWith("Bloom-page element not found at root level: someOtherId"), "Bad Html should fail ValidateBook().");
}
-
+
+ [Test]
+ public void ValidateBook_ReportsNewerVersionRequired()
+ {
+ var storage = GetInitialStorageWithCustomHtml(
+@"
+
+
+
+
+
+
+
+
+"
+ );
+
+ storage.GetHtmlMessageIfFeatureIncompatibility();
+ Assert.IsTrue(storage.ErrorMessagesHtml.Contains("or greater because it uses the feature"), "ErrorMessagesHtml");
+ Assert.IsFalse(storage.ErrorAllowsReporting, "ErrorAllowsReporting");
+ }
[Test]
public void Save_BookHasMissingImages_NoCrash()
{
diff --git a/src/BloomTests/Book/VersionRequirementTests.cs b/src/BloomTests/Book/VersionRequirementTests.cs
new file mode 100644
index 000000000000..c4c161d22727
--- /dev/null
+++ b/src/BloomTests/Book/VersionRequirementTests.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Bloom.Book;
+using NUnit.Framework;
+
+
+namespace BloomTests.Book
+{
+ class VersionRequirementTests
+ {
+ [TestCase("1.3", "2.0", -1)]
+ [TestCase("4.3", "4.4", -1)]
+ [TestCase("1.9", "1.10", -1)] // Exercise a case that will cause a naive lexicographical test to fail
+ [TestCase("4.4.0.0", "4.4", 0)] // Note: The actual assembly version in the assembly is x.y.z.w, so it's important to get this right
+ [TestCase("1.0", "1.0.1", -1)]
+ [TestCase("1.2.3", "1.2.2.9", 1)]
+ public void CompareToVersion(string version1, string version2, int expectedResult)
+ {
+ int result = VersionComparer.CompareToVersion(version1, version2);
+ Assert.AreEqual(expectedResult, result);
+
+ int commutativeResult = VersionComparer.CompareToVersion(version2, version1);
+ Assert.AreEqual(-expectedResult, commutativeResult, "Commutative test");
+ }
+ }
+}