Merge branch 'development' of https://github.com/o3de/o3de into daimini/settings-registry-origin-tracking
This commit is contained in:
@@ -10,8 +10,6 @@
|
||||
#include "StringHelpers.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
int StringHelpers::CompareIgnoreCase(const AZStd::string& str0, const AZStd::string& str1)
|
||||
{
|
||||
const size_t minLength = Util::getMin(str0.length(), str1.length());
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <vector>
|
||||
|
||||
namespace StringHelpers
|
||||
{
|
||||
|
||||
@@ -1882,7 +1882,10 @@ namespace AzQtComponents
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(m_dragCursor);
|
||||
if (!QApplication::overrideCursor())
|
||||
{
|
||||
QApplication::setOverrideCursor(m_dragCursor);
|
||||
}
|
||||
|
||||
QPoint relativePressPos = pressPos;
|
||||
|
||||
|
||||
+936
-181
File diff suppressed because it is too large
Load Diff
+68
-2
@@ -19,6 +19,7 @@
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/ComponentBus.h>
|
||||
#include <AzCore/Component/EntityBus.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
|
||||
#include <AzToolsFramework/Undo/UndoSystem.h>
|
||||
@@ -110,6 +111,7 @@ namespace AzToolsFramework
|
||||
, public EditorInspectorComponentNotificationBus::MultiHandler
|
||||
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
|
||||
, public AZ::EntitySystemBus::Handler
|
||||
, public AZ::TickBus::Handler
|
||||
, private EditorWindowUIRequestBus::Handler
|
||||
{
|
||||
Q_OBJECT;
|
||||
@@ -117,6 +119,23 @@ namespace AzToolsFramework
|
||||
|
||||
AZ_CLASS_ALLOCATOR(EntityPropertyEditor, AZ::SystemAllocator, 0)
|
||||
|
||||
enum class ReorderState
|
||||
{
|
||||
Inactive, // No row widget reordering operation is in progress.
|
||||
DraggingComponent, // User is dragging a component editor.
|
||||
DraggingRowWidget, // User is dragging a row widget around.
|
||||
UsingMenu, // User has the context menu open and may hover over a move up/down operation.
|
||||
MenuOperationInProgress, // User has selected a move/up down menu item.
|
||||
WaitForRedraw, // Wait for rebuild of RPE.
|
||||
HighlightMovedRow // User has moved a row, highlight the new position.
|
||||
};
|
||||
|
||||
enum class DropArea
|
||||
{
|
||||
Above,
|
||||
Below
|
||||
};
|
||||
|
||||
EntityPropertyEditor(QWidget* pParent = NULL, Qt::WindowFlags flags = Qt::WindowFlags(), bool isLevelEntityEditor = false);
|
||||
virtual ~EntityPropertyEditor();
|
||||
|
||||
@@ -151,6 +170,16 @@ namespace AzToolsFramework
|
||||
bool IsLockedToSpecificEntities() const { return !m_overrideSelectedEntityIds.empty(); }
|
||||
|
||||
static bool AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components, const ComponentFilter& filter);
|
||||
|
||||
ReorderState GetReorderState() const;
|
||||
ComponentEditor* GetEditorForCurrentReorderRowWidget() const;
|
||||
PropertyRowWidget* GetReorderRowWidget() const;
|
||||
PropertyRowWidget* GetReorderDropTarget() const;
|
||||
DropArea GetReorderDropArea() const;
|
||||
QPixmap GetReorderRowWidgetImage() const;
|
||||
float GetMoveIndicatorAlpha() const;
|
||||
PropertyRowWidget* GetRowToHighlight();
|
||||
|
||||
Q_SIGNALS:
|
||||
void SelectedEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name);
|
||||
|
||||
@@ -211,6 +240,9 @@ namespace AzToolsFramework
|
||||
void GetSelectedEntities(EntityIdList& selectedEntityIds) override;
|
||||
void SetNewComponentId(AZ::ComponentId componentId) override;
|
||||
|
||||
// TickBus
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
// EditorWindowRequestBus overrides
|
||||
void SetEditorUiEnabled(bool enable) override;
|
||||
|
||||
@@ -253,6 +285,10 @@ namespace AzToolsFramework
|
||||
void ContextMenuActionPullFieldData(AZ::Component* parentComponent, InstanceDataNode* fieldNode);
|
||||
void ContextMenuActionSetDataFlag(InstanceDataNode* node, AZ::DataPatch::Flag flag, bool additive);
|
||||
|
||||
void GenerateRowWidgetIndexMapToChildIndex(PropertyRowWidget* parent, int destIndex);
|
||||
void ContextMenuActionMoveItemUp(ComponentEditor* componentEditor, PropertyRowWidget* rowWidget);
|
||||
void ContextMenuActionMoveItemDown(ComponentEditor* componentEditor, PropertyRowWidget* rowWidget);
|
||||
|
||||
/// Given an InstanceDataNode, calculate a DataPatch address relative to the entity.
|
||||
/// @return true if successful.
|
||||
bool GetEntityDataPatchAddress(const InstanceDataNode* componentFieldNode, AZ::DataPatch::AddressType& dataPatchAddressOut, AZ::EntityId* entityIdOut = nullptr) const;
|
||||
@@ -341,8 +377,6 @@ namespace AzToolsFramework
|
||||
QAction* m_actionToMoveComponentsBottom = nullptr;
|
||||
QAction* m_resetToSliceAction = nullptr;
|
||||
|
||||
bool m_isShowingContextMenu = false;
|
||||
|
||||
void CreateActions();
|
||||
void UpdateActions();
|
||||
|
||||
@@ -390,6 +424,10 @@ namespace AzToolsFramework
|
||||
void ResetToSlice();
|
||||
|
||||
bool DoesOwnFocus() const;
|
||||
AZ::u32 GetHeightOfRowAndVisibleChildren(const PropertyRowWidget* row) const;
|
||||
QRect GetWidgetAndVisibleChildrenGlobalRect(const PropertyRowWidget* widget) const;
|
||||
PropertyRowWidget* GetRowWidgetAtSameLevelAfter(PropertyRowWidget* widget) const;
|
||||
PropertyRowWidget* GetRowWidgetAtSameLevelBefore(PropertyRowWidget* widget) const;
|
||||
QRect GetWidgetGlobalRect(const QWidget* widget) const;
|
||||
bool DoesIntersectWidget(const QRect& globalRect, const QWidget* widget) const;
|
||||
bool DoesIntersectSelectedComponentEditor(const QRect& globalRect) const;
|
||||
@@ -445,6 +483,8 @@ namespace AzToolsFramework
|
||||
bool HandleSelectionEvents(QObject* object, QEvent* event);
|
||||
bool m_selectionEventAccepted;
|
||||
|
||||
bool HandleMenuEvent(QObject* object, QEvent* event);
|
||||
|
||||
// drag and drop events
|
||||
QRect GetInflatedRectFromPoint(const QPoint& point, int radius) const;
|
||||
bool GetComponentsAtDropEventPosition(QDropEvent* event, AZ::Entity::ComponentArrayType& targetComponents);
|
||||
@@ -458,8 +498,12 @@ namespace AzToolsFramework
|
||||
|
||||
ComponentEditor* GetReorderDropTarget(const QRect& globalRect) const;
|
||||
bool ResetDrag(QMouseEvent* event);
|
||||
bool FindAllowedRowWidgetReorderDropTarget(const QPoint& globalPos);
|
||||
bool UpdateRowWidgetDrag(const QPoint& localPos, Qt::MouseButtons mouseButtons, const QMimeData* mimeData);
|
||||
PropertyRowWidget* FindPropertyRowWidgetAt(QPoint globalPos);
|
||||
bool UpdateDrag(const QPoint& localPos, Qt::MouseButtons mouseButtons, const QMimeData* mimeData);
|
||||
bool StartDrag(QMouseEvent* event);
|
||||
void EndRowWidgetReorder();
|
||||
bool HandleDrop(QDropEvent* event);
|
||||
bool HandleDropForComponentTypes(QDropEvent* event);
|
||||
bool HandleDropForComponentAssets(QDropEvent* event);
|
||||
@@ -468,6 +512,8 @@ namespace AzToolsFramework
|
||||
bool CanDropForComponentTypes(const QMimeData* mimeData) const;
|
||||
bool CanDropForComponentAssets(const QMimeData* mimeData) const;
|
||||
bool CanDropForAssetBrowserEntries(const QMimeData* mimeData) const;
|
||||
void SetRowWidgetHighlighted(PropertyRowWidget* rowWidget);
|
||||
|
||||
AZStd::vector<AZ::s32> ExtractComponentEditorIndicesFromMimeData(const QMimeData* mimeData) const;
|
||||
ComponentEditorVector GetComponentEditorsFromIndices(const AZStd::vector<AZ::s32>& indices) const;
|
||||
ComponentEditor* GetComponentEditorsFromIndex(const AZ::s32 index) const;
|
||||
@@ -559,6 +605,8 @@ namespace AzToolsFramework
|
||||
|
||||
QIcon m_emptyIcon;
|
||||
QIcon m_clearIcon;
|
||||
QIcon m_dragIcon;
|
||||
QCursor m_dragCursor;
|
||||
|
||||
QStandardItem* m_comboItems[StatusItems];
|
||||
EntityIdSet m_overrideSelectedEntityIds;
|
||||
@@ -566,6 +614,19 @@ namespace AzToolsFramework
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
bool m_prefabsAreEnabled = false;
|
||||
|
||||
// Reordering row widgets within the RPE.
|
||||
static constexpr float MoveFadeSeconds = 0.5f;
|
||||
|
||||
ReorderState m_currentReorderState = ReorderState::Inactive;
|
||||
ComponentEditor* m_reorderRowWidgetEditor = nullptr;
|
||||
InstanceDataNode* m_nodeToMove = nullptr;
|
||||
PropertyRowWidget* m_reorderRowWidget = nullptr;
|
||||
PropertyRowWidget* m_reorderDropTarget = nullptr;
|
||||
DropArea m_reorderDropArea = DropArea::Above;
|
||||
QPixmap m_reorderRowImage;
|
||||
float m_moveFadeSecondsRemaining;
|
||||
AZStd::vector<int> m_indexMapOfMovedRow;
|
||||
|
||||
// When m_initiatingPropertyChangeNotification is set to true, it means this EntityPropertyEditor is
|
||||
// broadcasting a change to all listeners about a property change for a given entity. This is needed
|
||||
// so that we don't update the values twice for this inspector
|
||||
@@ -573,6 +634,9 @@ namespace AzToolsFramework
|
||||
void ConnectToEntityBuses(const AZ::EntityId& entityId);
|
||||
void DisconnectFromEntityBuses(const AZ::EntityId& entityId);
|
||||
|
||||
void BeginMoveRowWidgetFade();
|
||||
void HighlightMovedRowWidget();
|
||||
|
||||
//! Stores a component id to be focused on next time the UI updates.
|
||||
AZStd::optional<AZ::ComponentId> m_newComponentId;
|
||||
|
||||
@@ -594,6 +658,8 @@ namespace AzToolsFramework
|
||||
|
||||
bool SelectedEntitiesAreFromSameSourceSliceEntity() const;
|
||||
|
||||
void DragStopped();
|
||||
|
||||
AZ::Entity* GetSelectedEntityById(AZ::EntityId& entityId) const;
|
||||
};
|
||||
|
||||
|
||||
+182
-1
@@ -368,10 +368,15 @@ namespace AzToolsFramework
|
||||
delete m_containerAddButton;
|
||||
}
|
||||
|
||||
this->unsetCursor();
|
||||
|
||||
if ((m_parentRow) && (m_parentRow->IsContainerEditable()))
|
||||
{
|
||||
if (!m_elementRemoveButton)
|
||||
{
|
||||
QIcon icon = QIcon(QStringLiteral(":/Cursors/Grab_release.svg"));
|
||||
this->setCursor(QCursor(icon.pixmap(16), 5, 2));
|
||||
|
||||
static QIcon s_iconRemove(QStringLiteral(":/stylesheet/img/UI20/delete-16.svg"));
|
||||
m_elementRemoveButton = new QToolButton(this);
|
||||
m_elementRemoveButton->setAutoRaise(true);
|
||||
@@ -570,7 +575,12 @@ namespace AzToolsFramework
|
||||
AZ_Assert(m_selectionEnabled, "Property is not selectable");
|
||||
m_isSelected = selected;
|
||||
m_nameLabel->setProperty("selected", selected);
|
||||
}
|
||||
}
|
||||
|
||||
bool PropertyRowWidget::GetSelected()
|
||||
{
|
||||
return m_isSelected;
|
||||
}
|
||||
|
||||
void PropertyRowWidget::SetSelectionEnabled(bool selectionEnabled)
|
||||
{
|
||||
@@ -1395,6 +1405,21 @@ namespace AzToolsFramework
|
||||
return !m_childrenRows.empty();
|
||||
}
|
||||
|
||||
AZ::u32 PropertyRowWidget::GetChildRowCount() const
|
||||
{
|
||||
return static_cast<AZ::u32>(m_childrenRows.size());
|
||||
}
|
||||
|
||||
PropertyRowWidget* PropertyRowWidget::GetChildRowByIndex(AZ::u32 index) const
|
||||
{
|
||||
if (index >= m_childrenRows.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_childrenRows[index];
|
||||
}
|
||||
|
||||
bool PropertyRowWidget::ShouldPreValidatePropertyChange() const
|
||||
{
|
||||
return (m_changeValidators.size() > 0);
|
||||
@@ -1722,6 +1747,162 @@ namespace AzToolsFramework
|
||||
|
||||
return m_parentRow->CanChildrenBeReordered();
|
||||
}
|
||||
|
||||
int PropertyRowWidget::GetIndexInParent() const
|
||||
{
|
||||
if (!GetParentRow())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (AZ::u32 index = 0; index < GetParentRow()->GetChildRowCount(); index++)
|
||||
{
|
||||
if (GetParentRow()->GetChildrenRows()[index] == this)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool PropertyRowWidget::CanMoveUp() const
|
||||
{
|
||||
if (!CanBeReordered())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this != m_parentRow->GetChildRowByIndex(0);
|
||||
}
|
||||
|
||||
bool PropertyRowWidget::CanMoveDown() const
|
||||
{
|
||||
if (!CanBeReordered())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::u32 numChildrenOfParent = m_parentRow->GetChildRowCount();
|
||||
|
||||
return this != m_parentRow->GetChildRowByIndex(numChildrenOfParent - 1);
|
||||
}
|
||||
|
||||
int PropertyRowWidget::GetContainingEditorFrameWidth()
|
||||
{
|
||||
QWidget* parent = parentWidget();
|
||||
|
||||
// Find the first ancestor that can be cast to a QFrame, this will be the RPE.
|
||||
while (!qobject_cast<QFrame*>(parent))
|
||||
{
|
||||
parent = parent->parentWidget();
|
||||
}
|
||||
|
||||
if (!parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The parent of the RPE is the size we want.
|
||||
parent = parent->parentWidget();
|
||||
|
||||
return parent->rect().width();
|
||||
}
|
||||
|
||||
int PropertyRowWidget::GetHeightOfRowAndVisibleChildren()
|
||||
{
|
||||
int height = rect().height();
|
||||
|
||||
if (!GetChildRowCount() || !IsExpanded())
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
for (auto childRow : GetChildrenRows())
|
||||
{
|
||||
height += childRow->GetHeightOfRowAndVisibleChildren();
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
int PropertyRowWidget::DrawDragImageAndVisibleChildrenInto(QPainter& painter, int xpos, int ypos)
|
||||
{
|
||||
// Render our image into the given painter.
|
||||
int ystart = ypos;
|
||||
|
||||
render(&painter, QPoint(xpos, ypos));
|
||||
|
||||
if (!GetChildRowCount() || !IsExpanded())
|
||||
{
|
||||
return rect().height();
|
||||
}
|
||||
|
||||
ypos += rect().height();
|
||||
|
||||
// Recursively draw any children.
|
||||
for (auto childRow : GetChildrenRows())
|
||||
{
|
||||
ypos += childRow->DrawDragImageAndVisibleChildrenInto(painter, xpos, ypos);
|
||||
}
|
||||
|
||||
return ypos - ystart;
|
||||
}
|
||||
|
||||
QPixmap PropertyRowWidget::createDragImage(
|
||||
const QColor backgroundColor, const QColor borderColor, const float alpha, DragImageType imageType)
|
||||
{
|
||||
// Make the drag box as wide as the containing editor minus a gap each side for the border.
|
||||
static constexpr int ParentEditorBorderSize = 2;
|
||||
int width = GetContainingEditorFrameWidth() - ParentEditorBorderSize * 2;
|
||||
int height = 0;
|
||||
|
||||
if (imageType == DragImageType::IncludeVisibleChildren)
|
||||
{
|
||||
height = GetHeightOfRowAndVisibleChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
height = rect().height();
|
||||
}
|
||||
|
||||
const auto dpr = devicePixelRatioF();
|
||||
QPixmap dragImage(width * dpr, height * dpr);
|
||||
dragImage.setDevicePixelRatio(dpr);
|
||||
dragImage.fill(Qt::transparent);
|
||||
|
||||
QRect imageRect = QRect(0, 0, width, height);
|
||||
|
||||
QPainter dragPainter(&dragImage);
|
||||
dragPainter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
dragPainter.fillRect(imageRect, Qt::transparent);
|
||||
dragPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
dragPainter.setOpacity(alpha);
|
||||
dragPainter.fillRect(imageRect, backgroundColor);
|
||||
|
||||
dragPainter.setOpacity(1.0f);
|
||||
|
||||
int marginWidth = (imageRect.width() - rect().width()) / 2 + ParentEditorBorderSize - 1;
|
||||
|
||||
if (imageType == DragImageType::IncludeVisibleChildren)
|
||||
{
|
||||
DrawDragImageAndVisibleChildrenInto(dragPainter, marginWidth, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
render(&dragPainter, QPoint(marginWidth, 0));
|
||||
}
|
||||
|
||||
QPen pen;
|
||||
pen.setColor(QColor(borderColor));
|
||||
pen.setWidth(1);
|
||||
dragPainter.setPen(pen);
|
||||
dragPainter.drawRect(0, 0, imageRect.width() - 1, imageRect.height() - 1);
|
||||
|
||||
dragPainter.end();
|
||||
|
||||
return dragImage;
|
||||
}
|
||||
}
|
||||
|
||||
#include "UI/PropertyEditor/moc_PropertyRowWidget.cpp"
|
||||
|
||||
+21
@@ -45,6 +45,13 @@ namespace AzToolsFramework
|
||||
Q_PROPERTY(bool appendDefaultLabelToName READ GetAppendDefaultLabelToName WRITE AppendDefaultLabelToName)
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PropertyRowWidget, AZ::SystemAllocator, 0)
|
||||
|
||||
enum class DragImageType
|
||||
{
|
||||
SingleRow,
|
||||
IncludeVisibleChildren
|
||||
};
|
||||
|
||||
PropertyRowWidget(QWidget* pParent);
|
||||
virtual ~PropertyRowWidget();
|
||||
|
||||
@@ -86,6 +93,9 @@ namespace AzToolsFramework
|
||||
bool GetAppendDefaultLabelToName();
|
||||
void AppendDefaultLabelToName(bool doAppend);
|
||||
|
||||
AZ::u32 GetChildRowCount() const;
|
||||
PropertyRowWidget* GetChildRowByIndex(AZ::u32 index) const;
|
||||
|
||||
AZStd::vector<PropertyRowWidget*>& GetChildrenRows() { return m_childrenRows; }
|
||||
bool HasChildRows() const;
|
||||
|
||||
@@ -124,6 +134,7 @@ namespace AzToolsFramework
|
||||
|
||||
void SetSelectionEnabled(bool selectionEnabled);
|
||||
void SetSelected(bool selected);
|
||||
bool GetSelected();
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
@@ -152,9 +163,18 @@ namespace AzToolsFramework
|
||||
bool CanChildrenBeReordered() const;
|
||||
bool CanBeReordered() const;
|
||||
|
||||
int GetIndexInParent() const;
|
||||
bool CanMoveUp() const;
|
||||
bool CanMoveDown() const;
|
||||
|
||||
int GetContainingEditorFrameWidth();
|
||||
QPixmap createDragImage(const QColor backgroundColor, const QColor borderColor, const float alpha, DragImageType imageType);
|
||||
protected:
|
||||
int CalculateLabelWidth() const;
|
||||
|
||||
int GetHeightOfRowAndVisibleChildren();
|
||||
int DrawDragImageAndVisibleChildrenInto(QPainter& painter, int xpos, int ypos);
|
||||
|
||||
bool IsHidden(InstanceDataNode* node) const;
|
||||
|
||||
struct ChangeNotification;
|
||||
@@ -216,6 +236,7 @@ namespace AzToolsFramework
|
||||
bool m_isMultiSizeContainer = false;
|
||||
bool m_isFixedSizeOrSmartPtrContainer = false;
|
||||
bool m_custom = false;
|
||||
bool m_canChildrenBeReordered = false;
|
||||
|
||||
bool m_isSelected = false;
|
||||
bool m_selectionEnabled = false;
|
||||
|
||||
+220
-4
@@ -19,6 +19,7 @@
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
#include <QtWidgets/QScrollArea>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QPainter>
|
||||
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QTextFormat::d': class 'QSharedDataPointer<QTextFormatPrivate>' needs to have dll-interface to be used by clients of class 'QTextFormat'
|
||||
#include <QtWidgets/QInputDialog>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
@@ -1343,7 +1344,7 @@ namespace AzToolsFramework
|
||||
|
||||
// calculate the index/offset of the instance data node in the container
|
||||
// (useful for notifying which element in a vector was modified/removed)
|
||||
static size_t CalculateElementIndexInContainer(
|
||||
static int CalculateElementIndexInContainer(
|
||||
InstanceDataNode* node, void* parentInstanceNode,
|
||||
AZ::SerializeContext::IDataContainer* container, AZStd::vector<void*>& nodeInstancesOut)
|
||||
{
|
||||
@@ -1358,7 +1359,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
size_t elementIndex = 0;
|
||||
int elementIndex = 0;
|
||||
void* elementPtr = nodeInstancesOut.empty() ? nullptr : nodeInstancesOut.front();
|
||||
|
||||
// find the index of the element we are about to remove
|
||||
@@ -1429,7 +1430,7 @@ namespace AzToolsFramework
|
||||
|
||||
// if the element being modified exists in a container, calculate
|
||||
// the index to be passed through to PropertyNotify
|
||||
const auto calculateElementIndex = [](InstanceDataNode* node) -> size_t {
|
||||
const auto calculateElementIndex = [](InstanceDataNode* node) -> int {
|
||||
if (InstanceDataNode* parent = node->GetParent())
|
||||
{
|
||||
if (AZ::SerializeContext::IDataContainer* container = parent->GetClassMetadata()->m_container)
|
||||
@@ -1656,6 +1657,221 @@ namespace AzToolsFramework
|
||||
AzToolsFramework::Refresh_EntireTree);
|
||||
}
|
||||
|
||||
InstanceDataNode* ReflectedPropertyEditor::FindContainerNodeForNode(InstanceDataNode* node) const
|
||||
{
|
||||
// Locate the owning container. There may be a level of indirection due to wrappers, such as DynamicSerializableField.
|
||||
InstanceDataNode* pContainerNode = node->GetParent();
|
||||
if (!pContainerNode)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (pContainerNode && !pContainerNode->GetClassMetadata()->m_container)
|
||||
{
|
||||
pContainerNode = pContainerNode->GetParent();
|
||||
node = node->GetParent();
|
||||
}
|
||||
|
||||
// Check for pContainerNode again, can happen if a node is deleted during operation.
|
||||
if (!pContainerNode)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (IsParentAssociativeContainer(pContainerNode) && IsPairContainer(pContainerNode))
|
||||
{
|
||||
// Go up one more level to the associative container, we'll remove the pair from that container
|
||||
pContainerNode = pContainerNode->GetParent();
|
||||
node = node->GetParent();
|
||||
}
|
||||
|
||||
AZ_Assert(
|
||||
pContainerNode, "Failed to locate parent container for element \"%s\" of type %s.",
|
||||
node->GetElementMetadata() ? node->GetElementMetadata()->m_name : node->GetClassMetadata()->m_name,
|
||||
node->GetClassMetadata()->m_typeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
return pContainerNode;
|
||||
}
|
||||
|
||||
InstanceDataNode* ReflectedPropertyEditor::GetNodeAtIndex(int index)
|
||||
{
|
||||
if (index >= m_impl->m_widgetsInDisplayOrder.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return GetNodeFromWidget(m_impl->m_widgetsInDisplayOrder[index]);
|
||||
}
|
||||
|
||||
QSet<PropertyRowWidget*> ReflectedPropertyEditor::GetTopLevelWidgets()
|
||||
{
|
||||
return m_impl->getTopLevelWidgets();
|
||||
}
|
||||
|
||||
void ReflectedPropertyEditor::ChangeNodeIndex(InstanceDataNode* containerNode, InstanceDataNode* node, int fromIndex, int toIndex)
|
||||
{
|
||||
auto container = containerNode->GetElementMetadata()
|
||||
? containerNode->GetElementMetadata()->m_genericClassInfo->GetClassData()->m_container
|
||||
: nullptr;
|
||||
|
||||
if (fromIndex == toIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!container || container->GetAssociativeContainerInterface())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Uuid typeId = node->GetClassMetadata()->m_typeId;
|
||||
|
||||
if (m_impl->m_ptrNotify)
|
||||
{
|
||||
m_impl->m_ptrNotify->BeforePropertyModified(containerNode);
|
||||
}
|
||||
|
||||
const AZ::SerializeContext::ClassElement* containerClassElement = container->GetElement(container->GetDefaultElementNameCrc());
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
|
||||
// Backup the item we're moving.
|
||||
void* srcElement = nullptr;
|
||||
void* destElement = nullptr;
|
||||
|
||||
int destIndex = -1;
|
||||
int srcIndex = fromIndex;
|
||||
|
||||
srcElement = container->GetElementByIndex(containerNode->GetInstance(0), containerClassElement, srcIndex);
|
||||
|
||||
void* tmpBuffer = serializeContext->CloneObject(srcElement, typeId);
|
||||
|
||||
// Shuffle all intervening items up (or down).
|
||||
int indexOffset = (toIndex < fromIndex) ? -1 : 1;
|
||||
|
||||
while (destIndex != toIndex - indexOffset)
|
||||
{
|
||||
destIndex = srcIndex;
|
||||
srcIndex += indexOffset;
|
||||
|
||||
destElement = srcElement;
|
||||
|
||||
srcElement = container->GetElementByIndex(containerNode->GetInstance(0), containerClassElement, srcIndex);
|
||||
|
||||
serializeContext->CloneObjectInplace(destElement, srcElement, typeId);
|
||||
}
|
||||
|
||||
// Now replace the final element with the one backed up previously.
|
||||
destElement = srcElement;
|
||||
|
||||
serializeContext->CloneObjectInplace(destElement, tmpBuffer, typeId);
|
||||
|
||||
if (m_impl->m_ptrNotify)
|
||||
{
|
||||
m_impl->m_ptrNotify->AfterPropertyModified(containerNode);
|
||||
m_impl->m_ptrNotify->SealUndoStack();
|
||||
}
|
||||
|
||||
// Need to refresh any pinned inspectors as well to keep the container state in sync
|
||||
QueueInvalidation(Refresh_Values);
|
||||
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values);
|
||||
}
|
||||
|
||||
void ReflectedPropertyEditor::MoveNodeToIndex(InstanceDataNode* node, int index)
|
||||
{
|
||||
InstanceDataNode* pContainerNode = FindContainerNodeForNode(node);
|
||||
|
||||
if (!pContainerNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
|
||||
|
||||
AZStd::vector<void*> nodeInstancesOut;
|
||||
const int elementIndex = CalculateElementIndexInContainer(node, pContainerNode->GetInstance(0), container, nodeInstancesOut);
|
||||
|
||||
ChangeNodeIndex(pContainerNode, node, elementIndex, index);
|
||||
}
|
||||
|
||||
void ReflectedPropertyEditor::MoveNodeBefore(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore)
|
||||
{
|
||||
InstanceDataNode* pContainerNode = FindContainerNodeForNode(nodeToMove);
|
||||
InstanceDataNode* pContainerNodeTarget = FindContainerNodeForNode(nodeToMoveBefore);
|
||||
|
||||
if (nodeToMove == nodeToMoveBefore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Can only move nodes within the same parent.
|
||||
if (pContainerNode != pContainerNodeTarget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
|
||||
|
||||
AZStd::vector<void*> nodeInstancesOut;
|
||||
int elementIndex = CalculateElementIndexInContainer(nodeToMove, pContainerNode->GetInstance(0), container, nodeInstancesOut);
|
||||
nodeInstancesOut.clear();
|
||||
int elementIndexTarget =
|
||||
CalculateElementIndexInContainer(nodeToMoveBefore, pContainerNode->GetInstance(0), container, nodeInstancesOut);
|
||||
|
||||
if (elementIndex < elementIndexTarget)
|
||||
{
|
||||
elementIndexTarget -= 1;
|
||||
}
|
||||
|
||||
ChangeNodeIndex(pContainerNode, nodeToMove, elementIndex, elementIndexTarget);
|
||||
}
|
||||
|
||||
void ReflectedPropertyEditor::MoveNodeAfter(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore)
|
||||
{
|
||||
InstanceDataNode* pContainerNode = FindContainerNodeForNode(nodeToMove);
|
||||
InstanceDataNode* pContainerNodeTarget = FindContainerNodeForNode(nodeToMoveBefore);
|
||||
|
||||
if (nodeToMove == nodeToMoveBefore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Can only move nodes within the same parent.
|
||||
if (pContainerNode != pContainerNodeTarget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
|
||||
|
||||
AZStd::vector<void*> nodeInstancesOut;
|
||||
int elementIndex = CalculateElementIndexInContainer(nodeToMove, pContainerNode->GetInstance(0), container, nodeInstancesOut);
|
||||
nodeInstancesOut.clear();
|
||||
int elementIndexTarget =
|
||||
CalculateElementIndexInContainer(nodeToMoveBefore, pContainerNode->GetInstance(0), container, nodeInstancesOut);
|
||||
|
||||
if (elementIndex > elementIndexTarget)
|
||||
{
|
||||
elementIndexTarget += 1;
|
||||
}
|
||||
|
||||
ChangeNodeIndex(pContainerNode, nodeToMove, elementIndex, elementIndexTarget);
|
||||
}
|
||||
|
||||
int ReflectedPropertyEditor::GetNodeIndexInContainer(InstanceDataNode* node)
|
||||
{
|
||||
InstanceDataNode* pContainerNode = FindContainerNodeForNode(node);
|
||||
|
||||
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
|
||||
|
||||
AZStd::vector<void*> nodeInstancesOut;
|
||||
int elementIndex = CalculateElementIndexInContainer(node, pContainerNode->GetInstance(0), container, nodeInstancesOut);
|
||||
|
||||
return elementIndex;
|
||||
}
|
||||
|
||||
void ReflectedPropertyEditor::OnPropertyRowRequestContainerRemoveItem(PropertyRowWidget* widget, InstanceDataNode* node)
|
||||
{
|
||||
// Locate the owning container. There may be a level of indirection due to wrappers, such as DynamicSerializableField.
|
||||
@@ -1690,7 +1906,7 @@ namespace AzToolsFramework
|
||||
|
||||
// the index of the element being removed
|
||||
AZStd::vector<void*> nodeInstancesOut;
|
||||
const size_t elementIndex = CalculateElementIndexInContainer(
|
||||
const int elementIndex = CalculateElementIndexInContainer(
|
||||
node, pContainerNode->GetInstance(0), container, nodeInstancesOut);
|
||||
|
||||
// pass the context as the last parameter to actually delete the related data.
|
||||
|
||||
+10
@@ -155,9 +155,19 @@ namespace AzToolsFramework
|
||||
using VisibilityCallback = AZStd::function<void(InstanceDataNode* node, NodeDisplayVisibility& visibility, bool& checkChildVisibility)>;
|
||||
void SetVisibilityCallback(VisibilityCallback callback);
|
||||
|
||||
void MoveNodeToIndex(InstanceDataNode* node, int index);
|
||||
void MoveNodeBefore(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore);
|
||||
void MoveNodeAfter(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore);
|
||||
|
||||
int GetNodeIndexInContainer(InstanceDataNode* node);
|
||||
InstanceDataNode* GetNodeAtIndex(int index);
|
||||
QSet<PropertyRowWidget*> GetTopLevelWidgets();
|
||||
signals:
|
||||
void OnExpansionContractionDone();
|
||||
private:
|
||||
InstanceDataNode* FindContainerNodeForNode(InstanceDataNode* node) const;
|
||||
void ChangeNodeIndex(InstanceDataNode* containerNode, InstanceDataNode* node, int oldIndex, int newIndex);
|
||||
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
|
||||
@@ -2873,7 +2873,7 @@ void CXConsole::Paste()
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int CXConsole::GetNumVars()
|
||||
{
|
||||
return (int)m_mapVariables.size();
|
||||
return static_cast<int>(m_mapVariables.size());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -3132,7 +3132,6 @@ char* CXConsole::GetCheatVarAt(uint32 nOffset)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, const char* szPrefix)
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t iPrefixLen = szPrefix ? strlen(szPrefix) : 0;
|
||||
|
||||
// variables
|
||||
@@ -3140,11 +3139,6 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
|
||||
ConsoleVariablesMap::const_iterator it, end = m_mapVariables.end();
|
||||
for (it = m_mapVariables.begin(); it != end; ++it)
|
||||
{
|
||||
if (i >= pszArray.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (szPrefix)
|
||||
{
|
||||
if (_strnicmp(it->first, szPrefix, iPrefixLen) != 0)
|
||||
@@ -3158,9 +3152,7 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
|
||||
continue;
|
||||
}
|
||||
|
||||
pszArray[i] = it->first;
|
||||
|
||||
i++;
|
||||
pszArray.push_back(it->first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3169,11 +3161,6 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
|
||||
ConsoleCommandsMap::iterator it, end = m_mapCommands.end();
|
||||
for (it = m_mapCommands.begin(); it != end; ++it)
|
||||
{
|
||||
if (i >= pszArray.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (szPrefix)
|
||||
{
|
||||
if (_strnicmp(it->first.c_str(), szPrefix, iPrefixLen) != 0)
|
||||
@@ -3187,25 +3174,18 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
|
||||
continue;
|
||||
}
|
||||
|
||||
pszArray[i] = it->first.c_str();
|
||||
|
||||
i++;
|
||||
pszArray.push_back(it->first.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
std::sort(pszArray.begin(), pszArray.end());
|
||||
}
|
||||
|
||||
return i;
|
||||
std::sort(pszArray.begin(), pszArray.end());
|
||||
return pszArray.size();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CXConsole::FindVar(const char* substr)
|
||||
{
|
||||
AZStd::vector<AZStd::string_view> cmds;
|
||||
cmds.resize(GetNumVars() + m_mapCommands.size());
|
||||
size_t cmdCount = GetSortedVars(cmds);
|
||||
|
||||
for (size_t i = 0; i < cmdCount; i++)
|
||||
@@ -3231,10 +3211,9 @@ const char* CXConsole::AutoComplete(const char* substr)
|
||||
// following code can be optimized
|
||||
|
||||
AZStd::vector<AZStd::string_view> cmds;
|
||||
cmds.resize(GetNumVars() + m_mapCommands.size());
|
||||
size_t cmdCount = GetSortedVars(cmds);
|
||||
|
||||
size_t substrLen = strlen(substr);
|
||||
size_t substrLen = substr ? strlen(substr) : 0;
|
||||
|
||||
// If substring is empty return first command.
|
||||
if (substrLen == 0 && cmdCount > 0)
|
||||
@@ -3246,7 +3225,7 @@ const char* CXConsole::AutoComplete(const char* substr)
|
||||
for (size_t i = 0; i < cmdCount; i++)
|
||||
{
|
||||
const char* szCmd = cmds[i].data();
|
||||
size_t cmdlen = strlen(szCmd);
|
||||
size_t cmdlen = cmds[i].size();
|
||||
if (cmdlen >= substrLen && memcmp(szCmd, substr, substrLen) == 0)
|
||||
{
|
||||
if (substrLen == cmdlen)
|
||||
@@ -3267,7 +3246,7 @@ const char* CXConsole::AutoComplete(const char* substr)
|
||||
{
|
||||
const char* szCmd = cmds[i].data();
|
||||
|
||||
size_t cmdlen = strlen(szCmd);
|
||||
size_t cmdlen = cmds[i].size();
|
||||
if (cmdlen >= substrLen && azstrnicmp(szCmd, substr, substrLen) == 0)
|
||||
{
|
||||
if (substrLen == cmdlen)
|
||||
@@ -3301,27 +3280,19 @@ void CXConsole::SetInputLine(const char* szLine)
|
||||
const char* CXConsole::AutoCompletePrev(const char* substr)
|
||||
{
|
||||
AZStd::vector<AZStd::string_view> cmds;
|
||||
cmds.resize(GetNumVars() + m_mapCommands.size());
|
||||
size_t cmdCount = GetSortedVars(cmds);
|
||||
GetSortedVars(cmds);
|
||||
|
||||
// If substring is empty return last command.
|
||||
if (strlen(substr) == 0 && cmds.size() > 0)
|
||||
if (strlen(substr) == 0 && !cmds.empty())
|
||||
{
|
||||
return cmds[cmdCount - 1].data();
|
||||
return cmds.back().data();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < cmdCount; i++)
|
||||
for (const AZStd::string_view& cmd : cmds)
|
||||
{
|
||||
if (azstricmp(substr, cmds[i].data()) == 0)
|
||||
if (azstricmp(substr, cmd.data()) == 0)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
return cmds[i - 1].data();
|
||||
}
|
||||
else
|
||||
{
|
||||
return cmds[0].data();
|
||||
}
|
||||
return cmd.data();
|
||||
}
|
||||
}
|
||||
return AutoComplete(substr);
|
||||
|
||||
+6
-19
@@ -27,7 +27,7 @@ namespace TestImpact
|
||||
"relative_paths",
|
||||
"artifact_dir",
|
||||
"enumeration_cache_dir",
|
||||
"test_impact_data_files",
|
||||
"test_impact_data_file",
|
||||
"temp",
|
||||
"active",
|
||||
"target_sources",
|
||||
@@ -72,7 +72,7 @@ namespace TestImpact
|
||||
RelativePaths,
|
||||
ArtifactDir,
|
||||
EnumerationCacheDir,
|
||||
TestImpactDataFiles,
|
||||
TestImpactDataFile,
|
||||
TempWorkspace,
|
||||
ActiveWorkspace,
|
||||
TargetSources,
|
||||
@@ -138,31 +138,18 @@ namespace TestImpact
|
||||
tempWorkspaceConfig.m_artifactDirectory =
|
||||
GetAbsPathFromRelPath(
|
||||
tempWorkspaceConfig.m_root, tempWorkspace[Config::Keys[Config::RelativePaths]][Config::Keys[Config::ArtifactDir]].GetString());
|
||||
tempWorkspaceConfig.m_enumerationCacheDirectory = GetAbsPathFromRelPath(
|
||||
tempWorkspaceConfig.m_root,
|
||||
tempWorkspace[Config::Keys[Config::RelativePaths]][Config::Keys[Config::EnumerationCacheDir]].GetString());
|
||||
return tempWorkspaceConfig;
|
||||
}
|
||||
|
||||
AZStd::array<RepoPath, 3> ParseTestImpactAnalysisDataFiles(const RepoPath& root, const rapidjson::Value& sparTiaFile)
|
||||
{
|
||||
AZStd::array<RepoPath, 3> sparTiaFiles;
|
||||
sparTiaFiles[static_cast<size_t>(SuiteType::Main)] =
|
||||
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Main).c_str()].GetString());
|
||||
sparTiaFiles[static_cast<size_t>(SuiteType::Periodic)] =
|
||||
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Periodic).c_str()].GetString());
|
||||
sparTiaFiles[static_cast<size_t>(SuiteType::Sandbox)] =
|
||||
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Sandbox).c_str()].GetString());
|
||||
|
||||
return sparTiaFiles;
|
||||
}
|
||||
|
||||
WorkspaceConfig::Active ParseActiveWorkspaceConfig(const rapidjson::Value& activeWorkspace)
|
||||
{
|
||||
WorkspaceConfig::Active activeWorkspaceConfig;
|
||||
const auto& relativePaths = activeWorkspace[Config::Keys[Config::RelativePaths]];
|
||||
activeWorkspaceConfig.m_root = activeWorkspace[Config::Keys[Config::Root]].GetString();
|
||||
activeWorkspaceConfig.m_enumerationCacheDirectory
|
||||
= GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::EnumerationCacheDir]].GetString());
|
||||
activeWorkspaceConfig.m_sparTiaFiles =
|
||||
ParseTestImpactAnalysisDataFiles(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::TestImpactDataFiles]]);
|
||||
activeWorkspaceConfig.m_sparTiaFile = relativePaths[Config::Keys[Config::TestImpactDataFile]].GetString();
|
||||
return activeWorkspaceConfig;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -530,7 +530,7 @@ namespace TestImpact
|
||||
size_t GetTotalNumTimedOutTestRuns() const override;
|
||||
size_t GetTotalNumUnexecutedTestRuns() const override;
|
||||
|
||||
//! Returns the report for the discarded test runs.
|
||||
// ImpactAnalysisSequenceReport overrides ...
|
||||
const TestRunSelection GetDiscardedTestRuns() const;
|
||||
|
||||
//! Returns the report for the discarded test runs.
|
||||
|
||||
+2
-2
@@ -37,14 +37,14 @@ namespace TestImpact
|
||||
{
|
||||
RepoPath m_root; //!< Path to the temporary workspace (cleaned prior to use).
|
||||
RepoPath m_artifactDirectory; //!< Path to read and write runtime artifacts to and from.
|
||||
RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache.
|
||||
};
|
||||
|
||||
//! Active persistent data workspace configuration.
|
||||
struct Active
|
||||
{
|
||||
RepoPath m_root; //!< Path to the persistent workspace tracked by the repository.
|
||||
RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache.
|
||||
AZStd::array<RepoPath, 3> m_sparTiaFiles; //!< Paths to the test impact analysis data files for each test suite.
|
||||
RepoPath m_sparTiaFile; //!< Paths to the test impact analysis data file.
|
||||
};
|
||||
|
||||
Temp m_temp;
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace TestImpact
|
||||
m_testEngine = AZStd::make_unique<TestEngine>(
|
||||
m_config.m_repo.m_root,
|
||||
m_config.m_target.m_outputDirectory,
|
||||
m_config.m_workspace.m_active.m_enumerationCacheDirectory,
|
||||
m_config.m_workspace.m_temp.m_enumerationCacheDirectory,
|
||||
m_config.m_workspace.m_temp.m_artifactDirectory,
|
||||
m_config.m_testEngine.m_testRunner.m_binary,
|
||||
m_config.m_testEngine.m_instrumentation.m_binary,
|
||||
@@ -289,7 +289,8 @@ namespace TestImpact
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sparTiaFile = m_config.m_workspace.m_active.m_sparTiaFiles[static_cast<size_t>(m_suiteFilter)].String();
|
||||
m_sparTiaFile =
|
||||
m_config.m_workspace.m_active.m_root / RepoPath(SuiteTypeAsString(m_suiteFilter)) / m_config.m_workspace.m_active.m_sparTiaFile;
|
||||
}
|
||||
|
||||
// Populate the dynamic dependency map with the existing source coverage data (if any)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AzCore/Math/MathIntrinsics.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/typetraits/aligned_storage.h>
|
||||
#include <AzCore/base.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -46,11 +46,26 @@ namespace PhysX
|
||||
|
||||
JointComponent::LeadFollowerInfo leadFollowerInfo;
|
||||
ObtainLeadFollowerInfo(leadFollowerInfo);
|
||||
if (!leadFollowerInfo.m_followerActor)
|
||||
if (leadFollowerInfo.m_followerActor == nullptr ||
|
||||
leadFollowerInfo.m_followerBody == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no lead body, this will be a constraint of the follower's global position, so use invalid body handle.
|
||||
AzPhysics::SimulatedBodyHandle parentHandle = AzPhysics::InvalidSimulatedBodyHandle;
|
||||
if (leadFollowerInfo.m_leadBody != nullptr)
|
||||
{
|
||||
parentHandle = leadFollowerInfo.m_leadBody->m_bodyHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
"PhysX", "Entity [%s] Ball Joint component missing lead entity. This joint will be a global constraint on the follower's global position.",
|
||||
GetEntity()->GetName().c_str());
|
||||
}
|
||||
|
||||
|
||||
BallJointConfiguration configuration;
|
||||
configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation();
|
||||
configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation();
|
||||
@@ -65,7 +80,7 @@ namespace PhysX
|
||||
m_jointHandle = sceneInterface->AddJoint(
|
||||
leadFollowerInfo.m_followerBody->m_sceneOwner,
|
||||
&configuration,
|
||||
leadFollowerInfo.m_leadBody->m_bodyHandle,
|
||||
parentHandle,
|
||||
leadFollowerInfo.m_followerBody->m_bodyHandle);
|
||||
m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner;
|
||||
}
|
||||
|
||||
@@ -54,11 +54,25 @@ namespace PhysX
|
||||
|
||||
JointComponent::LeadFollowerInfo leadFollowerInfo;
|
||||
ObtainLeadFollowerInfo(leadFollowerInfo);
|
||||
if (!leadFollowerInfo.m_followerActor)
|
||||
if (leadFollowerInfo.m_followerActor == nullptr ||
|
||||
leadFollowerInfo.m_followerBody == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no lead body, this will be a constraint of the follower's global position, so use invalid body handle.
|
||||
AzPhysics::SimulatedBodyHandle parentHandle = AzPhysics::InvalidSimulatedBodyHandle;
|
||||
if (leadFollowerInfo.m_leadBody != nullptr)
|
||||
{
|
||||
parentHandle = leadFollowerInfo.m_leadBody->m_bodyHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf("PhysX",
|
||||
"Entity [%s] Fixed Joint component missing lead entity. This joint will be a global constraint on the follower's global position.",
|
||||
GetEntity()->GetName().c_str());
|
||||
}
|
||||
|
||||
FixedJointConfiguration configuration;
|
||||
configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation();
|
||||
configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation();
|
||||
@@ -72,7 +86,7 @@ namespace PhysX
|
||||
m_jointHandle = sceneInterface->AddJoint(
|
||||
leadFollowerInfo.m_followerBody->m_sceneOwner,
|
||||
&configuration,
|
||||
leadFollowerInfo.m_leadBody->m_bodyHandle,
|
||||
parentHandle,
|
||||
leadFollowerInfo.m_followerBody->m_bodyHandle);
|
||||
m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner;
|
||||
}
|
||||
|
||||
@@ -48,12 +48,24 @@ namespace PhysX
|
||||
JointComponent::LeadFollowerInfo leadFollowerInfo;
|
||||
ObtainLeadFollowerInfo(leadFollowerInfo);
|
||||
if (leadFollowerInfo.m_followerActor == nullptr ||
|
||||
leadFollowerInfo.m_leadBody == nullptr ||
|
||||
leadFollowerInfo.m_followerBody == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no lead body, this will be a constraint of the follower's global position, so use invalid body handle.
|
||||
AzPhysics::SimulatedBodyHandle parentHandle = AzPhysics::InvalidSimulatedBodyHandle;
|
||||
if (leadFollowerInfo.m_leadBody != nullptr)
|
||||
{
|
||||
parentHandle = leadFollowerInfo.m_leadBody->m_bodyHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
"PhysX", "Entity [%s] Hinge Joint component missing lead entity. This joint will be a global constraint on the follower's global position.",
|
||||
GetEntity()->GetName().c_str());
|
||||
}
|
||||
|
||||
HingeJointConfiguration configuration;
|
||||
configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation();
|
||||
configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation();
|
||||
@@ -66,7 +78,9 @@ namespace PhysX
|
||||
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
|
||||
{
|
||||
m_jointHandle = sceneInterface->AddJoint(
|
||||
leadFollowerInfo.m_followerBody->m_sceneOwner, &configuration, leadFollowerInfo.m_leadBody->m_bodyHandle,
|
||||
leadFollowerInfo.m_followerBody->m_sceneOwner,
|
||||
&configuration,
|
||||
parentHandle,
|
||||
leadFollowerInfo.m_followerBody->m_bodyHandle);
|
||||
m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner;
|
||||
}
|
||||
|
||||
@@ -190,8 +190,9 @@ namespace PhysX {
|
||||
{
|
||||
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
|
||||
|
||||
if (!actorData.parentActor || !actorData.childActor)
|
||||
if (actorData.parentActor == nullptr && actorData.childActor == nullptr)
|
||||
{
|
||||
AZ_Warning("PhysX Joint", false, "CreateJoint failed - at least one body must be a PxRigidActor.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -239,7 +240,8 @@ namespace PhysX {
|
||||
{
|
||||
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
|
||||
|
||||
if (!actorData.parentActor || !actorData.childActor)
|
||||
//only check the child actor, as a null parent actor means this joint is a global constraint.
|
||||
if (!actorData.childActor)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -252,7 +254,8 @@ namespace PhysX {
|
||||
|
||||
{
|
||||
PHYSX_SCENE_READ_LOCK(actorData.childActor->getScene());
|
||||
joint = physx::PxFixedJointCreate(PxGetPhysics(),
|
||||
joint = physx::PxFixedJointCreate(
|
||||
PxGetPhysics(),
|
||||
actorData.parentActor, PxMathConvert(parentLocalTM),
|
||||
actorData.childActor, PxMathConvert(childLocalTM));
|
||||
}
|
||||
@@ -272,7 +275,8 @@ namespace PhysX {
|
||||
{
|
||||
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
|
||||
|
||||
if (!actorData.parentActor || !actorData.childActor)
|
||||
// only check the child actor, as a null parent actor means this joint is a global constraint.
|
||||
if (!actorData.childActor)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -306,7 +310,8 @@ namespace PhysX {
|
||||
{
|
||||
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
|
||||
|
||||
if (!actorData.parentActor || !actorData.childActor)
|
||||
// only check the child actor, as a null parent actor means this joint is a global constraint.
|
||||
if (!actorData.childActor)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace PhysX
|
||||
|
||||
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
|
||||
|
||||
EXPECT_TRUE(followerEndPosition.GetX() > followerPosition.GetX());
|
||||
EXPECT_GT(followerEndPosition.GetX(), followerPosition.GetX());
|
||||
}
|
||||
|
||||
TEST_F(PhysXJointsTest, Joint_HingeJoint_FollowerSwingsAroundLead)
|
||||
@@ -164,8 +164,8 @@ namespace PhysX
|
||||
|
||||
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
|
||||
|
||||
EXPECT_TRUE(followerEndPosition.GetX() > followerPosition.GetX());
|
||||
EXPECT_TRUE(abs(followerEndPosition.GetZ()) > FLT_EPSILON);
|
||||
EXPECT_GT(followerEndPosition.GetX(), followerPosition.GetX());
|
||||
EXPECT_GT(abs(followerEndPosition.GetZ()), FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST_F(PhysXJointsTest, Joint_BallJoint_FollowerSwingsUpAboutLead)
|
||||
@@ -206,7 +206,65 @@ namespace PhysX
|
||||
|
||||
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
|
||||
|
||||
EXPECT_TRUE(followerEndPosition.GetZ() > followerPosition.GetZ());
|
||||
EXPECT_GT(followerEndPosition.GetZ(), followerPosition.GetZ());
|
||||
}
|
||||
|
||||
TEST_F(PhysXJointsTest, Joint_BallJoint_GlobalConstraint)
|
||||
{
|
||||
// Place an entity in the world with a rigid body, physx collider, and a ball joint components.
|
||||
// Do not set a lead entity on the ball joint component.
|
||||
// Set entity's initial velocity to 10 in the X and Y directions on the rigid body component.
|
||||
// The entity should swing up on the global constraint.
|
||||
|
||||
const AZ::Vector3 followerPosition(0.0f, 0.0f, -1.0f);
|
||||
const AZ::Vector3 followerInitialLinearVelocity(10.0f, 10.0f, 0.0f);
|
||||
|
||||
const AZ::Vector3 jointLocalPosition(0.0f, 0.0f, 2.0f);
|
||||
const AZ::Quaternion jointLocalRotation = AZ::Quaternion::CreateRotationY(90.0f);
|
||||
const AZ::Transform jointLocalTransform = AZ::Transform::CreateFromQuaternionAndTranslation(jointLocalRotation, jointLocalPosition);
|
||||
|
||||
//we want a global constraint, so leave the lead entity unset.
|
||||
auto jointConfig = AZStd::make_shared<JointComponentConfiguration>();
|
||||
jointConfig->m_localTransformFromFollower = jointLocalTransform;
|
||||
|
||||
auto jointLimits = AZStd::make_shared<JointLimitProperties>();
|
||||
jointLimits->m_isLimited = false;
|
||||
|
||||
auto followerEntity = AddBodyColliderEntity<BallJointComponent>(
|
||||
m_testSceneHandle, followerPosition, followerInitialLinearVelocity, jointConfig, nullptr, jointLimits);
|
||||
|
||||
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
|
||||
|
||||
EXPECT_GT(followerEndPosition.GetZ(), followerPosition.GetZ());
|
||||
}
|
||||
|
||||
TEST_F(PhysXJointsTest, Joint_HingeJoint_GlobalConstraint)
|
||||
{
|
||||
// Place an entity in the world with a rigid body, physx collider, and a hinge joint components.
|
||||
// Do not set a lead entity on the hinge joint component.
|
||||
// Set entity's initial velocity to 10 in the X and Y directions on the rigid body component.
|
||||
// The entity should swing up on the global constraint.
|
||||
|
||||
const AZ::Vector3 followerPosition(0.0f, 0.0f, -1.0f);
|
||||
const AZ::Vector3 followerInitialLinearVelocity(10.0f, 10.0f, 0.0f);
|
||||
|
||||
const AZ::Vector3 jointLocalPosition(0.0f, 0.0f, 2.0f);
|
||||
const AZ::Quaternion jointLocalRotation = AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 180.0f, 90.0f));
|
||||
const AZ::Transform jointLocalTransform = AZ::Transform::CreateFromQuaternionAndTranslation(jointLocalRotation, jointLocalPosition);
|
||||
|
||||
// do not set the lead entity as that makes this a global constraint
|
||||
auto jointConfig = AZStd::make_shared<JointComponentConfiguration>();
|
||||
jointConfig->m_localTransformFromFollower = jointLocalTransform;
|
||||
|
||||
auto jointLimits = AZStd::make_shared<JointLimitProperties>();
|
||||
jointLimits->m_isLimited = false;
|
||||
|
||||
auto followerEntity = AddBodyColliderEntity<HingeJointComponent>(
|
||||
m_testSceneHandle, followerPosition, followerInitialLinearVelocity, jointConfig, nullptr, jointLimits);
|
||||
|
||||
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
|
||||
|
||||
EXPECT_GT(followerEndPosition.GetZ(), followerPosition.GetZ());
|
||||
}
|
||||
|
||||
// for some reason TYPED_TEST_CASE with the fixture is not working on Android + Linux
|
||||
|
||||
@@ -15,19 +15,14 @@
|
||||
"temp": {
|
||||
"root": "${temp_dir}",
|
||||
"relative_paths": {
|
||||
"artifact_dir": "RuntimeArtifact"
|
||||
"artifact_dir": "RuntimeArtifact",
|
||||
"enumeration_cache_dir": "EnumerationCache"
|
||||
}
|
||||
},
|
||||
"active": {
|
||||
"root": "${active_dir}",
|
||||
"relative_paths": {
|
||||
"test_impact_data_files": {
|
||||
"main": "TestImpactData.main.spartia",
|
||||
"periodic": "TestImpactData.periodic.spartia",
|
||||
"sandbox": "TestImpactData.sandbox.spartia"
|
||||
},
|
||||
"enumeration_cache_dir": "EnumerationCache",
|
||||
"last_build_target_list_file": "LastRunBuildTargets.json"
|
||||
"test_impact_data_file": "TestImpactData.spartia"
|
||||
}
|
||||
},
|
||||
"historic": {
|
||||
|
||||
@@ -372,9 +372,12 @@ CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_FORMAT_STR = """
|
||||
}}
|
||||
|
||||
compile{config}Sources.dependsOn copyNativeArtifacts{config}
|
||||
"""
|
||||
|
||||
CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_DEPENDENCY_FORMAT_STR = """
|
||||
|
||||
copyNativeArtifacts{config}.mustRunAfter {{
|
||||
tasks.findAll {{ task->task.name.contains('externalNativeBuild{config}') }}
|
||||
tasks.findAll {{ task->task.name.contains('syncLYLayoutMode{config}') }}
|
||||
}}
|
||||
"""
|
||||
|
||||
@@ -383,7 +386,13 @@ CUSTOM_APPLY_ASSET_LAYOUT_TASK_FORMAT_STR = """
|
||||
workingDir '{working_dir}'
|
||||
commandLine '{python_full_path}', 'layout_tool.py', '--project-path', '{project_path}', '-p', 'Android', '-a', '{asset_type}', '-m', '{asset_mode}', '--create-layout-root', '-l', '{asset_layout_folder}'
|
||||
}}
|
||||
|
||||
compile{config}Sources.dependsOn syncLYLayoutMode{config}
|
||||
|
||||
syncLYLayoutMode{config}.mustRunAfter {{
|
||||
tasks.findAll {{ task->task.name.contains('externalNativeBuild{config}') }}
|
||||
}}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@@ -832,25 +841,28 @@ class AndroidProjectGenerator(object):
|
||||
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
|
||||
file_includes='Test.Assets/**/*.*')
|
||||
else:
|
||||
# Copy over settings registry files from the Registry folder with build output directory
|
||||
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] = \
|
||||
CUSTOM_APPLY_ASSET_LAYOUT_TASK_FORMAT_STR.format(working_dir=common.normalize_path_for_settings(self.engine_root / 'cmake/Tools'),
|
||||
python_full_path=common.normalize_path_for_settings(self.engine_root / 'python' / PYTHON_SCRIPT),
|
||||
asset_type=self.asset_type,
|
||||
project_path=self.project_path.as_posix(),
|
||||
asset_mode=self.asset_mode if native_config != 'Release' else 'PAK',
|
||||
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
|
||||
config=native_config)
|
||||
# Copy over settings registry files from the Registry folder with build output directory
|
||||
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] += \
|
||||
CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_FORMAT_STR.format(config=native_config,
|
||||
config_lower=native_config_lower,
|
||||
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
|
||||
file_includes='**/Registry/*.setreg')
|
||||
|
||||
if self.include_assets_in_apk:
|
||||
if not self.is_test_project:
|
||||
if self.include_assets_in_apk:
|
||||
# This is a dependency of the layout sync only if we are including assets in the APK
|
||||
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] += \
|
||||
CUSTOM_APPLY_ASSET_LAYOUT_TASK_FORMAT_STR.format(working_dir=common.normalize_path_for_settings(self.engine_root / 'cmake/Tools'),
|
||||
python_full_path=common.normalize_path_for_settings(self.engine_root / 'python' / PYTHON_SCRIPT),
|
||||
asset_type=self.asset_type,
|
||||
project_path=self.project_path.as_posix(),
|
||||
asset_mode=self.asset_mode if native_config != 'Release' else 'PAK',
|
||||
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
|
||||
config=native_config)
|
||||
else:
|
||||
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] = ''
|
||||
CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_DEPENDENCY_FORMAT_STR.format(config=native_config)
|
||||
|
||||
|
||||
|
||||
|
||||
if self.signing_config:
|
||||
gradle_build_env[f'SIGNING_{native_config_upper}_CONFIG'] = f'signingConfig signingConfigs.{native_config_lower}' if self.signing_config else ''
|
||||
else:
|
||||
|
||||
@@ -230,7 +230,7 @@ class TestImpact:
|
||||
|
||||
# Flag for corner case where:
|
||||
# 1. TIAF was already run previously for this commit.
|
||||
# 2. There was no last commit hash when TIAF last ran on this commit (due to no coverage data existing get for this branch)
|
||||
# 2. There was no last commit hash when TIAF last ran on this commit (due to no coverage data existing yet for this branch)
|
||||
# 3. TIAF has not been run on any other commits between the run for this commit and the last run for this commit.
|
||||
# The above results in TIAF being stuck in a state of generating an empty change list (and thus doing no work until another
|
||||
# commit comes in) which is problematic if the commit needs to be re-run for whatever reason so in these conditions we revert
|
||||
@@ -323,7 +323,6 @@ class TestImpact:
|
||||
logger.info(f"Args: {unpacked_args}")
|
||||
runtime_result = subprocess.run([str(self._tiaf_bin)] + args)
|
||||
report = None
|
||||
|
||||
# If the sequence completed (with or without failures) we will update the historical meta-data
|
||||
if runtime_result.returncode == 0 or runtime_result.returncode == 7:
|
||||
logger.info("Test impact analysis runtime returned successfully.")
|
||||
|
||||
@@ -17,11 +17,11 @@ logger = get_logger(__file__)
|
||||
class PersistentStorage(ABC):
|
||||
|
||||
WORKSPACE_KEY = "workspace"
|
||||
LAST_RUNS_KEY = "last_runs"
|
||||
HISTORIC_SEQUENCES_KEY = "historic_sequences"
|
||||
ACTIVE_KEY = "active"
|
||||
ROOT_KEY = "root"
|
||||
RELATIVE_PATHS_KEY = "relative_paths"
|
||||
TEST_IMPACT_DATA_FILES_KEY = "test_impact_data_files"
|
||||
TEST_IMPACT_DATA_FILE_KEY = "test_impact_data_file"
|
||||
LAST_COMMIT_HASH_KEY = "last_commit_hash"
|
||||
COVERAGE_DATA_KEY = "coverage_data"
|
||||
|
||||
@@ -35,19 +35,21 @@ class PersistentStorage(ABC):
|
||||
"""
|
||||
|
||||
# Work on the assumption that there is no historic meta-data (a valid state to be in, should none exist)
|
||||
self._suite = suite
|
||||
self._last_commit_hash = None
|
||||
self._has_historic_data = False
|
||||
self._has_previous_last_commit_hash = False
|
||||
self._this_commit_hash = commit
|
||||
self._this_commit_hash_last_commit_hash = None
|
||||
self._historic_data = None
|
||||
logger.info(f"Attempting to access persistent storage for the commit {self._this_commit_hash}")
|
||||
logger.info(f"Attempting to access persistent storage for the commit '{self._this_commit_hash}' for suite '{self._suite}'")
|
||||
|
||||
try:
|
||||
# The runtime expects the coverage data to be in the location specified in the config file (unless overridden with
|
||||
# the --datafile command line argument, which the TIAF scripts do not do)
|
||||
self._active_workspace = pathlib.Path(config[self.WORKSPACE_KEY][self.ACTIVE_KEY][self.ROOT_KEY])
|
||||
unpacked_coverage_data_file = config[self.WORKSPACE_KEY][self.ACTIVE_KEY][self.RELATIVE_PATHS_KEY][self.TEST_IMPACT_DATA_FILES_KEY][suite]
|
||||
self._active_workspace = self._active_workspace.joinpath(pathlib.Path(self._suite))
|
||||
unpacked_coverage_data_file = config[self.WORKSPACE_KEY][self.ACTIVE_KEY][self.RELATIVE_PATHS_KEY][self.TEST_IMPACT_DATA_FILE_KEY]
|
||||
except KeyError as e:
|
||||
raise SystemError(f"The config does not contain the key {str(e)}.")
|
||||
|
||||
@@ -70,25 +72,27 @@ class PersistentStorage(ABC):
|
||||
self._last_commit_hash = self._historic_data[self.LAST_COMMIT_HASH_KEY]
|
||||
logger.info(f"Last commit hash '{self._last_commit_hash}' found.")
|
||||
|
||||
if self.LAST_RUNS_KEY in self._historic_data:
|
||||
# Last commit hash for the sequence that was run for this commit previously (if any)
|
||||
if self._this_commit_hash in self._historic_data[self.LAST_RUNS_KEY]:
|
||||
# Last commit hash for the sequence that was run for this commit previously (if any)
|
||||
if self.HISTORIC_SEQUENCES_KEY in self._historic_data:
|
||||
if self._this_commit_hash in self._historic_data[self.HISTORIC_SEQUENCES_KEY]:
|
||||
# 'None' is a valid value for the previously used last commit hash if there was no coverage data at that time
|
||||
self._this_commit_hash_last_commit_hash = self._historic_data[self.LAST_RUNS_KEY][self._this_commit_hash]
|
||||
self._this_commit_hash_last_commit_hash = self._historic_data[self.HISTORIC_SEQUENCES_KEY][self._this_commit_hash]
|
||||
self._has_previous_last_commit_hash = self._this_commit_hash_last_commit_hash is not None
|
||||
|
||||
if self._has_previous_last_commit_hash:
|
||||
logger.info(f"Last commit hash '{self._this_commit_hash_last_commit_hash}' was used previously for this commit.")
|
||||
else:
|
||||
logger.info(f"Prior sequence data found for this commit but it is empty (there was no coverage data vailable at that time).")
|
||||
logger.info(f"Prior sequence data found for this commit but it is empty (there was no coverage data available at that time).")
|
||||
else:
|
||||
logger.info(f"No prior sequence data found for commit '{self._this_commit_hash}', this is the first sequence for this commit.")
|
||||
else:
|
||||
logger.info(f"No prior sequence data found for any commits.")
|
||||
|
||||
# Create the active workspace directory where the coverage data file will be placed and unpack the coverage data so
|
||||
# it is accessible by the runtime
|
||||
# Create the active workspace directory for the unpacked historic data files so they are accessible by the runtime
|
||||
self._active_workspace.mkdir(exist_ok=True)
|
||||
|
||||
# Coverage file
|
||||
logger.info(f"Writing coverage data to '{self._unpacked_coverage_data_file}'.")
|
||||
with open(self._unpacked_coverage_data_file, "w", newline='\n') as coverage_data:
|
||||
coverage_data.write(self._historic_data[self.COVERAGE_DATA_KEY])
|
||||
|
||||
@@ -117,9 +121,9 @@ class PersistentStorage(ABC):
|
||||
self._historic_data[self.LAST_COMMIT_HASH_KEY] = self._this_commit_hash
|
||||
|
||||
# Last commit hash for this commit
|
||||
if not self.LAST_RUNS_KEY in self._historic_data:
|
||||
self._historic_data[self.LAST_RUNS_KEY] = {}
|
||||
self._historic_data[self.LAST_RUNS_KEY][self._this_commit_hash] = self._last_commit_hash
|
||||
if not self.HISTORIC_SEQUENCES_KEY in self._historic_data:
|
||||
self._historic_data[self.HISTORIC_SEQUENCES_KEY] = {}
|
||||
self._historic_data[self.HISTORIC_SEQUENCES_KEY][self._this_commit_hash] = self._last_commit_hash
|
||||
|
||||
# Coverage data for this branch
|
||||
with open(self._unpacked_coverage_data_file, "r") as coverage_data:
|
||||
|
||||
@@ -32,10 +32,12 @@ class PersistentStorageLocal(PersistentStorage):
|
||||
try:
|
||||
# Attempt to obtain the local persistent data location specified in the runtime config file
|
||||
self._historic_workspace = pathlib.Path(config[self.WORKSPACE_KEY][self.HISTORIC_KEY][self.ROOT_KEY])
|
||||
self._historic_workspace = self._historic_workspace.joinpath(pathlib.Path(self._suite))
|
||||
historic_data_file = pathlib.Path(config[self.WORKSPACE_KEY][self.HISTORIC_KEY][self.RELATIVE_PATHS_KEY][self.DATA_KEY])
|
||||
|
||||
# Attempt to unpack the local historic data file
|
||||
self._historic_data_file = self._historic_workspace.joinpath(historic_data_file)
|
||||
logger.info(f"Attempting to retrieve historic data at location '{self._historic_data_file}'...")
|
||||
if self._historic_data_file.is_file():
|
||||
with open(self._historic_data_file, "r") as historic_data_raw:
|
||||
historic_data_json = historic_data_raw.read()
|
||||
|
||||
@@ -43,9 +43,9 @@ class PersistentStorageS3(PersistentStorage):
|
||||
# historic_data.json.zip is the file containing the coverage and meta-data of the last TIAF sequence run
|
||||
historic_data_file = f"historic_data.{object_extension}"
|
||||
|
||||
# The location of the data is in the form <root_dir>/<branch>/<config> so the build config of each branch gets its own historic data
|
||||
self._historic_data_dir = f'{root_dir}/{branch}/{config[self.META_KEY][self.BUILD_CONFIG_KEY]}'
|
||||
self._historic_data_key = f'{self._historic_data_dir}/{historic_data_file}'
|
||||
# The location of the data is in the form <root_dir>/<branch>/<config>/<suite> so the build config of each branch gets its own historic data
|
||||
self._historic_data_dir = f"{root_dir}/{branch}/{config[self.META_KEY][self.BUILD_CONFIG_KEY]}/{self._suite}"
|
||||
self._historic_data_key = f"{self._historic_data_dir}/{historic_data_file}"
|
||||
|
||||
logger.info(f"Attempting to retrieve historic data for branch '{branch}' at location '{self._historic_data_key}' on bucket '{s3_bucket}'...")
|
||||
self._s3 = boto3.resource("s3")
|
||||
|
||||
Reference in New Issue
Block a user