Skip to content

Commit 502051f

Browse files
committed
Merge pull request #3450 from beersandrew/validation-usdUtils-MissingReferenceValidator
usdValidation: usdUtils: MissingReferenceValidator (Internal change: 2352292)
2 parents 85f3f21 + d71c0a1 commit 502051f

File tree

7 files changed

+181
-18
lines changed

7 files changed

+181
-18
lines changed

pxr/usdValidation/bin/usdchecker/testenv/testUsdChecker/baseline/arkitRules_validationFramework.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
Doc: Only valid core layer extensions (.usd, .usda, .usdc, .usdz), valid core texture extensions (.exr, .jpg, .jpeg, .png) and embedded audio files (.M4A, .MP3, .WAV) are allowed in a package.
6363
Keywords: UsdUtilsValidators, UsdzValidators
6464
isSuite: False
65+
[usdUtilsValidators:MissingReferenceValidator]:
66+
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.
67+
Keywords: UsdUtilsValidators
68+
isSuite: False
6569
[usdUtilsValidators:PackageEncapsulationValidator]:
6670
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.
6771
Keywords: UsdUtilsValidators, UsdzValidators

pxr/usdValidation/bin/usdchecker/testenv/testUsdChecker/baseline/baseRules_validationFramework.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858
Keywords: UsdSkelValidators
5959
SchemaTypes: UsdSkelBindingAPI
6060
isSuite: False
61+
[usdUtilsValidators:MissingReferenceValidator]:
62+
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.
63+
Keywords: UsdUtilsValidators
64+
isSuite: False
6165
[usdValidation:CompositionErrorTest]:
6266
Doc: Validator aims at providing all composition errors, which were generated while composing the stage.
6367
Keywords: UsdCoreValidators

pxr/usdValidation/usdUtilsValidators/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pxr_build_test(testUsdUtilsValidators
3030
tf
3131
sdf
3232
usd
33+
usdGeom
34+
usdShade
3335
usdValidation
3436
usdUtils
3537
usdUtilsValidators

pxr/usdValidation/usdUtilsValidators/plugInfo.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"UsdzValidators"
1010
]
1111
},
12+
"MissingReferenceValidator": {
13+
"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."
14+
},
1215
"PackageEncapsulationValidator": {
1316
"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.",
1417
"keywords": [

pxr/usdValidation/usdUtilsValidators/testenv/testUsdUtilsValidators.cpp

Lines changed: 119 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
#include "pxr/base/arch/systemInfo.h"
99
#include "pxr/base/tf/pathUtils.h"
10+
#include "pxr/usd/usd/editContext.h"
11+
#include "pxr/usd/usd/variantSets.h"
12+
#include "pxr/usd/usdShade/shader.h"
13+
#include "pxr/usd/usdGeom/tokens.h"
1014
#include "pxr/usdValidation/usdUtilsValidators/validatorTokens.h"
1115
#include "pxr/usdValidation/usdValidation/error.h"
1216
#include "pxr/usdValidation/usdValidation/registry.h"
@@ -30,18 +34,19 @@ TestUsdUsdzValidators()
3034
UsdValidationValidatorMetadataVector metadata
3135
= registry.GetValidatorMetadataForPlugin(
3236
_tokens->usdUtilsValidatorsPlugin);
33-
TF_AXIOM(metadata.size() == 2);
37+
TF_AXIOM(metadata.size() == 3);
3438
// Since other validators can be registered with a UsdUtilsValidators
3539
// keyword, our validators registered in usd are a subset of the entire
3640
// set.
3741
std::set<TfToken> validatorMetadataNameSet;
38-
for (const UsdValidationValidatorMetadata &metadata : metadata) {
39-
validatorMetadataNameSet.insert(metadata.name);
42+
for (const UsdValidationValidatorMetadata &m : metadata) {
43+
validatorMetadataNameSet.insert(m.name);
4044
}
4145

4246
const std::set<TfToken> expectedValidatorNames
4347
= { UsdUtilsValidatorNameTokens->packageEncapsulationValidator,
44-
UsdUtilsValidatorNameTokens->fileExtensionValidator };
48+
UsdUtilsValidatorNameTokens->fileExtensionValidator,
49+
UsdUtilsValidatorNameTokens->missingReferenceValidator };
4550

4651
TF_AXIOM(validatorMetadataNameSet == expectedValidatorNames);
4752
}
@@ -90,7 +95,7 @@ TestPackageEncapsulationValidator()
9095
const std::string realUsdzPath = rootLayer->GetRealPath();
9196
const std::string errorLayer
9297
= TfStringCatPaths(TfGetPathName(TfAbsPath(rootLayerIdentifier)),
93-
"excludedDirectory/layer.usda");
98+
"excludedDirectory/layer.usda");
9499

