def test_ElectrodeGrid(gtype):
    # Must pass in tuple/list of (rows, cols) for grid shape:
    with pytest.raises(TypeError):
        ElectrodeGrid("badinstantiation")
    with pytest.raises(TypeError):
        ElectrodeGrid(OrderedDict({'badinstantiation': 0}))
    with pytest.raises(ValueError):
        ElectrodeGrid([0], 10)
    with pytest.raises(ValueError):
        ElectrodeGrid([1, 2, 3], 10)
    with pytest.raises(TypeError):
        ElectrodeGrid({'1': 2}, 10)

    # Must pass in valid Electrode type:
    with pytest.raises(TypeError):
        ElectrodeGrid((2, 3), 10, type=gtype, etype=ElectrodeArray)
    with pytest.raises(TypeError):
        ElectrodeGrid((2, 3), 10, type=gtype, etype="foo")

    # Must pass in valid Orientation value:
    with pytest.raises(ValueError):
        ElectrodeGrid((2, 3), 10, type=gtype, orientation="foo")
    with pytest.raises(TypeError):
        ElectrodeGrid((2, 3), 10, type=gtype, orientation=False)

    # Must pass in radius `r` for grid of DiskElectrode objects:
    gshape = (4, 5)
    spacing = 100
    grid = ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode,
                         r=13)
    for e in grid.values():
        npt.assert_almost_equal(e.r, 13)
    grid = ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode,
                         r=np.arange(1, np.prod(gshape) + 1))
    for i, e in enumerate(grid.values()):
        npt.assert_almost_equal(e.r, i + 1)
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode)
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode,
                      radius=10)
    # Number of radii must match number of electrodes
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode,
                      radius=[2, 13, 14])
    # Only DiskElectrode needs r, not PointSource:
    with pytest.raises(TypeError):
        ElectrodeGrid(gshape, spacing, type=gtype, r=10)

    # Must pass in radius `r` for grid of DiskElectrode objects:
    gshape = (4, 5)
    spacing = 100
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode)
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode,
                      radius=10)
    # Number of radii must match number of electrodes
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode,
                      radius=[2, 13])

    # Must pass in valid grid type:
    with pytest.raises(TypeError):
        ElectrodeGrid(gshape, spacing, type=DiskElectrode)
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, type='unknown')

    # Verify spacing is correct:
    grid = ElectrodeGrid(gshape, spacing, type=gtype, etype=DiskElectrode,
                         r=30)
    npt.assert_almost_equal(np.sqrt((grid['A1'].x - grid['A2'].x) ** 2 +
                                    (grid['A1'].y - grid['A2'].y) ** 2),
                            spacing)
    npt.assert_almost_equal(np.sqrt((grid['A1'].x - grid['B1'].x) ** 2 +
                                    (grid['A1'].y - grid['B1'].y) ** 2),
                            spacing)
    grid = ElectrodeGrid(gshape, spacing, type=gtype, orientation='vertical',
                         etype=DiskElectrode, r=30)
    npt.assert_almost_equal(np.sqrt((grid['B1'].x - grid['B2'].x) ** 2 +
                                    (grid['B1'].y - grid['B2'].y) ** 2),
                            spacing)
    npt.assert_almost_equal(np.sqrt((grid['A1'].x - grid['B1'].x) ** 2 +
                                    (grid['A1'].y - grid['B1'].y) ** 2),
                            spacing)

    # A valid 2x5 grid centered at (0, 500):
    x, y = 0, 500
    radius = 30
    egrid = ElectrodeGrid(gshape, spacing, x=x, y=y, type='rect',
                          etype=DiskElectrode, r=radius)
    npt.assert_equal(egrid.shape, gshape)
    npt.assert_equal(egrid.n_electrodes, np.prod(gshape))
    # Make sure different electrodes have different coordinates:
    npt.assert_equal(len(np.unique([e.x for e in egrid.values()])), gshape[1])
    npt.assert_equal(len(np.unique([e.y for e in egrid.values()])), gshape[0])
    # Make sure the average of all x-coordinates == x:
    # (Note: egrid has all electrodes in a dictionary, with (name, object)
    # as (key, value) pairs. You can get the electrode names by iterating over
    # egrid.keys(). You can get the electrode objects by iterating over
    # egrid.values().)
    npt.assert_almost_equal(np.mean([e.x for e in egrid.values()]), x)
    # Same for y:
    npt.assert_almost_equal(np.mean([e.y for e in egrid.values()]), y)

    # Test whether egrid.z is set correctly, when z is a constant:
    z = 12
    egrid = ElectrodeGrid(gshape, spacing, z=z, type=gtype,
                          etype=DiskElectrode, r=radius)
    for i in egrid.values():
        npt.assert_equal(i.z, z)

    # and when every electrode has a different z:
    z = np.arange(np.prod(gshape))
    egrid = ElectrodeGrid(gshape, spacing, z=z, type=gtype,
                          etype=DiskElectrode, r=radius)
    x = -1
    for i in egrid.values():
        npt.assert_equal(i.z, x + 1)
        x = i.z

    # TODO display a warning when rot > 2pi (meaning it might be in degrees).
    # I think we did this somewhere in the old Argus code

    # TODO test rotation, making sure positive angles rotate CCW
    egrid1 = ElectrodeGrid((2, 2), spacing, type=gtype, etype=DiskElectrode,
                           r=radius)
    egrid2 = ElectrodeGrid((2, 2), spacing, rot=np.deg2rad(10), type=gtype,
                           etype=DiskElectrode, r=radius)
    npt.assert_equal(egrid1["A1"].x < egrid2["A1"].x, True)
    npt.assert_equal(egrid1["A1"].y > egrid2["A1"].y, True)
    npt.assert_equal(egrid1["B2"].x > egrid2["B2"].x, True)
    npt.assert_equal(egrid1["B2"].y < egrid2["B2"].y, True)

    # Smallest possible grid:
    egrid = ElectrodeGrid((1, 1), spacing, type=gtype, etype=DiskElectrode,
                          r=radius)
    npt.assert_equal(egrid.shape, (1, 1))
    npt.assert_equal(egrid.n_electrodes, 1)

    # Grid has same size as 'names':
    egrid = ElectrodeGrid((1, 2), spacing, type=gtype, names=('C1', '4'))
    npt.assert_equal(egrid[0, 0], egrid['C1'])
    npt.assert_equal(egrid[0, 1], egrid['4'])

    # Can't have a zero-sized grid:
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid((0, 0), spacing, type=gtype)
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid((5, 0), spacing, type=gtype)

    # Invalid naming conventions:
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=[1])
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=[])
    with pytest.raises(TypeError):
        egrid = ElectrodeGrid(gshape, spacing, type=gtype, names={1})
    with pytest.raises(TypeError):
        egrid = ElectrodeGrid(gshape, spacing, type=gtype, names={})
    with pytest.raises(TypeError):
        ElectrodeGrid(gshape, spacing, names={'1': 2})
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, names=('A', '1', 'A'))
    with pytest.raises(TypeError):
        ElectrodeGrid(gshape, spacing, names=(1, 'A'))
    with pytest.raises(TypeError):
        ElectrodeGrid(gshape, spacing, names=('A', 1))
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, names=('A', '~'))
    with pytest.raises(ValueError):
        ElectrodeGrid(gshape, spacing, names=('~', 'A'))

    # Test all naming conventions:
    gshape = (2, 3)
    egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=('A', '1'))
    # print([e for e in egrid.keys()])
    npt.assert_equal([e for e in egrid.keys()],
                     ['A1', 'A2', 'A3', 'B1', 'B2', 'B3'])
    egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=('1', 'A'))
    # print([e for e in egrid.keys()])
    # egrid = ElectrodeGrid(shape, names=('A', '1'))
    npt.assert_equal([e for e in egrid.keys()],
                     ['A1', 'B1', 'C1', 'A2', 'B2', 'C2'])

    egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=('1', '1'))
    # print([e for e in egrid.keys()])
    npt.assert_equal([e for e in egrid.keys()],
                     ['11', '12', '13', '21', '22', '23'])
    egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=('A', 'A'))
    # print([e for e in egrid.keys()])
    npt.assert_equal([e for e in egrid.keys()],
                     ['AA', 'AB', 'AC', 'BA', 'BB', 'BC'])

    # Still starts at A:
    egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=('B', '1'))
    npt.assert_equal([e for e in egrid.keys()],
                     ['A1', 'A2', 'A3', 'B1', 'B2', 'B3'])
    egrid = ElectrodeGrid(gshape, spacing, type=gtype, names=('A', '2'))
    npt.assert_equal([e for e in egrid.keys()],
                     ['A1', 'A2', 'A3', 'B1', 'B2', 'B3'])

    # test unique names
    egrid = ElectrodeGrid(gshape, spacing, type=gtype,
                          names=['53', '18', '00', '81', '11', '12'])
    npt.assert_equal([e for e in egrid.keys()],
                     ['53', '18', '00', '81', '11', '12'])

    # Slots:
    npt.assert_equal(hasattr(egrid, '__slots__'), True)
    npt.assert_equal(hasattr(egrid, '__dict__'), False)
