def testFullShapeAvailableAtRuntimeOnly(self):
    """What happens when shape information isn't available statically?

    The short answer is: it still works. The long answer is: it still works, but
    arguments that shouldn't work due to argument shape mismatch can sometimes
    work without raising any errors! This can cause insidious bugs. This test
    verifies correct behaviour and also demonstrates kinds of shape mismatch
    that can go undetected. Look for `!!!DANGER!!!` below.

    Why this is possible: internally, `batched_index` flattens its inputs,
    then transforms the action indices you provide into indices into its
    flattened Q values tensor. So long as these flattened indices don't go
    out-of-bounds, and so long as your arguments are compatible with a few
    other bookkeeping operations, the operation will succeed.

    The moral: always provide as much shape information as you can! See also
    `testInputShapeChecks` for more on what shape checking can accomplish when
    only partial shape information is available.
    """

    ## 1. No shape information is available during construction time.
    q_values = tf.placeholder(tf.float32)
    actions = tf.placeholder(tf.int32)
    values = indexing_ops.batched_index(q_values, actions)

    with self.test_session() as sess:
      # First, correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [51],
          sess.run(values, feed_dict={q_values: [[50, 51]], actions: [1]}))
      self.assertAllClose(
          [[51, 52]],
          sess.run(values,
                   feed_dict={q_values: [[[50, 51], [52, 53]]],
                              actions: [[1, 0]]}))

      # !!!DANGER!!! These "incompatible" shapes are silently tolerated!
      # (These examples are probably not exhaustive, either!)
      qs_2x2 = [[5, 5], [5, 5]]
      qs_2x2x2 = [[[5, 5], [5, 5]],
                  [[5, 5], [5, 5]]]
      sess.run(values, feed_dict={q_values: qs_2x2, actions: [0]})
      sess.run(values, feed_dict={q_values: qs_2x2, actions: 0})
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [[0]]})
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [0]})
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: 0})

    ## 2a. Shape information is only known for the batch size (2-D case).
    q_values = tf.placeholder(tf.float32, shape=[2, None])
    actions = tf.placeholder(tf.int32, shape=[2])
    values = indexing_ops.batched_index(q_values, actions)
    with self.test_session() as sess:
      # Correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [51, 52],
          sess.run(values,
                   feed_dict={q_values: [[50, 51], [52, 53]], actions: [1, 0]}))
      # There are no really terrible shape errors that go uncaught in this case.

    ## 2b. Shape information is only known for the batch size (3-D case).
    q_values = tf.placeholder(tf.float32, shape=[None, 2, None])
    actions = tf.placeholder(tf.int32, shape=[None, 2])
    values = indexing_ops.batched_index(q_values, actions)
    with self.test_session() as sess:
      # Correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [[51, 52]],
          sess.run(values,
                   feed_dict={q_values: [[[50, 51], [52, 53]]],
                              actions: [[1, 0]]}))

      # !!!DANGER!!! This "incompatible" shape is silently tolerated!
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [[0, 0]]})

    ## 3. Shape information is only known for the sequence length.
    q_values = tf.placeholder(tf.float32, shape=[2, None, None])
    actions = tf.placeholder(tf.int32, shape=[2, None])
    values = indexing_ops.batched_index(q_values, actions)
    with self.test_session() as sess:
      # Correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [[51, 52], [54, 57]],
          sess.run(values,
                   feed_dict={q_values: [[[50, 51], [52, 53]],
                                         [[54, 55], [56, 57]]],
                              actions: [[1, 0], [0, 1]]}))

      # !!!DANGER!!! This "incompatible" shape is silently tolerated!
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [[0], [0]]})

    ## 4a. Shape information is only known for the number of actions (2-D case).
    q_values = tf.placeholder(tf.float32, shape=[None, 2])
    actions = tf.placeholder(tf.int32, shape=[None])
    values = indexing_ops.batched_index(q_values, actions)
    with self.test_session() as sess:
      # Correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [51, 52],
          sess.run(values,
                   feed_dict={q_values: [[50, 51], [52, 53]], actions: [1, 0]}))

      # !!!DANGER!!! This "incompatible" shape is silently tolerated!
      sess.run(values, feed_dict={q_values: qs_2x2, actions: [0]})

    ## 4b. Shape information is only known for the number of actions (3-D case).
    q_values = tf.placeholder(tf.float32, shape=[None, None, 2])
    actions = tf.placeholder(tf.int32, shape=[None, None])
    values = indexing_ops.batched_index(q_values, actions)
    with self.test_session() as sess:
      # Correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [[51, 52]],
          sess.run(values,
                   feed_dict={q_values: [[[50, 51], [52, 53]]],
                              actions: [[1, 0]]}))

      # !!!DANGER!!! These "incompatible" shapes are silently tolerated!
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [[0, 0]]})
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [[0]]})

    ## 5a. Value shape is not known ahead of time.
    q_values = tf.placeholder(tf.float32)
    actions = tf.placeholder(tf.int32, shape=[2])
    values = indexing_ops.batched_index(q_values, actions)
    with self.test_session() as sess:
      # Correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [51, 52],
          sess.run(values,
                   feed_dict={q_values: [[50, 51], [52, 53]], actions: [1, 0]}))

      # !!!DANGER!!! This "incompatible" shape is silently tolerated!
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [0, 0]})

    ## 5b. Action shape is not known ahead of time.
    q_values = tf.placeholder(tf.float32, shape=[None, None, 2])
    actions = tf.placeholder(tf.int32)
    values = indexing_ops.batched_index(q_values, actions)
    with self.test_session() as sess:
      # Correct and compatible Q values and indices work as intended.
      self.assertAllClose(
          [[51, 52], [54, 57]],
          sess.run(values,
                   feed_dict={q_values: [[[50, 51], [52, 53]],
                                         [[54, 55], [56, 57]]],
                              actions: [[1, 0], [0, 1]]}))

      # !!!DANGER!!! This "incompatible" shape is silently tolerated!
      sess.run(values, feed_dict={q_values: qs_2x2x2, actions: [0, 0]})
What happens when shape information isn't available statically?

    The short answer is: it still works. The long answer is: it still works, but
    arguments that shouldn't work due to argument shape mismatch can sometimes
    work without raising any errors! This can cause insidious bugs. This test
    verifies correct behaviour and also demonstrates kinds of shape mismatch
    that can go undetected. Look for `!!!DANGER!!!` below.

    Why this is possible: internally, `batched_index` flattens its inputs,
    then transforms the action indices you provide into indices into its
    flattened Q values tensor. So long as these flattened indices don't go
    out-of-bounds, and so long as your arguments are compatible with a few
    other bookkeeping operations, the operation will succeed.

    The moral: always provide as much shape information as you can! See also
    `testInputShapeChecks` for more on what shape checking can accomplish when
    only partial shape information is available.
    
trfl/indexing_ops_test.py