using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
namespace UnityEditor.PackageManager.ValidationSuite
{
///
/// Class that manages the validation exceptions for this package.
///
public class ValidationExceptionManager
{
private const string ValidationExceptionsFileName = "ValidationExceptions.json";
private const string TestExceptionPrefix = "__**Test-";
private Dictionary errorExceptionDictionary;
private Dictionary warningExceptionDictionary;
///
/// Will return true if the package contains 1 or more validation error exceptions
///
public bool HasErrorExceptions
{
get { return errorExceptionDictionary.Any(); }
}
///
/// Will return true if the package contains 1 or more validation warning exceptions
///
public bool HasWarningExceptions
{
get { return warningExceptionDictionary.Any(); }
}
///
/// Will return true if the package contains 1 or more validation exceptions
///
public bool HasExceptions => HasErrorExceptions || HasWarningExceptions;
internal IEnumerable ErrorExceptionsList
{
get { return errorExceptionDictionary.Values; }
}
internal IEnumerable WarningExceptionsList
{
get { return warningExceptionDictionary.Values; }
}
///
/// Constructor for the Validation Exception Manager
///
/// Path that contains the exception file.
public ValidationExceptionManager(string packagePath)
{
errorExceptionDictionary = new Dictionary();
warningExceptionDictionary = new Dictionary();
var filePath = Path.Combine(packagePath, ValidationExceptionsFileName);
if (LongPathUtils.File.Exists(filePath))
{
var exceptions = Utilities.GetDataFromJson(filePath);
// If there is an exception error specified, let's use it.
// If there isn't one specified, this is a test level exception, all errors for that test should be flagged as exceptions.
if (exceptions.ErrorExceptions != null)
errorExceptionDictionary = exceptions.ErrorExceptions.ToDictionary(ex => string.IsNullOrWhiteSpace(ex.ExceptionMessage) ? (TestExceptionPrefix + ex.ValidationTest) : ex.ExceptionMessage);
// If there is an exception warning specified, let's use it.
// If there isn't one specified, this is a test level exception, all warning for that test should be flagged as exceptions.
if (exceptions.WarningExceptions != null)
warningExceptionDictionary = exceptions.WarningExceptions.ToDictionary(ex => string.IsNullOrWhiteSpace(ex.ExceptionMessage) ? (TestExceptionPrefix + ex.ValidationTest) : ex.ExceptionMessage);
// Handle backwards compatibility for old format (renamed fields)
// Sadly JsonUtility.FromJson doesn't support the FormerlySerializedAs attribute (case 1119033)
MergeWithOldFormat(filePath);
}
}
void MergeWithOldFormat(string filePath)
{
var oldExceptions = Utilities.GetDataFromJson(filePath);
if (oldExceptions.Exceptions != null)
{
foreach (var oldException in oldExceptions.Exceptions)
{
var errorException = new ValidationException
{
ValidationTest = oldException.ValidationTest,
ExceptionMessage = oldException.ExceptionError,
PackageVersion = oldException.PackageVersion,
};
var key = string.IsNullOrWhiteSpace(errorException.ExceptionMessage) ? (TestExceptionPrefix + errorException.ValidationTest) : errorException.ExceptionMessage;
errorExceptionDictionary.Add(key, errorException);
}
}
}
// Helper classes for ValidationExceptions.json format backwards compatibility
#pragma warning disable 0649
[Serializable]
class OldValidationException
{
public string ValidationTest;
public string ExceptionError;
public string PackageVersion;
}
[Serializable]
class OldValidationExceptions
{
public OldValidationException[] Exceptions;
}
#pragma warning restore 0649
///
/// Tests whether the requested error is part of the validation exception list.
///
/// Validation test display name
/// Error string, verbatim
/// Version of the package this exception is for.
/// True if the error is part of the validation exception list.
public bool IsErrorException(string validationTest, string validationMessage, string packageVersion)
{
ValidationException validationException;
if (errorExceptionDictionary.TryGetValue(validationMessage, out validationException))
{
if (validationException.ValidationTest == validationTest &&
validationException.PackageVersion == packageVersion)
{
return true;
}
}
return false;
}
///
/// Tests whether the requested warning is part of the validation exception list.
///
/// Validation test display name
/// Warning string, verbatim
/// Version of the package this exception is for.
/// True if the warning is part of the validation exception list.
public bool IsWarningException(string validationTest, string validationMessage, string packageVersion)
{
ValidationException validationException;
if (warningExceptionDictionary.TryGetValue(validationMessage, out validationException))
{
if (validationException.ValidationTest == validationTest &&
validationException.PackageVersion == packageVersion)
{
return true;
}
}
return false;
}
///
/// Tests whether a test class has been exceptioned completely with respect to errors.
///
/// Validation test display name
/// Version of the package this exception is for.
/// True if the error is part of the validation exception list.
public bool IsErrorException(string validationTest, string packageVersion)
{
ValidationException validationException;
if (errorExceptionDictionary.TryGetValue(TestExceptionPrefix + validationTest, out validationException))
{
if (validationException.PackageVersion == packageVersion)
{
return true;
}
}
return false;
}
///
/// Tests whether a test class has been exceptioned completely with respect to warnings.
///
/// Validation test display name
/// Version of the package this exception is for.
/// True if the warning is part of the validation exception list.
public bool IsWarningException(string validationTest, string packageVersion)
{
ValidationException validationException;
if (warningExceptionDictionary.TryGetValue(TestExceptionPrefix + validationTest, out validationException))
{
if (validationException.PackageVersion == packageVersion)
{
return true;
}
}
return false;
}
///
/// Checks the validity of the validation exception list.
///
/// Package version used for comparison.
/// Returns a list of issues found with the exception list. That list will be empty when no issues are found.
public IEnumerable CheckValidationExceptions(string packageVersion)
{
var formatString = "The following {0} was tagged as an exception to validation, but for a different version of the package. " +
$"Please consider getting this exception fixed and removed from \"{ValidationExceptionsFileName}\" before updating its package version field.\r\n" +
" \"{1}\" - \"{2}\"";
List issuesList = new List();
foreach (var validationException in ErrorExceptionsList)
{
// Validate that all error exceptions that were used had the right package version.
if (validationException.PackageVersion != packageVersion)
{
issuesList.Add(string.Format(formatString, "error", validationException.ValidationTest, validationException.ExceptionMessage));
}
}
foreach (var validationException in WarningExceptionsList)
{
// Validate that all warning exceptions that were used had the right package version.
if (validationException.PackageVersion != packageVersion)
{
issuesList.Add(string.Format(formatString, "warning", validationException.ValidationTest, validationException.ExceptionMessage));
}
}
return issuesList;
}
}
}