class Sim(object):
    def __init__(self):
        self.node = Node()
        self.node_fail = NodeFailure()
        self.path = Path(self.node_fail)

    # this generates the graph
    # Parameters: n = node amount (based on user input)
    # returns the graph (network)
    def generate_graph(self, n):
        # finds the max amount of edges that can exist
        max_edges = (n * (n - 1)) / 2

        # based on the max amount of edges, it randomizes a number of edges that our graph will have
        e = random.randint(n, max_edges)

        # this generates a random graph using the amount of nodes and edges
        g = nx.gnm_random_graph(n, e)

        # this randomly assigns weights to the edges
        for (u, v) in g.edges():
            g.edges[u, v]['weight'] = random.randint(1, 10)

        return g

    def run_sim(self):
        # get user inputs
        self.node.node_amount()
        self.node.src_node(self.node.n)
        self.node.dest_node(self.node.n)
        self.node_fail.node_failure()

        # randomly generates a list of the probability of each node failing
        self.node_fail.find_node_probs(self.node.n)

        # generates the graph
        g = self.generate_graph(self.node.n)

        # # finds the shortest path
        self.path.find_path(g, self.node.src, self.node.dest)
        self.path.print_path(g, self.node.src, self.node.dest)

        # automatically does one case of failed nodes, so user can see what happens
        print("Updates: ")
        self.path.update(g, self.node.n, self.node.src, self.node.dest)
        print("Nodes that failed: ", self.node_fail.failure)
        self.path.print_path(g, self.node.src, self.node.dest)
        #
        # will keep executing update until user enters a q
        q = ''
        while q != 'q':
            print(
                'Enter q to exit, or enter any other key to continue to update the graph with failed nodes: '
            )
            q = input()
            if q != 'q':
                print("Updates: ")
                self.path.update(g, self.node.n, self.node.src, self.node.dest)
                print("Nodes that failed: ", self.node_fail.failure)
                self.path.print_path(g, self.node.src, self.node.dest)
class IntegrationTests(unittest.TestCase):
    def setUp(self):
        self.node = Node()
        self.node_fail = NodeFailure()
        self.path = Path(self.node_fail)
        self.g = nx.Graph()
        self.g.add_nodes_from([0, 1, 2, 3, 4])

    def test_direct_path(self):
        self.g.add_weighted_edges_from([(1, 2, 1), (2, 4, 3), (1, 3, 1),
                                        (3, 4, 2)])
        self.node.n = 5
        self.node.src = 1
        self.node.dest = 4
        self.path.find_path(self.g, self.node.src, self.node.dest)
        self.assertEqual(self.path.path, [1, 3, 4])

    def test_direct_path_w_update(self):
        self.g.add_weighted_edges_from([(1, 2, 1), (2, 4, 3), (1, 3, 1),
                                        (3, 4, 2)])
        self.node.n = 5
        self.node.src = 1
        self.node.dest = 4
        self.node_fail.fail_prob = 0
        self.node_fail.node_probs = [.2, .3, .1, .4, .5]
        self.path.find_path(self.g, self.node.src, self.node.dest)
        self.path.update(self.g, 5, self.node.src, self.node.dest)
        self.assertEqual(self.path.path, [1, 3, 4])

    def test_direct_path_cost(self):
        self.g.add_weighted_edges_from([(1, 2, 1), (2, 4, 3), (1, 3, 1),
                                        (3, 4, 2)])
        self.node.n = 5
        self.node.src = 1
        self.node.dest = 4
        self.node_fail.fail_prob = 0
        self.node_fail.node_probs = [.2, .3, .1, .4, .5]
        self.path.find_path(self.g, self.node.src, self.node.dest)
        self.path.update(self.g, 5, self.node.src, self.node.dest)
        self.path.get_cost(self.g, self.node.src, self.node.dest)
        self.assertEqual(self.path.cost, 3)
class TestPathMethods(unittest.TestCase):
    def setUp(self):
        self.node_fail = NodeFailure()
        self.path = Path(self.node_fail)
        self.g = nx.Graph()
        self.g.add_nodes_from([1, 2, 3, 4, 5])

    def test_find_path_direct(self):
        self.g.add_edge(1, 3)
        self.path.find_path(self.g, 1, 3)
        self.assertEqual(self.path.path, [1, 3])

    def test_find_path_multi(self):
        self.g.add_edges_from([(1, 2), (2, 5), (1, 3), (3, 5)])
        self.path.find_path(self.g, 1, 5)
        self.assertEqual(self.path.path, [1, 2, 5])

    def test_find_path_shortest(self):
        self.g.add_weighted_edges_from([(1, 2, 1), (2, 5, 3), (1, 3, 1),
                                        (3, 5, 2)])
        self.path.find_path(self.g, 1, 5)
        self.assertEqual(self.path.path, [1, 3, 5])

    def test_find_dpath(self):
        self.g.add_edge(1, 5)
        self.path.find_path(self.g, 1, 5)
        self.assertEqual(self.path.dpath, [1, 5])

    def test_find_bfpath(self):
        self.g.add_edge(1, 2)
        self.path.find_path(self.g, 1, 2)
        self.assertEqual(self.path.bfpath, [1, 2])

    def test_find_path_none(self):
        self.g.add_edge(1, 3)
        self.path.find_path(self.g, 1, 5)
        self.assertEqual(self.path.path, [])

    def test_find_dpath_none(self):
        self.g.add_edge(1, 5)
        self.path.find_path(self.g, 1, 3)
        self.assertEqual(self.path.dpath, [])

    def test_find_bfpath_none(self):
        self.g.add_edge(1, 2)
        self.path.find_path(self.g, 1, 3)
        self.assertEqual(self.path.bfpath, [])

    def test_update(self):
        node_num = self.g.number_of_nodes()
        self.node_fail.fail_prob = .99
        self.node_fail.node_probs = [.2, .3, .1, .4, .5]
        self.path.update(self.g, 5, 1, 5)
        self.assertNotEqual(node_num, self.g.number_of_nodes())

    def test_find_get_cost(self):
        self.g.add_weighted_edges_from([(1, 3, 1), (3, 5, 2)])
        self.path.find_path(self.g, 1, 5)
        self.path.get_cost(self.g, 1, 5)
        self.assertEqual(self.path.cost, 3)

    def test_find_get_dcost(self):
        self.g.add_weighted_edges_from([(1, 3, 1), (3, 5, 2)])
        self.path.find_path(self.g, 1, 5)
        self.path.get_cost(self.g, 1, 5)
        self.assertEqual(self.path.dcost, 3)

    def test_find_get_bfcost(self):
        self.g.add_weighted_edges_from([(1, 3, 1), (3, 5, 2)])
        self.path.find_path(self.g, 1, 5)
        self.path.get_cost(self.g, 1, 5)
        self.assertEqual(self.path.bfcost, 3)