Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add referencial integrity checks to component collections #13

Open
sbabcoc opened this issue Dec 20, 2017 · 8 comments
Open

Add referencial integrity checks to component collections #13

sbabcoc opened this issue Dec 20, 2017 · 8 comments

Comments

@sbabcoc
Copy link
Owner

sbabcoc commented Dec 20, 2017

Due to the auto-recovery behavior of RobustWebElement objects, it's possible to have the items in an existing component collection silently fall out of sync with the actual page content they represent.

All of the solutions I've thought about so far will either impose overhead on every collection access or fire too late to be bulletproof.

This issue is complicated by the prospect of nested collections. When an element goes stale, all of the collections that contain the element must be rebuilt. This will require logic in the reference refresh implementation to check for collection associations. Ultimately, it may prove to be impossible to implement bulletproof auto-recovery for nested collections.

@sbabcoc
Copy link
Owner Author

sbabcoc commented Mar 6, 2018

To clarify, the issue here is that collections can represent elements that no longer exist, or that collections will fail to represent elements that have appeared since the collection was initially built.

@sbabcoc
Copy link
Owner Author

sbabcoc commented May 3, 2020

It may be sufficient to repopulate collections whenever element references they contain go stale. The current behavior merely refreshes existing references as they are used, ignoring the larger implications of stale references regarding the integrity of the entire collection.

To facility collection integrity checking, each item's root element will need a reference to the collection that contains it. If an item root element goes stale, the collection that contains it should be entirely repopulated.

The structure required to implement collection integrity checking is probably already in place, given that every enhanced web element retains the context it belongs to - its parent container. The element refresh logic will need to keep track of the highest-level component collection it encounters, repopulating this collection from top to bottom.

The refreshContext() method of the WrapsContext interface is no help here, as this is a bottom-up operation rather than the top-down repopulation we need in this scenario. Here, we'll need to invoke the core implementation of newComponentList() or newComponentMap() to repopulate.

If the size of a component list changes, or if the key set of a component map changes, this indicates that the collection has gone out of sync with the content it models. It may be desirable to implement some sort of notification mechanism, to enable models to synchronize with the system under test.

@sbabcoc
Copy link
Owner Author

sbabcoc commented Dec 10, 2020

The methods used to retrieve elements from collections would need to perform the check for referential integrity, rebuilding the collection if a stale reference is detected. Each item in the collection (list or map) includes an element reference. When a client retrieves an item from the collection, the element reference must be tested:

  1. Grab the time the reference was acquired: element.aquiredAt()
  2. Perform a low-cost operation that uses the reference: element.getTagName()
  3. Grab the element acquisition time again and compare it to the previous value.
  4. If the times don't match, this indicates that the element had gone stale and had to be refreshed. The collection must be rebuilt if the reference goes stale.
  5. Item retrieval is performed following the referential integrity check and potential repopulation of the collection.

Open question: How should we handle "optional" elements? I think that collections should explicitly prohibit the use of "optional" elements as item containers, because there's no way to build the collection without know what's in it.

@sbabcoc
Copy link
Owner Author

sbabcoc commented Dec 10, 2020

I don't know for certain, but I think the code needed to rebuild collections would just need to be extracted from the constructors for ContainerList and ContainerMap. These would be declared as new protected methods, so that subclasses could perform additional operations as required.

I don't know if it's possible for collection items to have "native" container elements. The implementation may need to ensure that container elements are "enhanced" (i.e. - RobustWebElement) objects, as native elements lack the ability to refresh themselves.

@sbabcoc
Copy link
Owner Author

sbabcoc commented Jul 12, 2021

Another thing to consider is how to handle map entries that no longer exist. These should probably throw StaleElementReferenceException for entries that once existed but no longer do after the collection is rebuilt.

@sbabcoc
Copy link
Owner Author

sbabcoc commented May 26, 2022

I'm not sure that this issue is solvable for component lists, save to determine that the size of the collection has changed. The reason is that the component container element selectors are just indexed versions of the selector from which the collection was originally populated. By definition, the index of the component in the list will always match the index in the selector.

For component maps, I should be able to compare the output of the getKey method to the associated container element key on the switchTo call. However, there's currently no explicit indication that a component is an element in a map. To resolve this part of the issue, I'll need to define a new interface that identifies the component as a map element. The API currently verifies collectability, but I need to add the ability for components to retain specific collection association:

  • component is a member of a collection (implements interface mentioned below)
  • collection to which the component belongs
  • list item index or map element key

Currently, I retain the parent container and locator used to acquire the container elements for the members of the collection. However, collections aren't currently linked into the page model hierarchy. This is a bit sticky, because collections aren't search contexts, and the current structure consists entirely of nested search contexts. You don't switch to a collection; you can only switch to search contexts.

I could add the collection membership interface/implementation during the "enhancement" process. This is a bit sneaky, but the client code shouldn't need to be aware of this implementation detail. Once the association is established between component and collection, I need to add code to the switchTo method to verify that the container element is still valid. For maps, this should be a matter of confirming that the value returned by the getKey method matches the corresponding map key.

@sbabcoc
Copy link
Owner Author

sbabcoc commented May 28, 2022

Collection integrity checks will be performed when we switch to the search context of a member of a collection. If the size of the list or the composition of the map changes, the response of the API will be to publish notifications to registered listeners. To be useful, these notifications will need to include an indication of which collection has changed. These notifications could be used for synchronization.

@sbabcoc
Copy link
Owner Author

sbabcoc commented May 28, 2022

  • There's no generic recovery strategy in the event that the size of a list changes. I can introduce a facility for clients to define model-specific handlers for this event, allowing implementers to determine how to respond.
  • If the composition of a map changes, but a component with the requested key still exists, the API could potentially perform a silent recovery as it would for a StaleElementReferenceException. However, it's probably still preferable to invoke model-specific handling if it's defined, because the key by itself may be insufficient to ensure the continuity of the target component contents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant