class TestGraphAddNodeExceptionWarning(UnittestPythonCompatibility):
    """
    Test logged warnings and raised Exceptions by Graph add_node.
    Same as for add_nodes
    """
    def setUp(self):
        """
        Build empty graph to add a node to and test default state
        """

        self.graph = Graph()

    def test_add_node_none(self):
        """
        Unable to add 'None' node when auto_nid False
        """

        # no problem when auto_nid
        self.graph.add_node()
        self.assertTrue(len(self.graph) == 1)

        self.graph.auto_nid = False
        self.assertRaises(GraphitException, self.graph.add_node, None)

    def test_add_node_hasable(self):
        """
        When auto_nid equals False, the nid should be a hashable object
        :return:
        """

        self.graph.auto_nid = False
        self.assertRaises(GraphitException, self.graph.add_node, [1, 2])

    def test_add_node_duplicate(self):
        """
        Duplication is no problem with auto_nid but without the previous node
        is updated with the attributes of the new one. A warning is logged.
        """

        # With auto_nid
        self.graph.add_nodes([1, 1])
        self.assertEqual(len(self.graph), 2)

        # Without auto_nid
        self.graph.auto_nid = False
        self.graph.add_nodes([3, 3])
        self.assertEqual(len(self.graph), 3)

        self.assertItemsEqual(self.graph.keys(), [1, 1, 3])
        self.assertItemsEqual(self.graph.keys('_id'), [1, 2, 3])