95100
std::filesystem::path parentDir
96101
= std::filesystem::path(realUsdzPath).parent_path();
@@ -99,22 +104,22 @@ TestPackageEncapsulationValidator()
99104

100105
std::array<std::string, 2> expectedErrorMessages
101106
= { TfStringPrintf(("Found referenced layer '%s' that does not "
102-
"belong to the package '%s'."),
103-
errorLayer.c_str(), realUsdzPath.c_str()),
107+
"belong to the package '%s'."),
108+
errorLayer.c_str(), realUsdzPath.c_str()),
104109
TfStringPrintf(("Found asset reference '%s' that does not belong"
105-
" to the package '%s'."),
106-
errorAsset.c_str(), realUsdzPath.c_str()) };
110+
" to the package '%s'."),
111+
errorAsset.c_str(), realUsdzPath.c_str()) };
107112

108113
std::array<TfToken, 2> expectedErrorIdentifiers = {
109114
TfToken(
110-
"usdUtilsValidators:PackageEncapsulationValidator.LayerNotInPackage"),
115+
"usdUtilsValidators:PackageEncapsulationValidator.LayerNotInPackage"),
111116
TfToken(
112-
"usdUtilsValidators:PackageEncapsulationValidator.AssetNotInPackage")
117+
"usdUtilsValidators:PackageEncapsulationValidator.AssetNotInPackage")
113118
};
114119

115120
for (size_t i = 0; i < errors.size(); ++i) {
116121
ValidateError(errors[i], expectedErrorMessages[i],
117-
expectedErrorIdentifiers[i], UsdValidationErrorType::Warn);
122+
expectedErrorIdentifiers[i], UsdValidationErrorType::Warn);
118123
}
119124
}
120125

@@ -160,12 +165,114 @@ TestFileExtensionValidator()
160165
TF_AXIOM(errors.empty());
161166
}
162167

