# -*- coding: utf-8 -*-
# Test graph dot file generation
#
# Copyright (C) Andrew Bartlett 2018.
#
# Written by Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Tests for samba.graph"""

from __future__ import print_function

import samba
import samba.tests
from samba import graph

import re
import itertools


class DotFileTests(samba.tests.TestCaseInTempDir):

    def assertMatch(self, exp, s):
        m = re.match(exp, s)
        if m is None:
            self.fail("%r did not match /%s/" % (s, exp))
        return m

    def assertHeader(self, lines, title, directed):
        self.assertEqual(lines[0], '/* generated by samba */')
        if directed:
            exp = r'^digraph \w+ {$'
        else:
            exp = r'^graph \w+ {$'
        self.assertMatch(exp, lines[1])
        m = self.assertMatch(r'^label="([\w ]+)";$', lines[2])
        self.assertEqual(m.group(1), title)
        self.assertMatch(r'^fontsize=10;$', lines[3])
        self.assertMatch(r'$', lines[4])
        self.assertEqual(lines[5], 'node[fontname=Helvetica; fontsize=10];')
        self.assertEqual(lines[6], '')

    def assertVertices(self, lines, names):
        for n, line in zip(names, lines):
            m = self.assertMatch(r'^"(\w+)";$', line)
            self.assertEqual(n, m.group(1))

    def assertEdges(self, lines, edges, directed):
        connector = '->' if directed else '--'

        for edge, line in zip(edges, lines):
            a, b = edge
            m = self.assertMatch((r'^"(\w+)" ([>-]{2}) '
                                  r'"(\w+)" ?(?:\[([^\]])\])?;$'),
                                 line)
            self.assertEqual(m.group(1), a)
            self.assertEqual(m.group(2), connector)
            self.assertEqual(m.group(3), b)
            if m.group(4):
                self.assertMatch(r'^[\w ]*$', m.group(4))

    def test_basic_dot_files(self):
        vertices = tuple('abcdefgh')
        all_edges = tuple(itertools.combinations(vertices, 2))
        line_edges = list(zip(vertices[1:], vertices[:-1]))
        ring_edges = line_edges + [(vertices[0], vertices[-1])]
        no_edges = []
        # even join to even numbers, odd to odd
        disjoint_edges = [(a, b) for a, b in all_edges if
                          ord(a) ^ ord(b) == 0]

        for name, edges in (('all', all_edges),
                            ('line', line_edges),
                            ('ring', ring_edges),
                            ('no', no_edges),
                            ('disjoint', disjoint_edges)):

            for directed, tag in ((True, "directed"),
                                  (False, "undirected")):
                title = "%s %s" % (name, tag)

                g = graph.dot_graph(vertices, edges,
                                    directed=directed,
                                    title=title)
                lines = g.split('\n')
                self.assertHeader(lines, title, directed)
                self.assertVertices(lines[7:], vertices)
                self.assertEdges(lines[len(vertices) + 7:], edges, directed)