def test_ElectrodeGrid():
    # Must pass in tuple/list of (rows, cols) for grid shape:
    with pytest.raises(TypeError):
        ElectrodeGrid("badinstantiation")
    with pytest.raises(TypeError):
        ElectrodeGrid(coll.OrderedDict({'badinstantiation': 0}))
    with pytest.raises(ValueError):
        ElectrodeGrid([0])
    with pytest.raises(ValueError):
        ElectrodeGrid([1, 2, 3])
    with pytest.raises(TypeError):
        ElectrodeGrid((2, 3), etype=ElectrodeArray)
    with pytest.raises(TypeError):
        ElectrodeGrid((2, 3), etype="foo")

    # A valid 2x5 grid centered at (0, 500):
    shape = (2, 3)
    x, y = 0, 500
    spacing = 100
    egrid = ElectrodeGrid(shape, x=x, y=y, spacing=spacing)
    npt.assert_equal(egrid.shape, shape)
    npt.assert_equal(egrid.n_electrodes, np.prod(shape))
    npt.assert_equal(egrid.x, x)
    npt.assert_equal(egrid.y, y)
    npt.assert_almost_equal(egrid.spacing, spacing)
    # Make sure different electrodes have different coordinates:
    npt.assert_equal(len(np.unique([e.x for e in egrid.values()])), shape[1])
    npt.assert_equal(len(np.unique([e.y for e in egrid.values()])), shape[0])
    # Make sure the average of all x-coordinates == x:
    # (Note: egrid has all electrodes in a dictionary, with (name, object)
    # as (key, value) pairs. You can get the electrode names by iterating over
    # egrid.keys(). You can get the electrode objects by iterating over
    # egrid.values().)
    npt.assert_almost_equal(np.mean([e.x for e in egrid.values()]), x)
    # Same for y:
    npt.assert_almost_equal(np.mean([e.y for e in egrid.values()]), y)

    # Test whether egrid.z is set correctly, when z is a constant:
    z = 12
    egrid = ElectrodeGrid(shape, z=z)
    npt.assert_equal(egrid.z, z)
    for i in egrid.values():
        npt.assert_equal(i.z, z)

    # and when every electrode has a different z:
    z = np.arange(np.prod(shape))
    egrid = ElectrodeGrid(shape, z=z)
    npt.assert_equal(egrid.z, z)
    x = -1
    for i in egrid.values():
        npt.assert_equal(i.z, x + 1)
        x = i.z

    # TODO display a warning when rot > 2pi (meaning it might be in degrees).
    # I think we did this somewhere in the old Argus code

    # TODO test rotation, making sure positive angles rotate CCW
    egrid1 = ElectrodeGrid(shape=(2, 2))
    egrid2 = ElectrodeGrid(shape=(2, 2), rot=np.deg2rad(10))
    npt.assert_equal(egrid1["A1"].x < egrid2["A1"].x, True)
    npt.assert_equal(egrid1["A1"].y > egrid2["A1"].y, True)
    npt.assert_equal(egrid1["B2"].x > egrid2["B2"].x, True)
    npt.assert_equal(egrid1["B2"].y < egrid2["B2"].y, True)

    # Smallest possible grid:
    egrid = ElectrodeGrid((1, 1))
    npt.assert_equal(egrid.shape, (1, 1))
    npt.assert_equal(egrid.n_electrodes, 1)

    # Can't have a zero-sized grid:
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid((0, 0))
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid((5, 0))

    # Invalid naming conventions:
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid(shape, names=[1])
    with pytest.raises(ValueError):
        egrid = ElectrodeGrid(shape, names=[])
    with pytest.raises(TypeError):
        egrid = ElectrodeGrid(shape, names={1})
    with pytest.raises(TypeError):
        egrid = ElectrodeGrid(shape, names={})

    # Test all naming conventions:
    egrid = ElectrodeGrid(shape, names=('A', '1'))
    # print([e for e in egrid.keys()])
    npt.assert_equal([e for e in egrid.keys()],
                     ['A1', 'A2', 'A3', 'B1', 'B2', 'B3'])
    egrid = ElectrodeGrid(shape, names=('1', 'A'))
    # print([e for e in egrid.keys()])
    # egrid = ElectrodeGrid(shape, names=('A', '1'))
    npt.assert_equal([e for e in egrid.keys()],
                     ['A1', 'B1', 'C1', 'A2', 'B2', 'C2'])

    # egrid = ElectrodeGrid(shape, names=('A', '1'))
    # npt.assert_equal([e for e in egrid.keys()],
    #                  ['A1', 'A1', 'C1', 'A2', 'B2', 'C2'])
    egrid = ElectrodeGrid(shape, names=('1', '1'))
    # print([e for e in egrid.keys()])
    npt.assert_equal([e for e in egrid.keys()],
                     ['11', '12', '13', '21', '22', '23'])
    egrid = ElectrodeGrid(shape, names=('A', 'A'))
    # print([e for e in egrid.keys()])
    npt.assert_equal([e for e in egrid.keys()],
                     ['AA', 'AB', 'AC', 'BA', 'BB', 'BC'])

    # rows and columns start at values other than A or 1
    egrid = ElectrodeGrid(shape, names=('B', '1'))
    npt.assert_equal([e for e in egrid.keys()],
                     ['B1', 'B2', 'B3', 'C1', 'C2', 'C3'])
    egrid = ElectrodeGrid(shape, names=('A', '2'))
    npt.assert_equal([e for e in egrid.keys()],
                     ['A2', 'A3', 'A4', 'B2', 'B3', 'B4'])

    # test unique names
    egrid = ElectrodeGrid(shape, names=['53', '18', '00', '81', '11', '12'])
    npt.assert_equal([e for e in egrid.keys()],
                     ['53', '18', '00', '81', '11', '12'])