Add CRC validator (#5857)

* Adds crc validation checks

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

* Fixes invalid CRCs

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

* Changes test to smoke suite

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

* excludes some test data from the validator

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

* uses pathlib instead of os.path

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

* fixes wrong path to test scripts

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

* Escape not needed

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-11-30 15:07:57 -08:00
committed by GitHub
parent 858885dca9
commit fb6e6e339f
21 changed files with 130 additions and 35 deletions
+2 -2
View File
@@ -16,7 +16,7 @@
//
// When AZ_CRC("My string") is used by default it will map to AZ::Crc32("My string").
// We do have a pro-processor program which will precompute the crc for you and
// transform that macro to AZ_CRC("My string",0xabcdef00) this will expand to just 0xabcdef00.
// transform that macro to AZ_CRC("My string", 0x18fbd270) this will expand to just 0x18fbd270.
// This will remove completely the "My string" from your executable, it will add it to a database and so on.
// WHen you want to update the string, just change the string.
// If you don't run the precompile step the code should still run fine, except it will be slower,
@@ -24,7 +24,7 @@
// a constant expression.
// For example
// switch(id) {
// case AZ_CRC("My string",0xabcdef00): {} break; // this will compile fine
// case AZ_CRC("My string",0x18fbd270): {} break; // this will compile fine
// case AZ_CRC("My string"): {} break; // this will cause "error C2051: case expression not constant"
// }
// So it's you choice what you do, depending on your needs.
@@ -23,11 +23,11 @@ namespace AzToolsFramework
/// @name Reverse URLs.
/// Used to identify common actions and override them when necessary.
//@{
static const AZ::Crc32 s_backAction = AZ_CRC("com.o3de.action.common.back", 0xd772a2af);
static const AZ::Crc32 s_deleteAction = AZ_CRC("com.o3de.action.common.delete", 0x5731f6cb);
static const AZ::Crc32 s_duplicateAction = AZ_CRC("com.o3de.action.common.duplicate", 0x08ccf461);
static const AZ::Crc32 s_nextComponentMode = AZ_CRC("com.o3de.action.common.nextComponentMode", 0xcc26094f);
static const AZ::Crc32 s_previousComponentMode = AZ_CRC("com.o3de.action.common.previousComponentMode", 0x0d18ff39);
static const AZ::Crc32 s_backAction = AZ_CRC("com.o3de.action.common.back", 0x80c3030f);
static const AZ::Crc32 s_deleteAction = AZ_CRC("com.o3de.action.common.delete", 0x58e78eed);
static const AZ::Crc32 s_duplicateAction = AZ_CRC("com.o3de.action.common.duplicate", 0xbc5a4a23);
static const AZ::Crc32 s_nextComponentMode = AZ_CRC("com.o3de.action.common.nextComponentMode", 0xf9aca3a8);
static const AZ::Crc32 s_previousComponentMode = AZ_CRC("com.o3de.action.common.previousComponentMode", 0x0580eaec);
//@}
/// Specific Action properties to be sent to a type implementing
@@ -67,12 +67,12 @@ namespace AZ
void AtomBridgeSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99));
provided.push_back(AZ_CRC("AtomBridgeService", 0x92d990b5));
}
void AtomBridgeSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99));
incompatible.push_back(AZ_CRC("AtomBridgeService", 0x92d990b5));
}
void AtomBridgeSystemComponent::GetRequiredServices(ComponentDescriptor::DependencyArrayType& required)
@@ -109,12 +109,12 @@ namespace AZ
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0xdd5ab934));
services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0x66d04369));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0xdd5ab934));
services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0x66d04369));
}
static void Reflect(AZ::ReflectContext* context);
@@ -73,7 +73,7 @@ namespace AZ::Render
void AtomViewportDisplayIconsSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("RPISystem", 0xf2add773));
required.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99));
required.push_back(AZ_CRC("AtomBridgeService", 0x92d990b5));
}
void AtomViewportDisplayIconsSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
@@ -59,12 +59,12 @@ namespace AZ
void OcclusionCullingPlaneComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d));
provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x7d036c2e));
}
void OcclusionCullingPlaneComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d));
incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x7d036c2e));
}
void OcclusionCullingPlaneComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
@@ -31,12 +31,12 @@ namespace Blast
private:
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("BlastEditorService", 0x0a61cda5));
provided.push_back(AZ_CRC("BlastEditorService", 0xeddfed0d));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("BlastService", 0x75beae2d));
required.push_back(AZ_CRC("BlastService", 0x46927a9f));
}
AZStd::unique_ptr<EditorBlastChunksAssetHandler> m_editorBlastChunksAssetHandler;
@@ -33,7 +33,7 @@ namespace GraphModelIntegration
//! Constructor
//! \param nodeName Name of the node that will show up in the Palette
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a))
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0x0a1dff96))
//! \param dataType The type of data that the InputGraphNode or OutputGraphNode will represent
InputOutputNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId, GraphModel::DataTypePtr dataType)
: DraggableNodePaletteTreeItem(nodeName, editorId)
@@ -95,7 +95,7 @@ namespace GraphModelIntegration
AZ_CLASS_ALLOCATOR(ModuleNodePaletteItem, AZ::SystemAllocator, 0);
//! Constructor
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a))
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0x0a1dff96))
//! \param sourceFileId The unique id for the module node graph source file.
//! \param sourceFilePath The path to the module node graph source file. This will be used for node naming and debug output.
ModuleNodePaletteItem(GraphCanvas::EditorId editorId, AZ::Uuid sourceFileId, AZStd::string_view sourceFilePath)
@@ -34,7 +34,7 @@ namespace GraphModelIntegration
//! Constructor
//! \param nodeName Name of the node that will show up in the Palette
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a))
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0x0a1dff96))
StandardNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId)
: DraggableNodePaletteTreeItem(nodeName, editorId)
{
@@ -22,7 +22,7 @@ namespace LmbrCentral
{
AZ_CLASS_ALLOCATOR_IMPL(EditorTubeShapeComponentMode, AZ::SystemAllocator, 0)
static const AZ::Crc32 s_resetVariableRadii = AZ_CRC("com.o3de.action.tubeshape.reset_radii", 0x0f2ef8e2);
static const AZ::Crc32 s_resetVariableRadii = AZ_CRC("com.o3de.action.tubeshape.reset_radii", 0xa987659c);
static const char* const s_resetRadiiTitle = "Reset Radii";
static const char* const s_resetRadiiDesc = "Reset all variable radius values to the default";
@@ -50,7 +50,7 @@ class PropertyHandlerUiParticleColorKeyframe
public:
AZ_CLASS_ALLOCATOR(PropertyHandlerUiParticleColorKeyframe, AZ::SystemAllocator, 0);
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleColorKeyframeCtrl", 0x8cb3a9f1); }
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleColorKeyframeCtrl", 0xe3ef28b6); }
bool IsDefaultHandler() const override { return true; }
QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyUiParticleColorKeyframeCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
@@ -50,7 +50,7 @@ class PropertyHandlerUiParticleFloatKeyframe
public:
AZ_CLASS_ALLOCATOR(PropertyHandlerUiParticleFloatKeyframe, AZ::SystemAllocator, 0);
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleFloatKeyframeCtrl", 0xba9359a2); }
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleFloatKeyframeCtrl", 0x448a90ec); }
bool IsDefaultHandler() const override { return true; }
QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyUiParticleFloatKeyframeCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
@@ -31,12 +31,12 @@ namespace LyShine
void LyShineLoadScreenComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xBB5EAB17));
provided.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xbb5eab17));
}
void LyShineLoadScreenComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xBB5EAB17));
incompatible.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xbb5eab17));
}
void LyShineLoadScreenComponent::Init()
@@ -27,10 +27,10 @@ namespace PhysX
namespace
{
//! Uri's for shortcut actions.
const AZ::Crc32 SetDimensionsSubModeActionUri = AZ_CRC("com.o3de.action.physx.setdimensionssubmode", 0x77b70dd6);
const AZ::Crc32 SetOffsetSubModeActionUri = AZ_CRC("com.o3de.action.physx.setoffsetsubmode", 0xc06132e5);
const AZ::Crc32 SetRotationSubModeActionUri = AZ_CRC("com.o3de.action.physx.setrotationsubmode", 0xc4225918);
const AZ::Crc32 ResetSubModeActionUri = AZ_CRC("com.o3de.action.physx.resetsubmode", 0xb70b120e);
const AZ::Crc32 SetDimensionsSubModeActionUri = AZ_CRC("com.o3de.action.physx.setdimensionssubmode", 0x508b1781);
const AZ::Crc32 SetOffsetSubModeActionUri = AZ_CRC("com.o3de.action.physx.setoffsetsubmode", 0x777ac743);
const AZ::Crc32 SetRotationSubModeActionUri = AZ_CRC("com.o3de.action.physx.setrotationsubmode", 0xf1a8f3ff);
const AZ::Crc32 ResetSubModeActionUri = AZ_CRC("com.o3de.action.physx.resetsubmode", 0x599d1594);
} // namespace
AZ_CLASS_ALLOCATOR_IMPL(ColliderComponentMode, AZ::SystemAllocator, 0);
@@ -32,7 +32,7 @@ namespace PhysXDebug
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("PhysXDebugEditorService", 0xe3dde7d8));
provided.push_back(AZ_CRC("PhysXDebugEditorService", 0xf8611967));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
@@ -33,8 +33,8 @@ namespace WhiteBox
AZ::Color, cl_whiteBoxVertexIndicatorColor, AZ::Color::CreateFromRgba(0, 0, 0, 102), nullptr,
AZ::ConsoleFunctorFlags::Null, "The color of the vertex indicator");
static const AZ::Crc32 HideEdge = AZ_CRC("com.o3de.action.whitebox.hide_edge", 0x6a60ae23);
static const AZ::Crc32 HideVertex = AZ_CRC("com.o3de.action.whitebox.hide_vertex", 0x4a4bd092);
static const AZ::Crc32 HideEdge = AZ_CRC("com.o3de.action.whitebox.hide_edge", 0x84f6a9b9);
static const AZ::Crc32 HideVertex = AZ_CRC("com.o3de.action.whitebox.hide_vertex", 0x5f81c937);
static const char* const HideEdgeTitle = "Hide Edge";
static const char* const HideEdgeDesc = "Hide the selected edge to merge the two connected polygons";
+2 -4
View File
@@ -6,10 +6,8 @@
#
#
# this ctest makes sure that the commit validation function
# also runs its tests during commit validation!
ly_add_pytest(
NAME test_commit_validation
PATH ${CMAKE_CURRENT_LIST_DIR}
PATH ${CMAKE_CURRENT_LIST_DIR}/commit_validation/tests
TEST_SUITE smoke
)
@@ -181,4 +181,6 @@ EXCLUDED_VALIDATION_PATTERNS = [
'restricted/*/Tools/*RemoteControl',
'*/user/Cache/*',
'*/user/log/*',
'*/user/log_test_1/*',
'*/user/log_test_2/*',
]
@@ -0,0 +1,47 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import unittest
from unittest.mock import patch, mock_open
from commit_validation.tests.mocks.mock_commit import MockCommit
from commit_validation.validators.crc_validator import CrcValidator
class CrcValidatorTests(unittest.TestCase):
@patch('builtins.open', mock_open(read_data='This file does not contain an AZ_CRC macro'))
def test_fileWithNoCrc_passes(self):
commit = MockCommit(files=['/someCppFile.cpp'])
error_list = []
self.assertTrue(CrcValidator().run(commit, error_list))
self.assertEqual(len(error_list), 0, f"Unexpected errors: {error_list}")
@patch('builtins.open', mock_open(read_data='This file contains an invalid CRC macro AZ_CRC("My string", 0xabcdef00)'))
def test_fileWithInvalidCrc_fails(self):
commit = MockCommit(files=['/someCppFile.cpp'])
error_list = []
self.assertFalse(CrcValidator().run(commit, error_list))
self.assertNotEqual(len(error_list), 0, f"Errors were expected but none were returned.")
@patch('builtins.open', mock_open(read_data='This file contains a valid CRC macro AZ_CRC("My string", 0x18fbd270)'))
def test_fileWithValidCrc_fails(self):
commit = MockCommit(files=['/someCppFile.cpp'])
error_list = []
self.assertTrue(CrcValidator().run(commit, error_list))
self.assertEqual(len(error_list), 0, f"Unexpected errors: {error_list}")
@patch('builtins.open', mock_open(read_data='This file contains an invalid CRC macro AZ_CRC("My string", 0xabcdef00)'))
def test_fileExtensionIgnored_passes(self):
commit = MockCommit(files=['/someCppFile.somerandomextension'])
error_list = []
self.assertTrue(CrcValidator().run(commit, error_list))
self.assertEqual(len(error_list), 0, f"Unexpected errors: {error_list}")
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,48 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import binascii
import fnmatch
import pathlib
import re
from typing import Type, List
from commit_validation.commit_validation import Commit, CommitValidator, SOURCE_FILE_EXTENSIONS, EXCLUDED_VALIDATION_PATTERNS, VERBOSE
class CrcValidator(CommitValidator):
"""A file-level validator that makes sure a file does not contain an invalid CRC"""
def run(self, commit: Commit, errors: List[str]) -> bool:
for file_name in commit.get_files():
for pattern in EXCLUDED_VALIDATION_PATTERNS:
if fnmatch.fnmatch(file_name, pattern):
if VERBOSE: print(f'{file_name}::{self.__class__.__name__} SKIPPED - Validation pattern excluded on path.')
break
else:
if pathlib.Path(file_name).suffix.lower() not in SOURCE_FILE_EXTENSIONS:
if VERBOSE: print(f'{file_name}::{self.__class__.__name__} SKIPPED - File excluded based on extension.')
continue
with open(file_name, mode='r', encoding='utf8') as fh:
fileContents = fh.read()
matchesFound = re.findall(r'AZ_CRC\("([^"]+)",([^)]*)\)', fileContents)
for element in matchesFound:
stringInCode = element[0]
valueInCode = element[1].strip()
expectedValue = "{0:#0{1}x}".format(binascii.crc32(stringInCode.lower().encode('utf8')), 10)
if expectedValue != valueInCode:
error_message = str(f'{file_name}::{self.__class__.__name__} FAILED - Source file contains a CRC mismatch!\n'
f' AZ_CRC("{stringInCode}", {valueInCode}), expected value {expectedValue}')
if VERBOSE: print(error_message)
errors.append(error_message)
return (not errors)
def get_validator() -> Type[CrcValidator]:
"""Returns the validator class for this module"""
return CrcValidator