168+
static void
169+
TestMissingReferenceValidator()
170+
{
171+
UsdValidationRegistry& registry = UsdValidationRegistry::GetInstance();
172+
173+
// Verify the validator exists
174+
const UsdValidationValidator *validator = registry.GetOrLoadValidatorByName(
175+
UsdUtilsValidatorNameTokens->missingReferenceValidator);
176+
177+
TF_AXIOM(validator);
178+
179+
const UsdStageRefPtr& stage = UsdStage::CreateInMemory();
180+
181+
const UsdPrim xform = stage->DefinePrim(SdfPath("/Prim"),
182+
UsdGeomTokens->Xform);
183+
const SdfReference badLayerReference("doesNotExist.usd");
184+
xform.GetReferences().AddReference(badLayerReference);
185+
186+
// Validate an error occurs for a missing layer reference
187+
{
188+
const UsdValidationErrorVector errors = validator->Validate(stage);
189+
TF_AXIOM(errors.size() == 1);
190+
const std::string expectedErrorMessage = "Found unresolvable external "
191+
"dependency 'doesNotExist.usd'.";
192+
const TfToken expectedIdentifier =
193+
TfToken(
194+
"usdUtilsValidators:MissingReferenceValidator.UnresolvableDependency");
195+
196+
ValidateError(errors[0], expectedErrorMessage,
197+
expectedIdentifier);
198+
}
199+
200+
// Remove the bad layer reference and add a shader prim for the next test
201+
xform.GetReferences().RemoveReference(badLayerReference);
202+
UsdShadeShader shader = UsdShadeShader::Define(stage,
203+
SdfPath("/Prim/Shader"));
204+
const TfToken notFoundAsset("notFoundAsset");
205+
UsdShadeInput notFoundAssetInput = shader.CreateInput(
206+
notFoundAsset, SdfValueTypeNames->Asset);
207+
notFoundAssetInput.Set(SdfAssetPath("doesNotExist.jpg"));
208+
209+
// Validate an error occurs for a missing asset reference
210+
{
211+
const UsdValidationErrorVector errors = validator->Validate(stage);
212+
TF_AXIOM(errors.size() == 1);
213+
const std::string expectedErrorMessage = "Found unresolvable external "
214+
"dependency 'doesNotExist.jpg'.";
215+
const TfToken expectedIdentifier =
216+
TfToken(
217+
"usdUtilsValidators:MissingReferenceValidator.UnresolvableDependency");
218+
219+
ValidateError(errors[0], expectedErrorMessage,
220+
expectedIdentifier);
221+
}
222+
223+
// Remove shader prim and add a variant set for the next test
224+
stage->RemovePrim(shader.GetPath());
225+
UsdVariantSets variantSets = xform.GetVariantSets();
226+
UsdVariantSet testVariantSet = variantSets.AddVariantSet("testVariantSet");
227+
228+
testVariantSet.AddVariant("invalid");
229+
testVariantSet.AddVariant("valid");
230+
231+
testVariantSet.SetVariantSelection("invalid");
232+
233+
{
234+
UsdEditContext context(testVariantSet.GetVariantEditContext());
235+
UsdShadeShader s = UsdShadeShader::Define(
236+
stage, xform.GetPath().AppendChild(TfToken("Shader")));
237+
UsdShadeInput invalidAssetInput = s.CreateInput(
238+
TfToken("invalidAsset"), SdfValueTypeNames->Asset);
239+
invalidAssetInput.Set(SdfAssetPath("doesNotExistOnVariant.jpg"));
240+
}
241+
testVariantSet.SetVariantSelection("valid");
242+
243+
// Validate an error occurs on a nonexistent asset on an inactive variant
244+
{
245+
const UsdValidationErrorVector errors = validator->Validate(stage);
246+
TF_AXIOM(errors.size() == 1);
247+
const std::string expectedErrorMessage =
248+
"Found unresolvable external dependency "
249+
"'doesNotExistOnVariant.jpg'.";
250+
const TfToken expectedIdentifier =
251+
TfToken(
252+
"usdUtilsValidators:MissingReferenceValidator.UnresolvableDependency");
253+
254+
ValidateError(errors[0], expectedErrorMessage,
255+
expectedIdentifier);
256+
}
257+
258+
// Remove the variant set and add a valid reference
259+
SdfPrimSpecHandle primSpec =
260+
stage->GetRootLayer()->GetPrimAtPath(xform.GetPath());
261+
primSpec->RemoveVariantSet("testVariantSet");
262+
xform.GetPrim().GetReferences().AddReference("pass.usdz");
263+
264+
const UsdValidationErrorVector errors = validator->Validate(stage);
265+
266+
TF_AXIOM(errors.empty());
267+
}
268+
163269
int
164270
main()
165271
{
166272
TestUsdUsdzValidators();
167273
TestPackageEncapsulationValidator();
168274
TestFileExtensionValidator();
275+
TestMissingReferenceValidator();
169276

170277
return EXIT_SUCCESS;
171278
}

pxr/usdValidation/usdUtilsValidators/validatorTokens.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ PXR_NAMESPACE_OPEN_SCOPE
2020
((packageEncapsulationValidator, \
2121
"usdUtilsValidators:PackageEncapsulationValidator")) \
2222
((fileExtensionValidator, \
23-
"usdUtilsValidators:FileExtensionValidator"))
23+
"usdUtilsValidators:FileExtensionValidator")) \
24+
((missingReferenceValidator, \
25+
"usdUtilsValidators:MissingReferenceValidator")) \
2426

2527
#define USD_UTILS_VALIDATOR_KEYWORD_TOKENS \
2628
(UsdUtilsValidators) \
@@ -31,7 +33,8 @@ PXR_NAMESPACE_OPEN_SCOPE
3133
((assetNotInPackage, "AssetNotInPackage")) \
3234
((invalidLayerInPackage, "InvalidLayerInPackage")) \
3335
((unsupportedFileExtensionInPackage, \
34-
"UnsupportedFileExtensionInPackage"))
36+
"UnsupportedFileExtensionInPackage")) \
37+
((unresolvableDependency, "UnresolvableDependency"))
3538

