from data.logic import _ast_factory, _dimension_factory, _dimension_slice
from spec.mamba import *

with description('_dimension_slice._DimensionSlice'):
  with before.each:
    self.factory = _dimension_factory._DimensionFactory()

  with description('constructor'):
    with it('handles simple input'):
      expect(calling(
          _dimension_slice._DimensionSlice, None, {}
      )).not_to(raise_error)

  with description('resolve'):
    with it('rejects invalid access'):
      empty = _dimension_slice._DimensionSlice(self.factory, {})
      expect(lambda: empty.foo).to(raise_error(KeyError))
      expect(lambda: empty['foo']).to(raise_error(KeyError))

    with it('rejects redundant access'):
      a, = self.factory(name=['andy'])
      expect(lambda: a.andy).to(raise_error(KeyError))
      expect(lambda: a['andy']).to(raise_error(KeyError))

    with it('accepts valid sub-slices'):
      andy, bob = self.factory(name=['andy', 'bob'])
      cherries, dates = self.factory(fruit=['cherries', 'dates'])
      expect(lambda: andy.dates).not_to(raise_error)
      expect(andy.dates).to(be_a(_dimension_slice._DimensionSlice))
      expect(bob.cherries).to(be_a(_dimension_slice._DimensionSlice))
      expect(dates.bob.dimension_constraints()).to(equal({
        'name': 'bob',
        'fruit': 'dates',
      }))
      expect(cherries.andy.dimension_constraints()).to(equal({
        'name': 'andy',
        'fruit': 'cherries',
      }))

    with it('accepts valid array sub-slices'):
      andy, = self.factory(name=['andy'])
      cherries, = self.factory(fruit=['cherries'])
      expect(lambda: andy['cherries']).not_to(raise_error)
      expect(andy['cherries']).to(be_a(_dimension_slice._DimensionSlice))
      expect(andy['cherries'].dimension_constraints()).to(equal({
        'name': 'andy',
        'fruit': 'cherries',
      }))
      expect(cherries['andy'].dimension_constraints()).to(equal({
        'name': 'andy',
        'fruit': 'cherries',
      }))

    with it('accepts slicing dimensions'):
      andy, = self.factory(name=['andy'])
      cherries, = self.factory(fruit=['cherries'])
      expect(lambda: andy.fruit).not_to(raise_error)
      expect(andy.fruit).to(be_a(_dimension_slice._DimensionSlice))
      expect(cherries['name'].dimension_constraints()).to(equal({
        'name': None,
        'fruit': 'cherries',
      }))

  with description('dimension_name'):
    with it('produces fully qualified names'):
      andy, = self.factory(name=['andy'])
      cherries, = self.factory(fruit=['cherries'])
      expect(andy.cherries.dimension_address()).to(
          equal('name["andy"].fruit["cherries"]'))
      expect(cherries.andy.dimension_address()).to(
          equal('name["andy"].fruit["cherries"]'))

    with it('produces properly named sub-slices'):
      name = self.factory(name=['andy'])
      expect(name['andy'].dimension_address()).to(equal('name["andy"]'))

    with it('ignores unconstrained slices'):
      name = self.factory(name=['andy'])
      fruit = self.factory(fruit=['cherries'])
      age = self.factory(age=[10])
      expect(name['andy'].dimension_address()).to(equal('name["andy"]'))
      expect(name['andy'].fruit.dimension_address()).to(equal(
          'name["andy"].fruit[None]'))
      expect(fruit['cherries'].dimension_address()).to(equal(
          'fruit["cherries"]'))

  with description('cache'):
    with it('returns unique slices for new requests'):
      andy, bob = self.factory(name=['andy', 'bob'])
      cherries, dates = self.factory(fruit=['cherries', 'dates'])
      slices = [
        andy.fruit, andy.cherries, andy.dates,
        bob.fruit, bob.cherries, bob.dates,
        cherries.name, cherries.andy, cherries.bob,
        dates.name, dates.andy, dates.bob,
      ]
      seen = set()
      for slice in slices:
        address = id(slice)
        expect(seen).not_to(contain(address))

    with it('returns cached slices for repeated requests'):
      andy, = self.factory(name=['andy'])
      cherries, = self.factory(fruit=['cherries'])
      expect(andy.cherries).to(be(andy['cherries']))
      expect(andy.fruit).to(be(andy['fruit']))
      expect(cherries.andy).to(be(cherries['andy']))
      expect(cherries.name).to(be(cherries['name']))

  with description('operators'):
    with description('eq'):
      with it('accepts two unconstrained slices'):
        andy, = self.factory(name=['andy'])
        cherries, = self.factory(fruit=['cherries'])
        expression = andy == cherries
        expect(expression).to(be_a(_ast_factory.AccumulatingExpr))

      with it('accumulates comparisons'):
        andy, bob = self.factory(name=['andy', 'bob'])
        age = self.factory(age=[10, 11])
        expression = ((andy.age + 1) == bob)
        expect(expression).to(be_a(_ast_factory.AccumulatingExpr))

@@ -1972,32 +1972,527 @@
 es',%0A      %7D))%0A%0A
+    with it('accepts sub-slices on duplicate int values'):%0A      andy, bob = self.factory(name=%5B'andy', 'bob'%5D)%0A      _10, _ = self.factory(age=%5B10, 10%5D)%0A      expect(lambda: andy%5B10%5D).not_to(raise_error)%0A      expect(andy%5B10%5D).to(be_a(_dimension_slice._DimensionSlice))%0A      expect(bob%5B10%5D.dimension_constraints()).to(equal(%7B%0A        'name': 'bob',%0A        'age': 10,%0A      %7D))%0A      expect(_10%5B'andy'%5D.dimension_constraints()).to(equal(%7B%0A        'name': 'andy',%0A        'age': 10,%0A      %7D))%0A%0A
     with it('acc

Add test to verify slicing on a duplicated value is OK.