One potential problem that I ran across when working on entity test phase9 /Changelist 100 was the issue of singletons. The strategy in that test phase is simple:
- 1- Create two entities; one of which comes with "members". The one with members will create an entire association network.
- 2- Add the entity to one the "members" (at the prescribed mount point)
- 3- Check to ensure that we can traverse the network in both directions between the two entities.
- 4- Break that link
- 5- Check to make sure that we can no longer traverse the network and reach one from the other.
In the original version, one of those entities was a MemeF1 and I was adding a MemeA1 at MemeC1. I beat my head against a wall for a week. I could traverse well enough, but not delete. There seemed to be a ghostlike problem where 135 links were being created instead of the prescribed three. After stepping through with the debugger 10,000 times, it occurred to me? MemeC1 is a singleton. Every single meme in the repository that links to MemeC1 links to the exact same entity. So of course I was getting strange results. I moved to a network without a singleton and my test phase suddenly worked.
The moral of the story is that double wild card search patterns and singletons are potentially rife for trouble. I played around today with introducing a triple wildcard pattern, but that made the search method more complex, fragile and slower.
My solution in the end is simple to halt all double wild card search patterns when they try to traverse a singleton. You can still traverse a singleton, but you have to be more exact in your member path specification than a double wildcard. The table below gives examples of member path traversing between MemeG1 and MemeA3 entities in the Examples module; both of which have the singleton entity MemeC2 in their association networks and by proxy, each other. There are three columns in the table. The first indicates the direction of the search. The second is the member path syntax fed to the script command getLinkCounterpartsByType() and the third indicates whether it will be successful or not.
| MemeA3->MemeG1 | MemeB::MemeC2::MemeG1 | success |
| MemeG1->MemeA3 | MemeC2::MemeB::MemeA3 | success |
| MemeA3->MemeG1 | *::MemeC2::MemeG1 | success |
| MemeA3->MemeG1 | **::MemeC2::MemeG1 | success |
| MemeA3->MemeG1 | **::MemeG1 | failure |
| MemeG1->MemeA3 | **::MemeA3 | failure |
| MemeG1->MemeA3 | *::MemeB::MemeA3 | success |