class TestGraphNodeAttribute(UnittestPythonCompatibility):
    """
    Test methods to get and set node attributes using a node storage driver.
    `DictStorage` is the default driver tested here.
    """

    def setUp(self):
        """
        Build default Graph with node and edge attributes
        """

        self.graph = Graph()
        self.graph.add_nodes([('g', {'weight': 1.0, 'value': 'gr'}), ('r', {'weight': 1.5, 'value': 'ra'}),
                              ('a', {'weight': 2.0, 'value': 'ap'}), ('p', {'weight': 2.5, 'value': 'ph'}),
                              ('h', {'weight': 3.0})])
        self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], value=True, weight=43.2, key='edge')

    def test_graph_node_attr_storeget(self):
        """
        Test getting node attributes directly from the `nodes` storage
        """

        self.assertEqual(self.graph.nodes[1]['weight'], 1.0)
        self.assertEqual(self.graph.nodes[3]['value'], 'ap')

    def test_graph_node_attr_storeset(self):
        """
        Test setting node attributes directly from the `nodes` storage
        """

        self.graph.nodes[1]['weight'] = 5.0
        self.graph.nodes[3]['value'] = 'dd'

        self.assertEqual(self.graph.nodes[1]['weight'], 5.0)
        self.assertEqual(self.graph.nodes[3]['value'], 'dd')

    def test_graph_node_attr_key_tag(self):
        """
        Test get attributes based on `key_tag`
        """

        self.assertEqual(self.graph.nodes[1][self.graph.data.key_tag], 'g')
        self.assertEqual(self.graph.nodes[3][self.graph.data.key_tag], 'a')
        self.assertEqual(self.graph.get(1), 'g')  # uses default node data tag

    def test_graph_node_attr_value_tag(self):
        """
        Test get attributes based on `value_tag`
        """

        self.assertEqual(self.graph.nodes[1][self.graph.data.value_tag], 'gr')
        self.assertEqual(self.graph.nodes[3][self.graph.data.value_tag], 'ap')

    def test_graph_node_attr_dict(self):
        """
        Test if the returned full attribute dictionary is of expected format
        """

        self.assertDictEqual(self.graph.nodes[1], {'_id': 1, 'key': 'g', 'weight': 1.0, 'value': 'gr'})
        self.assertDictEqual(self.graph.nodes[3], {'_id': 3, 'key': 'a', 'weight': 2.0, 'value': 'ap'})

    def test_graph_node_attr_exception(self):
        """
        Test `nodes` exception if node not present
        """

        self.assertRaises(GraphitException, self.graph.__getitem__, 10)
        self.assertIsNone(self.graph.nodes.get(10))

    def test_graph_node_attr_graphget(self):
        """
        Test access node attributes by nid using the (sub)graph 'get' method
        """

        self.assertEqual(self.graph.get(4), 'p')
        self.assertEqual(self.graph.get(4, 'weight'), 2.5)

        # Key does not exist
        self.assertIsNone(self.graph.get(4, key='no_key'))

        # Key does not exist return defaultkey
        self.assertEqual(self.graph.get(4, key='no_key', defaultattr='weight'), 2.5)

    def test_graph_node_attr_singlenode_get(self):
        """
        Test getting node attribute values directly using the single node
        Graph API which has the required methods (node_tools) added to it.
        """

        node = self.graph.getnodes(5)
        self.assertEqual(node['key'], 'h')
        self.assertEqual(node.key, 'h')
        self.assertEqual(node.get('key'), 'h')

        self.assertEqual(node['weight'], 3.0)
        self.assertEqual(node.weight, 3.0)
        self.assertEqual(node.get('weight'), 3.0)

    def test_graph_node_attr_singlenode_set(self):
        """
        Test setting node attribute values directly using the single node
        Graph API which has the required methods (node_tools) added to it.
        """

        node = self.graph.getnodes(5)
        node.weight = 5.0
        node['key'] = 'z'
        node.set('value', True)

        self.assertEqual(node.nodes[5]['weight'], 5.0)
        self.assertEqual(node.nodes[5]['key'], 'z')
        self.assertEqual(node.nodes[5]['value'], True)

    def test_graph_node_attr_singlenode_exception(self):
        """
        Test exceptions in direct access to node attributes in a single graph
        class
        """

        node = self.graph.getnodes(5)
        self.assertEqual(node.get(), None)  # Default get returns value, not set
        self.assertRaises(KeyError, node.__getitem__, 'no_key')
        self.assertRaises(AttributeError, node.__getattr__, 'no_key')

    def test_graph_nodes_dict_keys(self):
        """
        Test graph dict-like 'keys' support.
        """

        self.assertListEqual(self.graph.keys(), ['g', 'r', 'a', 'p', 'h'])
        self.assertListEqual(self.graph.keys('weight'), [1.0, 1.5, 2.0, 2.5, 3.0])

    def test_graph_nodes_dict_values(self):
        """
        Test graph dict-like 'values' support.
        """

        self.assertListEqual(self.graph.values(), ['gr', 'ra', 'ap', 'ph', None])
        self.assertItemsEqual(self.graph.values('no_value'), [None, None, None, None, None])

    def test_graph_nodes_dict_items(self):
        """
        Test graph dict-like 'items' support.
        """

        self.assertItemsEqual(self.graph.items(), [('g', 'gr'), ('r', 'ra'), ('a', 'ap'), ('p', 'ph'), ('h', None)])
        self.assertItemsEqual(self.graph.items(valuestring='_id'), [('g', 1), ('r', 2), ('a', 3), ('p', 4), ('h', 5)])
        self.assertItemsEqual(self.graph.items(keystring='_id', valuestring='weight'), [(1, 1.0), (2, 1.5), (3, 2.0),
                                                                                        (4, 2.5), (5, 3.0)])
