function comprehensiveTestCase(name, testCase, options) {
    var label = '#' + name;

    if (testCase.label) {
      label += ' (' + testCase.label + ')';
    }

    describe(label, function() {
      var monitor, sequence;

      beforeEach(function() {
        monitor  = createMonitor(testCase.input);
        sequence = Lazy(monitor);
      });

      function getResult() {
        return sequence[name].apply(sequence, testCase.params || []);
      }

      function iterateResult() {
        return iterate(getResult());
      }

      function assertResult() {
        expect(getResult()).toComprise(testCase.result);
      }

      var sequenceTypes = [
        {
          label: 'an ArrayWrapper',
          transform: function() { return sequence; }
        },
        {
          label: 'an ArrayLikeSequence',
          transform: function() { return sequence.map(Lazy.identity); }
        },
        {
          label: 'an ordinary sequence',
          transform: function() { return sequence.filter(alwaysTrue); },
          arrayLike: false
        }
      ];

      Lazy(sequenceTypes).each(function(sequenceType) {
        describe('for ' + sequenceType.label, function() {
          beforeEach(function() {
            sequence = sequenceType.transform();
          });

          it('works as expected', function() {
            assertResult();
          });

          it('is actually lazy', function() {
            getResult();
            expect(monitor.accessCount()).toBe(0);
          });

          it('supports early termination', function() {
            expect(getResult().take(2)).toComprise(testCase.result.slice(0, 2));
          });

          // For something like Lazy([...]).take(N), we only need to access N
          // elements; however, some sequence types may require > N accesses
          // to produce N results. An obvious example is #filter.
          if (!lookupValue('skipAccessCounts', [sequenceType, options])) {
            it('accesses the minimum number of elements from the source', function() {
              var expectedAccessCount = testCase.accessCountForTake2 || 2;

              iterate(getResult().take(2));
              expect(monitor.accessCount()).toEqual(expectedAccessCount);
            });
          }

          it('passes along the index with each element during iteration', function() {
            indexes = getResult().map(function(e, i) { return i; }).toArray();
            expect(indexes).toComprise(Lazy.range(indexes.length));
          });

          describe('each', function() {
            it('returns true if the entire sequence is iterated', function() {
              var result = iterateResult();
              expect(result).toBe(true);
            });

            it('returns false if iteration is terminated early', function() {
              var result = getResult().each(alwaysFalse);
              expect(result).toBe(false);
            });

            it('returns false if the last iteration returns false', function() {
              var length = getResult().value().length;
              var result = getResult().each(function(e, i) {
                if (i === length - 1) {
                  return false;
                }
              });
              expect(result).toBe(false);
            });
          });

          describe('indexed access', function() {
            it('is supported', function() {
              expect(getResult().get(1)).toEqual(testCase.result[1]);
            });

            if (lookupValue('arrayLike', [sequenceType, options])) {
              it('does not invoke full iteration', function() {
                getResult().get(1);
                expect(monitor.accessCount()).toEqual(1);
              });
            }
          });

          if (lookupValue('supportsAsync', [sequenceType, options])) {
            describe('async iteration', function() {
              var async = new AsyncSpec(this);

              function getAsyncResult() {
                return getResult().async();
              }

              // Currently this tests if blah().async() works.
              // TODO: First, think about whether async().blah() should work.
              // TODO: IF it should work, then make it work (better)!

              async.it('is supported', function(done) {
                getAsyncResult().toArray().onComplete(function(result) {
                  expect(result).toEqual(testCase.result);
                  done();
                });
              });

              async.it('supports early termination', function(done) {
                var expectedAccessCount = testCase.accessCountForTake2 || 2;

                getAsyncResult().take(2).toArray().onComplete(function(result) {
                  expect(result).toEqual(testCase.result.slice(0, 2));
                  done();
                });
              });
            });
          }
        });
      });
    });
  }
Verifies the following for a given sequence method, for a specified case
(input/output):

- the actual sequence behavior (result matches expected output)
- consistent behavior among different base sequence types (e.g., wrapped
  array, array-like, and vanilla Sequence)
- true laziness (does not iterate until `each` is called)
- support for early termination
- support for async iteration