Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usdUtils: Added MissingReferenceValidator #3403

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pxr/usd/usdUtils/plugInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
{
"Info": {
"Validators": {
"MissingReferenceValidator": {
"doc": "The composed USD stage should not contain any unresolvable asset dependencies (in every possible variation of the asset), when using the default asset resolver."
},
"PackageEncapsulationValidator": {
"doc": "If the root layer is a package, then its recommended for the composed stage to not contain references to files outside the package. The package should be self-contained, warn if not.",
"keywords": [
Expand Down
58 changes: 51 additions & 7 deletions pxr/usd/usdUtils/testenv/testUsdUtilsValidators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

#include "pxr/usd/usd/validator.h"
#include "pxr/usd/usd/validationError.h"
#include "pxr/usd/usdGeom/xform.h"
#include "pxr/usd/usdUtils/validatorTokens.h"
#include "pxr/usd/usd/validationRegistry.h"
#include "pxr/base/arch/systemInfo.h"
#include "pxr/base/tf/pathUtils.h"

#include <filesystem>
Expand All @@ -30,7 +30,7 @@ TestUsdUsdzValidators()
UsdValidationRegistry& registry = UsdValidationRegistry::GetInstance();
UsdValidatorMetadataVector metadata =
registry.GetValidatorMetadataForPlugin(_tokens->usdUtilsPlugin);
TF_AXIOM(metadata.size() == 1);
TF_AXIOM(metadata.size() == 2);
// Since other validators can be registered with a UsdUtilsValidators
// keyword, our validators registered in usd are a subset of the entire
// set.
Expand All @@ -40,7 +40,8 @@ TestUsdUsdzValidators()
}

const std::set<TfToken> expectedValidatorNames =
{UsdUtilsValidatorNameTokens->packageEncapsulationValidator};
{UsdUtilsValidatorNameTokens->packageEncapsulationValidator,
UsdUtilsValidatorNameTokens->missingReferenceValidator};

TF_AXIOM(validatorMetadataNameSet == expectedValidatorNames);
}
Expand Down Expand Up @@ -73,12 +74,12 @@ TestPackageEncapsulationValidator()
const std::string& rootLayerIdentifier = rootLayer->GetIdentifier();
const std::string realUsdzPath = rootLayer->GetRealPath();
const std::string errorLayer = TfStringCatPaths(
TfGetPathName(TfAbsPath(rootLayerIdentifier)),
TfGetPathName(TfAbsPath(rootLayerIdentifier)),
"excludedDirectory/layer.usda");

std::filesystem::path parentDir =
std::filesystem::path parentDir =
std::filesystem::path(realUsdzPath).parent_path();
const std::string errorAsset =
const std::string errorAsset =
(parentDir / "excludedDirectory" / "image.jpg").string();

std::array<std::string, 2> expectedErrorMessages = {
Expand Down Expand Up @@ -107,18 +108,61 @@ TestPackageEncapsulationValidator()
// Load the pre-created usdz stage with relative paths to both a reference
// and an asset that are included in the package.
const UsdStageRefPtr& passStage = UsdStage::Open("pass.usdz");

errors = validator->Validate(passStage);

// Verify the errors are gone
TF_AXIOM(errors.empty());
}

static
void
TestMissingReferenceValidator()
{
UsdValidationRegistry& registry = UsdValidationRegistry::GetInstance();

// Verify the validator exists
const UsdValidator *validator = registry.GetOrLoadValidatorByName(
UsdUtilsValidatorNameTokens->missingReferenceValidator);

TF_AXIOM(validator);

// Create stage with a reference that does not exist
const UsdStageRefPtr& stage = UsdStage::CreateInMemory();

const UsdGeomXform xform = UsdGeomXform::Define(stage, SdfPath("/Xform"));
const SdfReference badReference("doesNotExist.usd");
xform.GetPrim().GetReferences().AddReference(badReference);

UsdValidationErrorVector errors = validator->Validate(stage);

// Verify both the layer & asset errors are present
const TfToken expectedIdentifier =
TfToken("usdUtils:MissingReferenceValidator.UnresolvableDependency");
TF_AXIOM(errors.size() == 1);
TF_AXIOM(errors[0].GetIdentifier() == expectedIdentifier);
TF_AXIOM(errors[0].GetType() == UsdValidationErrorType::Error);
TF_AXIOM(errors[0].GetSites().size() == 1);
TF_AXIOM(!errors[0].GetSites()[0].GetLayer().IsInvalid());
const std::string expectedErrorMessage = "Found unresolvable external "
"dependency 'doesNotExist.usd'.";
TF_AXIOM(errors[0].GetMessage() == expectedErrorMessage);

// Remove the nonexistent reference, add an existing reference
xform.GetPrim().GetReferences().RemoveReference(badReference);
xform.GetPrim().GetReferences().AddReference("pass.usdz");
errors = validator->Validate(stage);

// Verify the errors are gone
TF_AXIOM(errors.empty());
}

int
main()
{
TestUsdUsdzValidators();
TestPackageEncapsulationValidator();
TestMissingReferenceValidator();

return EXIT_SUCCESS;
}
10 changes: 6 additions & 4 deletions pxr/usd/usdUtils/validatorTokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@
PXR_NAMESPACE_OPEN_SCOPE

#define USD_UTILS_VALIDATOR_NAME_TOKENS \
((missingReferenceValidator, "usdUtils:MissingReferenceValidator")) \
((packageEncapsulationValidator, "usdUtils:PackageEncapsulationValidator"))

#define USD_UTILS_VALIDATOR_KEYWORD_TOKENS \
(UsdUtilsValidators) \
(UsdzValidators)

#define USD_UTILS_VALIDATION_ERROR_NAME_TOKENS \
((layerNotInPackage, "LayerNotInPackage")) \
((assetNotInPackage, "AssetNotInPackage")) \
((invalidLayerInPackage, "InvalidLayerInPackage"))
#define USD_UTILS_VALIDATION_ERROR_NAME_TOKENS \
((layerNotInPackage, "LayerNotInPackage")) \
((assetNotInPackage, "AssetNotInPackage")) \
((invalidLayerInPackage, "InvalidLayerInPackage")) \
((unresolvableDependency, "UnresolvableDependency")) \

///\def
/// Tokens representing validator names. Note that for plugin provided
Expand Down
35 changes: 35 additions & 0 deletions pxr/usd/usdUtils/validators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,44 @@ _PackageEncapsulationValidator(const UsdStagePtr& usdStage) {
return errors;
}

static
UsdValidationErrorVector
_MissingReferenceValidator(const UsdStagePtr& usdStage) {
const SdfLayerRefPtr& rootLayer = usdStage->GetRootLayer();

SdfLayerRefPtrVector layers;
std::vector<std::basic_string<char>> assets, unresolvedPaths;
const SdfAssetPath& path = SdfAssetPath(rootLayer->GetIdentifier());

UsdUtilsComputeAllDependencies(path, &layers, &assets, &unresolvedPaths,
nullptr);

UsdValidationErrorVector errors;
for(const std::basic_string<char>& unresolvedPath : unresolvedPaths)
{
errors.emplace_back(
UsdUtilsValidationErrorNameTokens->unresolvableDependency,
UsdValidationErrorType::Error,
UsdValidationErrorSites {
UsdValidationErrorSite(
rootLayer, SdfPath(unresolvedPath))
},
TfStringPrintf(
("Found unresolvable external dependency "
"'%s'."), unresolvedPath.c_str())
);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tallytalwar One note here, it looks like complianceChecker.py is doing another check here which gets some diagnostic information, but it seems like this process of collecting diagnostic information should be started before opening the stage (here). I tried doing this inside the validator with stage.Reload() but was unable to populate the diagnostics this way.

In my specific test, the errors were similar so I wanted your opinion on if it's possible to do something similar here, or if it's necessary to move the diagnostic part over in this case.


return errors;
}

TF_REGISTRY_FUNCTION(UsdValidationRegistry)
{
UsdValidationRegistry& registry = UsdValidationRegistry::GetInstance();

registry.RegisterPluginValidator(
UsdUtilsValidatorNameTokens->missingReferenceValidator, _MissingReferenceValidator);

registry.RegisterPluginValidator(
UsdUtilsValidatorNameTokens->packageEncapsulationValidator, _PackageEncapsulationValidator);
}
Expand Down