class TestGraphAddNode(UnittestPythonCompatibility):
    """
    Test Graph add_node method with the Graph.auto_nid set to False
    mimicking the behaviour of many popular graph packages
    """
    currpath = os.path.dirname(__file__)
    image = os.path.join(currpath, '../', 'files', 'graph_tgf.png')

    def setUp(self):
        """
        Build empty graph to add a node to and test default state
        """

        self.graph = Graph(auto_nid=False)

        # empty before addition
        self.assertTrue(len(self.graph) == 0)
        self.assertTrue(len(self.graph.nodes) == 0)
        self.assertTrue(len(self.graph.edges) == 0)
        self.assertTrue(len(self.graph.adjacency) == 0)

        # auto_nid
        self.assertFalse(self.graph.auto_nid)

    def tearDown(self):
        """
        Test state after node addition
        """

        nid = list(self.graph.nodes)

        # The nid should equal the node
        self.assertEqual(nid, [self.node])

        # The _id is still set
        self.assertEqual(self.graph.nodes[self.node]['_id'], 1)
        self.assertEqual(self.graph._nodeid, 2)

        # filled after addition
        self.assertTrue(len(self.graph) == 1)
        self.assertTrue(len(self.graph.nodes) == 1)
        self.assertTrue(len(self.graph.edges) == 0)
        self.assertTrue(len(self.graph.adjacency) == 1)

        # no adjacency
        self.assertTrue(len(self.graph.adjacency[nid[0]]) == 0)

        # node key
        self.assertItemsEqual(self.graph.keys(), [self.node])

    def test_add_node_string(self):
        """
        Test adding a single node, string type
        """

        self.node = 'first'
        nid = self.graph.add_node(self.node)

        # Added string should be unicode
        self.assertIsInstance(self.graph.nodes[nid][self.graph.key_tag],
                              UNICODE_TYPE)

    def test_add_node_int(self):
        """
        Test adding a single node, int type
        """

        self.node = 100
        self.graph.add_node(self.node)

    def test_add_node_float(self):
        """
        Test adding a single node, float type
        """

        self.node = 4.55
        self.graph.add_node(self.node)

    def test_add_node_bool(self):
        """
        Test adding a single node, float bool
        """

        self.node = False
        self.graph.add_node(self.node)

    def test_add_node_function(self):
        """
        Test adding a single node, function type
        """

        self.node = map
        self.graph.add_node(self.node)

    def test_add_node_object(self):
        """
        Test adding an object as a single node.
        In this case the object is file
        """

        self.node = open(self.image, 'r')
        self.graph.add_node(self.node)
class TestGraphAddNodesAutonid(UnittestPythonCompatibility):
    """
    Test Graph the add_nodes method using different input with the Graph class
    set to default auto_nid = True
    """
    def setUp(self):
        """
        Build empty graph to add nodes to and test default state
        """

        self.graph = Graph()

        # empty before addition
        self.assertTrue(len(self.graph) == 0)
        self.assertTrue(len(self.graph.nodes) == 0)
        self.assertTrue(len(self.graph.edges) == 0)
        self.assertTrue(len(self.graph.adjacency) == 0)

        # auto_nid
        self.assertTrue(self.graph.auto_nid)
        self.assertEqual(self.graph._nodeid, 1)

    def tearDown(self):
        """
        Test state after node addition
        """

        length = len(self.itr)
        nids = range(1, length + 1)

        # auto_nid
        self.assertItemsEqual(list(self.graph.nodes), nids)
        self.assertEqual(self.graph._nodeid, length + 1)

        # filled after addition
        self.assertTrue(len(self.graph) == length)
        self.assertTrue(len(self.graph.nodes) == length)
        self.assertTrue(len(self.graph.edges) == 0)
        self.assertTrue(len(self.graph.adjacency) == length)

        # node key
        self.assertItemsEqual(self.graph.keys(), self.itr)

    def test_add_nodes_from_list(self):
        """
        Test adding multiple nodes from a list
        """

        self.itr = [1, 2, 3]
        self.graph.add_nodes(self.itr)

    def test_add_nodes_from_tuple(self):
        """
        Test adding multiple nodes from a tuple
        """

        self.itr = [1, 2, 3]
        self.graph.add_nodes(tuple(self.itr))

    def test_add_nodes_from_set(self):
        """
        Test adding multiple nodes from a set
        """

        self.itr = [1, 2, 3]
        self.graph.add_nodes(set(self.itr))

    def test_add_nodes_from_dict(self):
        """
        Test adding multiple nodes from a dict
        """

        self.itr = [1, 2, 3]
        self.graph.add_nodes({1: 'one', 2: 'two', 3: 'three'})

    def test_add_nodes_from_string(self):
        """
        Test adding multiple nodes from a string
        """

        self.itr = ['g', 'r', 'a', 'p', 'h']
        self.graph.add_nodes(''.join(self.itr))

    def test_add_nodes_from_range(self):
        """
        Test adding multiple nodes from a range
        """

        self.itr = range(5)
        self.graph.add_nodes(self.itr)

    def test_add_nodes_from_graph(self):
        """
        Test adding multiple nodes from another graph
        """

        self.itr = [1, 2, 3]
        g = Graph()
        g.add_nodes(self.itr)

        self.graph.add_nodes(g.nodes)
