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

Bug Fix for #418 - Graph<T>::removeNode has potential to throw due to optional being accessed early #430

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Ajay-26
Copy link

@Ajay-26 Ajay-26 commented May 6, 2024

Pull Request for Bug #418.

  • Issue: Graph::removeNode does not check whether the node to be removed is actually present in the graph.
  • Changes: Graph::removeNode now checks whether the std::optional node obtained from Graph::getNode() is indeed present in the Graph or not. If not present, the function does nothing. There is also a test called Test_removeInvalidNode within GraphTest.cpp.

Comment on lines 235 to 239
auto isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value());

auto isolatedNodeIt = isolatedNodesSet.end();
if (nodeOpt) {
isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value());
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems ok, but it would be more concise if the entire downstream logic were gated by nodeOpt, i.e.:

auto nodeOpt = getNode(nodeUserId);

if (nodeOpt)
{
  auto isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value());

  if (isolatedNodeIt != isolatedNodesSet.end()) {
    // Remove the isolated node
     ...
  } else {
    // Remove the node and connected edges
     ...
  }
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your feedback, I made a commit with the suggested edits - let me know if it looks okay.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Approved

@@ -1340,6 +1340,45 @@ TEST(TestRemoveNode, Test_connectedNode) {
ASSERT_EQ(graph.getEdgeSet().size(), 1);
}

TEST(TestRemoveNode, Test_removeInvalidNode) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link

codecov bot commented May 6, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.87%. Comparing base (1b31e63) to head (6f4e654).
Report is 23 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #430      +/-   ##
==========================================
+ Coverage   97.58%   97.87%   +0.29%     
==========================================
  Files          87       87              
  Lines        9492    10083     +591     
  Branches        0      666     +666     
==========================================
+ Hits         9263     9869     +606     
+ Misses        229      214      -15     
Flag Coverage Δ
unittests 97.87% <100.00%> (+0.29%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

// The node is not isolated
// Remove the edges containing the node
auto edgeset = edgeSet;
for (auto edgeIt : edgeset) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I would use const auto & instead of auto

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thank you for your reply. Yes makes sense to use const auto & - no need to make a copy of the edge set, accidentally missed it. When I make that change and use const auto & edgeset = edgeSet, I run into a segmentation fault on the Test_connectedNode test. Not sure how to deal with it. Trying to look through the code to see what the problem could be

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely due to the iterator being invalidated after a successful removal (https://stackoverflow.com/a/16904496/1078527).

The solution is to increment the iterator before removing the element.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, that you'll likely need to use the explicit for loop, instead of iterator-enhanced:

for (auto edgeIt = edgeSet.begin(); edgeIt != edgeSet.end(); ++edgeIt) { ...

This is necessary because std::unordered_set automatically dereferences iterators in iterator-enhanced for loops (which is why it's crashing).

This also means you'll need a dereference on every iterator access, i.e. (*edgeIt)->

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! That's awesome, I hadn't seen that! Thank you!

@ZigRazor
Copy link
Owner

ZigRazor commented May 6, 2024

It's ok, we can wait to merge this due to the license change?
What do you think @sbaldu @nolankramer ?

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

Successfully merging this pull request may close these issues.

Graph<T>::removeNode has potential to throw due to optional being accessed early
4 participants