3639
///\def
3740
/// Tokens representing validator names. Note that for plugin provided

pxr/usdValidation/usdUtilsValidators/validators.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include "pxr/usd/sdf/fileFormat.h"
1919

20+
#include <string>
21+
2022
PXR_NAMESPACE_OPEN_SCOPE
2123

2224
static UsdValidationErrorVector
@@ -37,7 +39,7 @@ _PackageEncapsulationValidator(
3739
}
3840

3941
SdfLayerRefPtrVector layers;
40-
std::vector<std::basic_string<char>> assets, unresolvedPaths;
42+
std::vector<std::string> assets, unresolvedPaths;
4143
const SdfAssetPath &path = SdfAssetPath(rootLayer->GetIdentifier());
4244

4345
UsdUtilsComputeAllDependencies(path, &layers, &assets, &unresolvedPaths,
@@ -62,15 +64,15 @@ _PackageEncapsulationValidator(
6264
"a valid layer to be bundled in a package.");
6365
continue;
6466
}
65-
const std::string &realPath = referencedLayer->GetRealPath();
67+
const std::string &refRealPath = referencedLayer->GetRealPath();
6668

6769
// We don't want to validate in-memory or session layers
6870
// since these layers will not have a real path, we skip here
69-
if (realPath.empty()) {
71+
if (refRealPath.empty()) {
7072
continue;
7173
}
7274

73-
if (!TfStringStartsWith(realPath, packagePath)) {
75+
if (!TfStringStartsWith(refRealPath, packagePath)) {
7476
errors.emplace_back(
7577
UsdUtilsValidationErrorNameTokens->layerNotInPackage,
7678
UsdValidationErrorType::Warn,
@@ -148,6 +150,39 @@ _FileExtensionValidator(const UsdStagePtr& usdStage,
148150
return errors;
149151
}
150152

153+
static
154+
UsdValidationErrorVector
155+
_MissingReferenceValidator(const UsdStagePtr& usdStage,
156+
const UsdValidationTimeRange &/*timeRange*/)
157+
{
158+
const SdfLayerRefPtr& rootLayer = usdStage->GetRootLayer();
159+
160+
SdfLayerRefPtrVector layers;
161+
std::vector<std::string> unresolvedPaths;
162+
const SdfAssetPath& path = SdfAssetPath(rootLayer->GetIdentifier());
163+
164+
UsdUtilsComputeAllDependencies(path, &layers, nullptr /*assets*/,
165+
&unresolvedPaths, nullptr);
166+
167+
UsdValidationErrorVector errors;
168+
for(const std::string &unresolvedPath : unresolvedPaths)
169+
{
170+
errors.emplace_back(
171+
UsdUtilsValidationErrorNameTokens->unresolvableDependency,
172+
UsdValidationErrorType::Error,
173+
UsdValidationErrorSites {
174+
UsdValidationErrorSite(
175+
rootLayer, SdfPath(unresolvedPath))
176+
},
177+
TfStringPrintf(
178+
("Found unresolvable external dependency "
179+
"'%s'."), unresolvedPath.c_str())
180+
);
181+
}
182+
183+
return errors;
184+
}
185+
151186
TF_REGISTRY_FUNCTION(UsdValidationRegistry)
152187
{
153188
UsdValidationRegistry &registry = UsdValidationRegistry::GetInstance();
@@ -159,6 +194,11 @@ TF_REGISTRY_FUNCTION(UsdValidationRegistry)
159194
registry.RegisterPluginValidator(
160195
UsdUtilsValidatorNameTokens->fileExtensionValidator,
161196
_FileExtensionValidator);
197+
198+
registry.RegisterPluginValidator(
199+
UsdUtilsValidatorNameTokens->missingReferenceValidator,
200+
_MissingReferenceValidator);
201+
162202
}
163203

164204
PXR_NAMESPACE_CLOSE_SCOPE

0 commit comments

Comments
 (0)