class TestGraphAddNodeAutonid(UnittestPythonCompatibility):
    """
    Test Graph add_node method using different input with the Graph class
    set to default auto_nid = True
    """
    currpath = os.path.dirname(__file__)
    image = os.path.join(currpath, '../', 'files', 'graph_tgf.png')

    def setUp(self):
        """
        Build empty graph to add a node to and test default state
        """

        self.graph = Graph()

        # empty before addition
        self.assertTrue(len(self.graph) == 0)
        self.assertTrue(len(self.graph.nodes) == 0)
        self.assertTrue(len(self.graph.edges) == 0)
        self.assertTrue(len(self.graph.adjacency) == 0)

        # auto_nid
        self.assertTrue(self.graph.auto_nid)
        self.assertEqual(self.graph._nodeid, 1)

    def tearDown(self):
        """
        Test state after node addition
        """

        nid = list(self.graph.nodes)

        # auto_nid
        self.assertItemsEqual(nid, [1])
        self.assertEqual(self.graph._nodeid, 2)

        # filled after addition
        self.assertTrue(len(self.graph) == 1)
        self.assertTrue(len(self.graph.nodes) == 1)
        self.assertTrue(len(self.graph.edges) == 0)
        self.assertTrue(len(self.graph.adjacency) == 1)

        # no adjacency
        self.assertTrue(len(self.graph.adjacency[nid[0]]) == 0)

        # node key
        self.assertItemsEqual(self.graph.keys(), [self.node])

    def test_add_node_string(self):
        """
        Test adding a single node, string type
        """

        self.node = 'first'
        nid = self.graph.add_node(self.node)

        # Added string should be unicode
        self.assertIsInstance(self.graph.nodes[nid][self.graph.key_tag],
                              UNICODE_TYPE)

    def test_add_node_int(self):
        """
        Test adding a single node, int type
        """

        self.node = 100
        self.graph.add_node(self.node)

    def test_add_node_float(self):
        """
        Test adding a single node, float type
        """

        self.node = 4.55
        self.graph.add_node(self.node)

    def test_add_node_bool(self):
        """
        Test adding a single node, float bool
        """

        self.node = False
        self.graph.add_node(self.node)

    def test_add_node_function(self):
        """
        Test adding a single node, function type
        """

        self.node = map
        self.graph.add_node(self.node)

    def test_add_node_list(self):
        """
        Test adding a single node, list type
        """

        self.node = [1.22, 4.5, 6]
        self.graph.add_node(self.node)

    def test_add_node_set(self):
        """
        Test adding a single node, set type
        """

        self.node = {1.22, 4.5, 6}
        self.graph.add_node(self.node)

    def test_add_node_object(self):
        """
        Test adding an object as a single node.
        In this case the object is file
        """

        self.node = open(self.image, 'rb')
        self.graph.add_node(self.node)

    def test_add_node_image(self):
        """
        Test adding an object as a single node.
        In this case the object is file and we do not convert it to unicode
        """

        self.node = open(self.image, 'rb').read()
        self.graph.add_node(self.node, unicode_convert=False)