function setupGappyManifest(gapAtStart, dropSegment) {
      // In these tests we are going to test GapJumpingController so we are
      // interested in activating the stall detector.
      config.stallEnabled = true;

      /**
       * @param {string} type
       * @param {shaka.media.InitSegmentReference} initSegmentReference
       * @return {!shaka.media.SegmentIndex}
       */
      function createIndex(type, initSegmentReference) {
        const d = metadata[type].segmentDuration;
        const refs = [];
        let i = 0;
        let time = gapAtStart;
        while (time < 30) {
          let end = time + d;
          // Make segment 0 longer to make the manifest continuous, despite the
          // dropped segment.
          if (i == 0 && dropSegment) {
            end += d;
          }

          let cur = i;
          const getUris = () => {
            // The times in the media are based on the URL; so to drop a
            // segment, we change the URL.
            if (cur >= 1 && dropSegment) {
              cur++;
            }
            return ['0_' + type + '_' + cur];
          };
          refs.push(new shaka.media.SegmentReference(
              /* startTime= */ time,
              /* endTime= */ end,
              getUris,
              /* startByte= */ 0,
              /* endByte= */ null,
              initSegmentReference,
              /* timestampOffset= */ gapAtStart,
              /* appendWindowStart= */ 0,
              /* appendWindowEnd= */ Infinity));
          i++;
          time = end;
        }
        return new shaka.media.SegmentIndex(refs);
      }

      function createInit(type) {
        const getUris = () => {
          return ['0_' + type + '_init'];
        };
        return new shaka.media.InitSegmentReference(getUris, 0, null);
      }

      const videoInit = createInit('video');
      const videoIndex = createIndex('video', videoInit);
      const audioInit = createInit('audio');
      const audioIndex = createIndex('audio', audioInit);

      return {
        presentationTimeline: timeline,
        offlineSessionIds: [],
        textStreams: [],
        imageStreams: [],
        sequenceMode: false,
        ignoreManifestTimestampsInSegmentsMode: false,
        type: 'UNKNOWN',
        serviceDescription: null,
        nextUrl: null,
        periodCount: 1,
        gapCount: 0,
        isLowLatency: false,
        startTime: null,
        variants: [{
          id: 1,
          video: {
            id: 2,
            createSegmentIndex: () => Promise.resolve(),
            segmentIndex: videoIndex,
            mimeType: 'video/mp4',
            codecs: 'avc1.42c01e',
            bandwidth: 5000000,
            width: 600,
            height: 400,
            type: shaka.util.ManifestParserUtils.ContentType.VIDEO,
            drmInfos: [],
          },
          audio: {
            id: 3,
            createSegmentIndex: () => Promise.resolve(),
            segmentIndex: audioIndex,
            mimeType: 'audio/mp4',
            codecs: 'mp4a.40.2',
            bandwidth: 192000,
            type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
            drmInfos: [],
          },
        }],
      };
    }
TODO: Consolidate with StreamingEngineUtil.createManifest?
@param {number} gapAtStart
@param {boolean} dropSegment
@return {shaka.extern.Manifest}