class DistanceTests(samba.tests.TestCase):

    def setUp(self):
        super(DistanceTests, self).setUp()
        # a sorted list of colour set names.
        self.sorted_colour_sets = sorted(
            graph.COLOUR_SETS,
            # return '' for None, so it's sortable.
            key=lambda name: name or '')

    def test_simple_distance(self):
        edges = [('ant', 'bat'),
                 ('cat', 'dog'),
                 ('ant', 'elephant'),
                 ('elephant', 'dog'),
                 ('bat', 'dog'),
                 ('frog', 'elephant'),
                 ('frog', 'cat'),
                 ('bat', 'elephant'),
                 ('elephant', 'cat'),
                 ('cat', 'ant'),
                 ('cat', 'dog')]

        expected = {
                "utf8 True, colour None": '''
                 destination
         ╭────── ant
         │╭───── bat
         ││╭──── cat
         │││╭─── dog
         ││││╭── elephant
  source │││││╭─ frog
     ant ·1221-
     bat 3·211-
     cat 12·12-
     dog ---·--
elephant 2311·-
    frog 23121·''',
                'utf8 True, colour ansi': '''
                 [4mdestination[0m
         [0m[37m╭────── ant[0m
         [37m│[0m[1;30m╭───── bat[0m
         [37m│[1;30m│[0m[37m╭──── cat[0m
         [37m│[1;30m│[37m│[0m[1;30m╭─── dog[0m
         [37m│[1;30m│[37m│[1;30m│[0m[37m╭── elephant[0m
  [4msource[0m [37m│[1;30m│[37m│[1;30m│[37m│[0m[1;30m╭─ frog[0m
[37m     ant[0m [0m[37m·[0m[1;32m1[0m[33m2[0m[33m2[0m[1;32m1[0m[1;31m-[0m
[1;30m     bat[0m [33m3[0m[0m[1;30m·[0m[33m2[0m[1;32m1[0m[1;32m1[0m[1;31m-[0m
[37m     cat[0m [1;32m1[0m[33m2[0m[0m[37m·[0m[1;32m1[0m[33m2[0m[1;31m-[0m
[1;30m     dog[0m [1;31m-[1;31m-[1;31m-[0m[1;30m·[0m[1;31m-[1;31m-[0m
[37melephant[0m [33m2[0m[33m3[0m[1;32m1[0m[1;32m1[0m[0m[37m·[0m[1;31m-[0m
[1;30m    frog[0m [33m2[0m[33m3[0m[1;32m1[0m[33m2[0m[1;32m1[0m[0m[1;30m·[0m[0m
                ''',
                'utf8 True, colour ansi-heatmap': '''
                 [4mdestination[0m
         [0m[37m╭────── ant[0m
         [37m│[0m[1;30m╭───── bat[0m
         [37m│[1;30m│[0m[37m╭──── cat[0m
         [37m│[1;30m│[37m│[0m[1;30m╭─── dog[0m
         [37m│[1;30m│[37m│[1;30m│[0m[37m╭── elephant[0m
  [4msource[0m [37m│[1;30m│[37m│[1;30m│[37m│[0m[1;30m╭─ frog[0m
[37m     ant[0m [0m[37m·[0m[1;42m1[0m[43m2[0m[43m2[0m[1;42m1[0m[1;41m-[0m
[1;30m     bat[0m [43m3[0m[0m[1;30m·[0m[43m2[0m[1;42m1[0m[1;42m1[0m[1;41m-[0m
[37m     cat[0m [1;42m1[0m[43m2[0m[0m[37m·[0m[1;42m1[0m[43m2[0m[1;41m-[0m
[1;30m     dog[0m [1;41m-[1;41m-[1;41m-[0m[1;30m·[0m[1;41m-[1;41m-[0m
[37melephant[0m [43m2[0m[43m3[0m[1;42m1[0m[1;42m1[0m[0m[37m·[0m[1;41m-[0m
[1;30m    frog[0m [43m2[0m[43m3[0m[1;42m1[0m[43m2[0m[1;42m1[0m[0m[1;30m·[0m[0m
                ''',
                'utf8 True, colour xterm-256color': '''
                 [4mdestination[0m
         [0m[38;5;39m╭────── ant[0m
         [38;5;39m│[0m[38;5;45m╭───── bat[0m
         [38;5;39m│[38;5;45m│[0m[38;5;39m╭──── cat[0m
         [38;5;39m│[38;5;45m│[38;5;39m│[0m[38;5;45m╭─── dog[0m
         [38;5;39m│[38;5;45m│[38;5;39m│[38;5;45m│[0m[38;5;39m╭── elephant[0m
  [4msource[0m [38;5;39m│[38;5;45m│[38;5;39m│[38;5;45m│[38;5;39m│[0m[38;5;45m╭─ frog[0m
[38;5;39m     ant[0m [0m[38;5;39m·[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;214m2[0m[38;5;112m1[0m[48;5;124m-[0m
[38;5;45m     bat[0m [38;5;208m3[0m[0m[38;5;45m·[0m[38;5;214m2[0m[38;5;112m1[0m[38;5;112m1[0m[48;5;124m-[0m
[38;5;39m     cat[0m [38;5;112m1[0m[38;5;214m2[0m[0m[38;5;39m·[0m[38;5;112m1[0m[38;5;214m2[0m[48;5;124m-[0m
[38;5;45m     dog[0m [48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;45m·[0m[48;5;124m-[48;5;124m-[0m
[38;5;39melephant[0m [38;5;214m2[0m[38;5;208m3[0m[38;5;112m1[0m[38;5;112m1[0m[0m[38;5;39m·[0m[48;5;124m-[0m
[38;5;45m    frog[0m [38;5;214m2[0m[38;5;208m3[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;112m1[0m[0m[38;5;45m·[0m[0m
                ''',
            'utf8 True, colour xterm-256color-heatmap': '''
                 [4mdestination[0m
         [0m[38;5;171m╭────── ant[0m
         [38;5;171m│[0m[38;5;207m╭───── bat[0m
         [38;5;171m│[38;5;207m│[0m[38;5;171m╭──── cat[0m
         [38;5;171m│[38;5;207m│[38;5;171m│[0m[38;5;207m╭─── dog[0m
         [38;5;171m│[38;5;207m│[38;5;171m│[38;5;207m│[0m[38;5;171m╭── elephant[0m
  [4msource[0m [38;5;171m│[38;5;207m│[38;5;171m│[38;5;207m│[38;5;171m│[0m[38;5;207m╭─ frog[0m
[38;5;171m     ant[0m [0m[38;5;171m·[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;214m2[0m[48;5;112m1[0m[48;5;124m-[0m
[38;5;207m     bat[0m [48;5;208m3[0m[0m[38;5;207m·[0m[48;5;214m2[0m[48;5;112m1[0m[48;5;112m1[0m[48;5;124m-[0m
[38;5;171m     cat[0m [48;5;112m1[0m[48;5;214m2[0m[0m[38;5;171m·[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;124m-[0m
[38;5;207m     dog[0m [48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;207m·[0m[48;5;124m-[48;5;124m-[0m
[38;5;171melephant[0m [48;5;214m2[0m[48;5;208m3[0m[48;5;112m1[0m[48;5;112m1[0m[0m[38;5;171m·[0m[48;5;124m-[0m
[38;5;207m    frog[0m [48;5;214m2[0m[48;5;208m3[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;112m1[0m[0m[38;5;207m·[0m[0m
''',
            'utf8 False, colour None': '''
                 destination
         ,------ ant
         |,----- bat
         ||,---- cat
         |||,--- dog
         ||||,-- elephant
  source |||||,- frog
     ant 01221-
     bat 30211-
     cat 12012-
     dog ---0--
elephant 23110-
    frog 231210
''',
            'utf8 False, colour ansi': '''
                 [4mdestination[0m
         [0m[37m,------ ant[0m
         [37m|[0m[1;30m,----- bat[0m
         [37m|[1;30m|[0m[37m,---- cat[0m
         [37m|[1;30m|[37m|[0m[1;30m,--- dog[0m
         [37m|[1;30m|[37m|[1;30m|[0m[37m,-- elephant[0m
  [4msource[0m [37m|[1;30m|[37m|[1;30m|[37m|[0m[1;30m,- frog[0m
[37m     ant[0m [0m[37m0[0m[1;32m1[0m[33m2[0m[33m2[0m[1;32m1[0m[1;31m-[0m
[1;30m     bat[0m [33m3[0m[0m[1;30m0[0m[33m2[0m[1;32m1[0m[1;32m1[0m[1;31m-[0m
[37m     cat[0m [1;32m1[0m[33m2[0m[0m[37m0[0m[1;32m1[0m[33m2[0m[1;31m-[0m
[1;30m     dog[0m [1;31m-[1;31m-[1;31m-[0m[1;30m0[0m[1;31m-[1;31m-[0m
[37melephant[0m [33m2[0m[33m3[0m[1;32m1[0m[1;32m1[0m[0m[37m0[0m[1;31m-[0m
[1;30m    frog[0m [33m2[0m[33m3[0m[1;32m1[0m[33m2[0m[1;32m1[0m[0m[1;30m0[0m[0m
''',
            'utf8 False, colour ansi-heatmap': '''
                 [4mdestination[0m
         [0m[37m,------ ant[0m
         [37m|[0m[1;30m,----- bat[0m
         [37m|[1;30m|[0m[37m,---- cat[0m
         [37m|[1;30m|[37m|[0m[1;30m,--- dog[0m
         [37m|[1;30m|[37m|[1;30m|[0m[37m,-- elephant[0m
  [4msource[0m [37m|[1;30m|[37m|[1;30m|[37m|[0m[1;30m,- frog[0m
[37m     ant[0m [0m[37m0[0m[1;42m1[0m[43m2[0m[43m2[0m[1;42m1[0m[1;41m-[0m
[1;30m     bat[0m [43m3[0m[0m[1;30m0[0m[43m2[0m[1;42m1[0m[1;42m1[0m[1;41m-[0m
[37m     cat[0m [1;42m1[0m[43m2[0m[0m[37m0[0m[1;42m1[0m[43m2[0m[1;41m-[0m
[1;30m     dog[0m [1;41m-[1;41m-[1;41m-[0m[1;30m0[0m[1;41m-[1;41m-[0m
[37melephant[0m [43m2[0m[43m3[0m[1;42m1[0m[1;42m1[0m[0m[37m0[0m[1;41m-[0m
[1;30m    frog[0m [43m2[0m[43m3[0m[1;42m1[0m[43m2[0m[1;42m1[0m[0m[1;30m0[0m[0m
''',
            'utf8 False, colour xterm-256color': '''
                 [4mdestination[0m
         [0m[38;5;39m,------ ant[0m
         [38;5;39m|[0m[38;5;45m,----- bat[0m
         [38;5;39m|[38;5;45m|[0m[38;5;39m,---- cat[0m
         [38;5;39m|[38;5;45m|[38;5;39m|[0m[38;5;45m,--- dog[0m
         [38;5;39m|[38;5;45m|[38;5;39m|[38;5;45m|[0m[38;5;39m,-- elephant[0m
  [4msource[0m [38;5;39m|[38;5;45m|[38;5;39m|[38;5;45m|[38;5;39m|[0m[38;5;45m,- frog[0m
[38;5;39m     ant[0m [0m[38;5;39m0[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;214m2[0m[38;5;112m1[0m[48;5;124m-[0m
[38;5;45m     bat[0m [38;5;208m3[0m[0m[38;5;45m0[0m[38;5;214m2[0m[38;5;112m1[0m[38;5;112m1[0m[48;5;124m-[0m
[38;5;39m     cat[0m [38;5;112m1[0m[38;5;214m2[0m[0m[38;5;39m0[0m[38;5;112m1[0m[38;5;214m2[0m[48;5;124m-[0m
[38;5;45m     dog[0m [48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;45m0[0m[48;5;124m-[48;5;124m-[0m
[38;5;39melephant[0m [38;5;214m2[0m[38;5;208m3[0m[38;5;112m1[0m[38;5;112m1[0m[0m[38;5;39m0[0m[48;5;124m-[0m
[38;5;45m    frog[0m [38;5;214m2[0m[38;5;208m3[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;112m1[0m[0m[38;5;45m0[0m[0m
''',
            'utf8 False, colour xterm-256color-heatmap': '''
                 [4mdestination[0m
         [0m[38;5;171m,------ ant[0m
         [38;5;171m|[0m[38;5;207m,----- bat[0m
         [38;5;171m|[38;5;207m|[0m[38;5;171m,---- cat[0m
         [38;5;171m|[38;5;207m|[38;5;171m|[0m[38;5;207m,--- dog[0m
         [38;5;171m|[38;5;207m|[38;5;171m|[38;5;207m|[0m[38;5;171m,-- elephant[0m
  [4msource[0m [38;5;171m|[38;5;207m|[38;5;171m|[38;5;207m|[38;5;171m|[0m[38;5;207m,- frog[0m
[38;5;171m     ant[0m [0m[38;5;171m0[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;214m2[0m[48;5;112m1[0m[48;5;124m-[0m
[38;5;207m     bat[0m [48;5;208m3[0m[0m[38;5;207m0[0m[48;5;214m2[0m[48;5;112m1[0m[48;5;112m1[0m[48;5;124m-[0m
[38;5;171m     cat[0m [48;5;112m1[0m[48;5;214m2[0m[0m[38;5;171m0[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;124m-[0m
[38;5;207m     dog[0m [48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;207m0[0m[48;5;124m-[48;5;124m-[0m
[38;5;171melephant[0m [48;5;214m2[0m[48;5;208m3[0m[48;5;112m1[0m[48;5;112m1[0m[0m[38;5;171m0[0m[48;5;124m-[0m
[38;5;207m    frog[0m [48;5;214m2[0m[48;5;208m3[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;112m1[0m[0m[38;5;207m0[0m[0m
'''
        }
        for utf8 in (True, False):
            for colour in self.sorted_colour_sets:
                k = 'utf8 %s, colour %s' % (utf8, colour)
                s = graph.distance_matrix(None, edges, utf8=utf8,
                                          colour=colour)
                self.assertStringsEqual(s, expected[k], strip=True,
                                        msg='Wrong output: %s\n\n%s' % (k, s))

    def test_simple_distance2(self):
        edges = [('ant', 'bat'),
                 ('cat', 'bat'),
                 ('bat', 'ant'),
                 ('ant', 'cat')]
        expected = {
            'utf8 True, colour None': '''
            destination
       ╭─── ant
       │╭── bat
source ││╭─ cat
   ant ·11
   bat 1·2
   cat 21·
            ''',
            'utf8 True, colour ansi': '''
            [4mdestination[0m
       [0m[37m╭─── ant[0m
       [37m│[0m[1;30m╭── bat[0m
[4msource[0m [37m│[1;30m│[0m[37m╭─ cat[0m
[37m   ant[0m [0m[37m·[0m[1;32m1[0m[1;32m1[0m[0m
[1;30m   bat[0m [1;32m1[0m[0m[1;30m·[0m[33m2[0m[0m
[37m   cat[0m [33m2[0m[1;32m1[0m[0m[37m·[0m[0m
            ''',
            'utf8 True, colour ansi-heatmap': '''
            [4mdestination[0m
       [0m[37m╭─── ant[0m
       [37m│[0m[1;30m╭── bat[0m
[4msource[0m [37m│[1;30m│[0m[37m╭─ cat[0m
[37m   ant[0m [0m[37m·[0m[1;42m1[0m[1;42m1[0m[0m
[1;30m   bat[0m [1;42m1[0m[0m[1;30m·[0m[43m2[0m[0m
[37m   cat[0m [43m2[0m[1;42m1[0m[0m[37m·[0m[0m
        ''',
            'utf8 True, colour xterm-256color': '''
            [4mdestination[0m
       [0m[38;5;39m╭─── ant[0m
       [38;5;39m│[0m[38;5;45m╭── bat[0m
[4msource[0m [38;5;39m│[38;5;45m│[0m[38;5;39m╭─ cat[0m
[38;5;39m   ant[0m [0m[38;5;39m·[0m[38;5;112m1[0m[38;5;112m1[0m[0m
[38;5;45m   bat[0m [38;5;112m1[0m[0m[38;5;45m·[0m[38;5;208m2[0m[0m
[38;5;39m   cat[0m [38;5;208m2[0m[38;5;112m1[0m[0m[38;5;39m·[0m[0m
''',
            'utf8 True, colour xterm-256color-heatmap': '''
            [4mdestination[0m
       [0m[38;5;171m╭─── ant[0m
       [38;5;171m│[0m[38;5;207m╭── bat[0m
[4msource[0m [38;5;171m│[38;5;207m│[0m[38;5;171m╭─ cat[0m
[38;5;171m   ant[0m [0m[38;5;171m·[0m[48;5;112m1[0m[48;5;112m1[0m[0m
[38;5;207m   bat[0m [48;5;112m1[0m[0m[38;5;207m·[0m[48;5;208m2[0m[0m
[38;5;171m   cat[0m [48;5;208m2[0m[48;5;112m1[0m[0m[38;5;171m·[0m[0m
''',
            'utf8 False, colour None': '''
            destination
       ,--- ant
       |,-- bat
source ||,- cat
   ant 011
   bat 102
   cat 210
''',
            'utf8 False, colour ansi': '''
            [4mdestination[0m
       [0m[37m,--- ant[0m
       [37m|[0m[1;30m,-- bat[0m
[4msource[0m [37m|[1;30m|[0m[37m,- cat[0m
[37m   ant[0m [0m[37m0[0m[1;32m1[0m[1;32m1[0m[0m
[1;30m   bat[0m [1;32m1[0m[0m[1;30m0[0m[33m2[0m[0m
[37m   cat[0m [33m2[0m[1;32m1[0m[0m[37m0[0m[0m
''',
            'utf8 False, colour ansi-heatmap': '''
            [4mdestination[0m
       [0m[37m,--- ant[0m
       [37m|[0m[1;30m,-- bat[0m
[4msource[0m [37m|[1;30m|[0m[37m,- cat[0m
[37m   ant[0m [0m[37m0[0m[1;42m1[0m[1;42m1[0m[0m
[1;30m   bat[0m [1;42m1[0m[0m[1;30m0[0m[43m2[0m[0m
[37m   cat[0m [43m2[0m[1;42m1[0m[0m[37m0[0m[0m
''',
            'utf8 False, colour xterm-256color': '''
            [4mdestination[0m
       [0m[38;5;39m,--- ant[0m
       [38;5;39m|[0m[38;5;45m,-- bat[0m
[4msource[0m [38;5;39m|[38;5;45m|[0m[38;5;39m,- cat[0m
[38;5;39m   ant[0m [0m[38;5;39m0[0m[38;5;112m1[0m[38;5;112m1[0m[0m
[38;5;45m   bat[0m [38;5;112m1[0m[0m[38;5;45m0[0m[38;5;208m2[0m[0m
[38;5;39m   cat[0m [38;5;208m2[0m[38;5;112m1[0m[0m[38;5;39m0[0m[0m
''',
            'utf8 False, colour xterm-256color-heatmap': '''
            [4mdestination[0m
       [0m[38;5;171m,--- ant[0m
       [38;5;171m|[0m[38;5;207m,-- bat[0m
[4msource[0m [38;5;171m|[38;5;207m|[0m[38;5;171m,- cat[0m
[38;5;171m   ant[0m [0m[38;5;171m0[0m[48;5;112m1[0m[48;5;112m1[0m[0m
[38;5;207m   bat[0m [48;5;112m1[0m[0m[38;5;207m0[0m[48;5;208m2[0m[0m
[38;5;171m   cat[0m [48;5;208m2[0m[48;5;112m1[0m[0m[38;5;171m0[0m[0m
'''
        }
        for utf8 in (True, False):
            for colour in self.sorted_colour_sets:
                k = 'utf8 %s, colour %s' % (utf8, colour)
                s = graph.distance_matrix(None, edges, utf8=utf8,
                                          colour=colour)
                self.assertStringsEqual(s, expected[k], strip=True,
                                        msg='Wrong output: %s\n\n%s' % (k, s))

    def test_simple_distance3(self):
        edges = [('ant', 'bat'),
                 ('bat', 'cat'),
                 ('cat', 'dog'),
                 ('dog', 'ant'),
                 ('dog', 'eel')]
        expected = {
            'utf8 True, colour None': '''
              destination
       ╭───── ant
       │╭──── bat
       ││╭─── cat
       │││╭── dog
source ││││╭─ eel
   ant ·1234
   bat 3·123
   cat 23·12
   dog 123·1
   eel ----·
''',
            'utf8 True, colour ansi': '''
              [4mdestination[0m
       [0m[37m╭───── ant[0m
       [37m│[0m[1;30m╭──── bat[0m
       [37m│[1;30m│[0m[37m╭─── cat[0m
       [37m│[1;30m│[37m│[0m[1;30m╭── dog[0m
[4msource[0m [37m│[1;30m│[37m│[1;30m│[0m[37m╭─ eel[0m
[37m   ant[0m [0m[37m·[0m[1;32m1[0m[33m2[0m[33m3[0m[33m4[0m[0m
[1;30m   bat[0m [33m3[0m[0m[1;30m·[0m[1;32m1[0m[33m2[0m[33m3[0m[0m
[37m   cat[0m [33m2[0m[33m3[0m[0m[37m·[0m[1;32m1[0m[33m2[0m[0m
[1;30m   dog[0m [1;32m1[0m[33m2[0m[33m3[0m[0m[1;30m·[0m[1;32m1[0m[0m
[37m   eel[0m [1;31m-[1;31m-[1;31m-[1;31m-[0m[37m·[0m[0m
''',
            'utf8 True, colour ansi-heatmap': '''
              [4mdestination[0m
       [0m[37m╭───── ant[0m
       [37m│[0m[1;30m╭──── bat[0m
       [37m│[1;30m│[0m[37m╭─── cat[0m
       [37m│[1;30m│[37m│[0m[1;30m╭── dog[0m
[4msource[0m [37m│[1;30m│[37m│[1;30m│[0m[37m╭─ eel[0m
[37m   ant[0m [0m[37m·[0m[1;42m1[0m[43m2[0m[43m3[0m[43m4[0m[0m
[1;30m   bat[0m [43m3[0m[0m[1;30m·[0m[1;42m1[0m[43m2[0m[43m3[0m[0m
[37m   cat[0m [43m2[0m[43m3[0m[0m[37m·[0m[1;42m1[0m[43m2[0m[0m
[1;30m   dog[0m [1;42m1[0m[43m2[0m[43m3[0m[0m[1;30m·[0m[1;42m1[0m[0m
[37m   eel[0m [1;41m-[1;41m-[1;41m-[1;41m-[0m[37m·[0m[0m
''',
            'utf8 True, colour xterm-256color': '''
              [4mdestination[0m
       [0m[38;5;39m╭───── ant[0m
       [38;5;39m│[0m[38;5;45m╭──── bat[0m
       [38;5;39m│[38;5;45m│[0m[38;5;39m╭─── cat[0m
       [38;5;39m│[38;5;45m│[38;5;39m│[0m[38;5;45m╭── dog[0m
[4msource[0m [38;5;39m│[38;5;45m│[38;5;39m│[38;5;45m│[0m[38;5;39m╭─ eel[0m
[38;5;39m   ant[0m [0m[38;5;39m·[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;208m3[0m[38;5;208m4[0m[0m
[38;5;45m   bat[0m [38;5;208m3[0m[0m[38;5;45m·[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;208m3[0m[0m
[38;5;39m   cat[0m [38;5;214m2[0m[38;5;208m3[0m[0m[38;5;39m·[0m[38;5;112m1[0m[38;5;214m2[0m[0m
[38;5;45m   dog[0m [38;5;112m1[0m[38;5;214m2[0m[38;5;208m3[0m[0m[38;5;45m·[0m[38;5;112m1[0m[0m
[38;5;39m   eel[0m [48;5;124m-[48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;39m·[0m[0m
''',
            'utf8 True, colour xterm-256color-heatmap': '''
              [4mdestination[0m
       [0m[38;5;171m╭───── ant[0m
       [38;5;171m│[0m[38;5;207m╭──── bat[0m
       [38;5;171m│[38;5;207m│[0m[38;5;171m╭─── cat[0m
       [38;5;171m│[38;5;207m│[38;5;171m│[0m[38;5;207m╭── dog[0m
[4msource[0m [38;5;171m│[38;5;207m│[38;5;171m│[38;5;207m│[0m[38;5;171m╭─ eel[0m
[38;5;171m   ant[0m [0m[38;5;171m·[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;208m3[0m[48;5;208m4[0m[0m
[38;5;207m   bat[0m [48;5;208m3[0m[0m[38;5;207m·[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;208m3[0m[0m
[38;5;171m   cat[0m [48;5;214m2[0m[48;5;208m3[0m[0m[38;5;171m·[0m[48;5;112m1[0m[48;5;214m2[0m[0m
[38;5;207m   dog[0m [48;5;112m1[0m[48;5;214m2[0m[48;5;208m3[0m[0m[38;5;207m·[0m[48;5;112m1[0m[0m
[38;5;171m   eel[0m [48;5;124m-[48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;171m·[0m[0m
''',
            'utf8 False, colour None': '''
              destination
       ,----- ant
       |,---- bat
       ||,--- cat
       |||,-- dog
source ||||,- eel
   ant 01234
   bat 30123
   cat 23012
   dog 12301
   eel ----0
''',
            'utf8 False, colour ansi': '''
              [4mdestination[0m
       [0m[37m,----- ant[0m
       [37m|[0m[1;30m,---- bat[0m
       [37m|[1;30m|[0m[37m,--- cat[0m
       [37m|[1;30m|[37m|[0m[1;30m,-- dog[0m
[4msource[0m [37m|[1;30m|[37m|[1;30m|[0m[37m,- eel[0m
[37m   ant[0m [0m[37m0[0m[1;32m1[0m[33m2[0m[33m3[0m[33m4[0m[0m
[1;30m   bat[0m [33m3[0m[0m[1;30m0[0m[1;32m1[0m[33m2[0m[33m3[0m[0m
[37m   cat[0m [33m2[0m[33m3[0m[0m[37m0[0m[1;32m1[0m[33m2[0m[0m
[1;30m   dog[0m [1;32m1[0m[33m2[0m[33m3[0m[0m[1;30m0[0m[1;32m1[0m[0m
[37m   eel[0m [1;31m-[1;31m-[1;31m-[1;31m-[0m[37m0[0m[0m
''',
            'utf8 False, colour ansi-heatmap': '''
              [4mdestination[0m
       [0m[37m,----- ant[0m
       [37m|[0m[1;30m,---- bat[0m
       [37m|[1;30m|[0m[37m,--- cat[0m
       [37m|[1;30m|[37m|[0m[1;30m,-- dog[0m
[4msource[0m [37m|[1;30m|[37m|[1;30m|[0m[37m,- eel[0m
[37m   ant[0m [0m[37m0[0m[1;42m1[0m[43m2[0m[43m3[0m[43m4[0m[0m
[1;30m   bat[0m [43m3[0m[0m[1;30m0[0m[1;42m1[0m[43m2[0m[43m3[0m[0m
[37m   cat[0m [43m2[0m[43m3[0m[0m[37m0[0m[1;42m1[0m[43m2[0m[0m
[1;30m   dog[0m [1;42m1[0m[43m2[0m[43m3[0m[0m[1;30m0[0m[1;42m1[0m[0m
[37m   eel[0m [1;41m-[1;41m-[1;41m-[1;41m-[0m[37m0[0m[0m
''',
            'utf8 False, colour xterm-256color':
            '''              [4mdestination[0m
       [0m[38;5;39m,----- ant[0m
       [38;5;39m|[0m[38;5;45m,---- bat[0m
       [38;5;39m|[38;5;45m|[0m[38;5;39m,--- cat[0m
       [38;5;39m|[38;5;45m|[38;5;39m|[0m[38;5;45m,-- dog[0m
[4msource[0m [38;5;39m|[38;5;45m|[38;5;39m|[38;5;45m|[0m[38;5;39m,- eel[0m
[38;5;39m   ant[0m [0m[38;5;39m0[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;208m3[0m[38;5;208m4[0m[0m
[38;5;45m   bat[0m [38;5;208m3[0m[0m[38;5;45m0[0m[38;5;112m1[0m[38;5;214m2[0m[38;5;208m3[0m[0m
[38;5;39m   cat[0m [38;5;214m2[0m[38;5;208m3[0m[0m[38;5;39m0[0m[38;5;112m1[0m[38;5;214m2[0m[0m
[38;5;45m   dog[0m [38;5;112m1[0m[38;5;214m2[0m[38;5;208m3[0m[0m[38;5;45m0[0m[38;5;112m1[0m[0m
[38;5;39m   eel[0m [48;5;124m-[48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;39m0[0m[0m
''',
            'utf8 False, colour xterm-256color-heatmap': '''
              [4mdestination[0m
       [0m[38;5;171m,----- ant[0m
       [38;5;171m|[0m[38;5;207m,---- bat[0m
       [38;5;171m|[38;5;207m|[0m[38;5;171m,--- cat[0m
       [38;5;171m|[38;5;207m|[38;5;171m|[0m[38;5;207m,-- dog[0m
[4msource[0m [38;5;171m|[38;5;207m|[38;5;171m|[38;5;207m|[0m[38;5;171m,- eel[0m
[38;5;171m   ant[0m [0m[38;5;171m0[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;208m3[0m[48;5;208m4[0m[0m
[38;5;207m   bat[0m [48;5;208m3[0m[0m[38;5;207m0[0m[48;5;112m1[0m[48;5;214m2[0m[48;5;208m3[0m[0m
[38;5;171m   cat[0m [48;5;214m2[0m[48;5;208m3[0m[0m[38;5;171m0[0m[48;5;112m1[0m[48;5;214m2[0m[0m
[38;5;207m   dog[0m [48;5;112m1[0m[48;5;214m2[0m[48;5;208m3[0m[0m[38;5;207m0[0m[48;5;112m1[0m[0m
[38;5;171m   eel[0m [48;5;124m-[48;5;124m-[48;5;124m-[48;5;124m-[0m[38;5;171m0[0m[0m
'''
        }
        for utf8 in (True, False):
            for colour in self.sorted_colour_sets:
                k = 'utf8 %s, colour %s' % (utf8, colour)
                s = graph.distance_matrix(None, edges, utf8=utf8,
                                          colour=colour)
                self.assertStringsEqual(s, expected[k], strip=True,
                                        msg='Wrong output: %s\n\n%s' % (k, s))
