diff --git a/generated_tests/random_ubo-arb_uniform_buffer_object.py b/generated_tests/random_ubo-arb_uniform_buffer_object.py
index 915963f..d9cdb5d 100644
--- a/generated_tests/random_ubo-arb_uniform_buffer_object.py
+++ b/generated_tests/random_ubo-arb_uniform_buffer_object.py
@@ -64,7 +64,7 @@ def do_test(requirements, packing):
         blocks,
         packing,
         130,
-        ["GL_ARB_uniform_buffer_object"]))
+        ["GL_ARB_uniform_buffer_object","GL_ARB_arrays_of_arrays"]))
 
     file.close()
 
@@ -115,6 +115,11 @@ all_requirements.append([["struct", "array", "struct"]])
 all_requirements.append([["array", "struct", "struct"]])
 all_requirements.append([["array", "struct", "array", "struct"]])
 
+all_requirements.append([["struct", "array", "array", "struct"]])
+all_requirements.append([["struct", "array", "array", "array", "struct"]])
+all_requirements.append([["array", "array", "struct", "array"]])
+all_requirements.append([["struct", "array", "array", "array"]])
+
 for p in all_packing:
     for r in all_requirements:
         do_test(r, p)
diff --git a/generated_tests/random_ubo.py b/generated_tests/random_ubo.py
index 33359f6..f6952d3 100644
--- a/generated_tests/random_ubo.py
+++ b/generated_tests/random_ubo.py
@@ -141,7 +141,6 @@ def array_elements(type):
     # Is there a better way to do this?
     return int(type.split("[")[1].split("]")[0])
 
-
 def basic_machine_units(type):
     """Return the size in 'basic machine units' of a scalar type."""
     if type in ["float", "bool", "int", "uint"]:
@@ -158,9 +157,27 @@ def array_base_type(type):
     if not isarray(type):
         raise Exception("Non-array type {}".format(type))
 
-    # XXX This will need changes for arrays-of-arrays.
+    return type[:type.index("[")] + type[type.index("]")+1:]
+
+def without_array(type):
+    """Return the type of an element with all arrays removed."""
+    if not isarray(type):
+        raise Exception("Non-array type {}".format(type))
+
     return type.split("[")[0]
 
+def arrays_of_arrays_size(type):
+    """Return the total number of elements in an array of arrays."""
+    if not isarray(type):
+        raise Exception("Non-array type {}".format(type))
+
+    size = 1
+    base_type = type
+    while (isarray(base_type)):
+        size = size * array_elements(base_type)
+        base_type = array_base_type(base_type)
+
+    return size
 
 def vector_base_type(type):
     """Return the type of an element from a vector."""
@@ -188,7 +205,7 @@ def iterate_structures(fields, types_seen=[], types_yielded=[]):
     """
     for (type, name) in fields:
         if isarray(type):
-            type = array_base_type(type)
+            type = without_array(type)
 
         if not isstructure(type):
             continue
@@ -351,7 +368,7 @@ def generate_layouts(fields, required_layouts, allow_row_major_structure):
     layouts = []
     for ((type, name), lay) in zip(fields, required_layouts):
         if isarray(type):
-            type = array_base_type(type)
+            type = without_array(type)
 
         if lay:
             layouts.append(lay)
@@ -407,6 +424,60 @@ def fields_to_glsl_struct(type):
 
     return structure_template.render(struct_name=type, fields=struct_types[type])
 
+def iterate_struct_array_recursive(field_type,
+                                   name_from_API,
+                                   name_from_shader,
+                                   packing,
+                                   offset,
+                                   row_major,
+                                   field_row_major,
+                                   explicit_layout):
+
+    astride = packing.array_stride(field_type, field_row_major)
+    array_member_align = packing.base_alignment(field_type, field_row_major)
+
+    element_t = array_base_type(field_type)
+
+    for i in xrange(array_elements(field_type)):
+        name_from_API_with_index = "{}[{}]".format(name_from_API, i)
+        name_from_shader_with_index = "{}[{}]".format(name_from_shader, i)
+
+        if isarray(element_t):
+            inner_aoa_size = arrays_of_arrays_size(element_t)
+            o = align(offset, array_member_align) + (astride * i * inner_aoa_size)
+
+            for x in iterate_struct_array_recursive(element_t,
+                                                    name_from_API_with_index,
+                                                    name_from_shader_with_index,
+                                                    packing,
+                                                    o,
+                                                    row_major,
+                                                    field_row_major,
+                                                    explicit_layout):
+                yield x
+
+                a = packing.base_alignment(x.GLSL_type, row_major)
+                o = align(o, a) + packing.size(x.GLSL_type, row_major)
+        else:
+            o = align(offset, array_member_align) + (astride * i)
+            yield block_member(
+                name_from_shader_with_index,
+                name_from_API_with_index,
+                element_t,
+                explicit_layout,
+                o,
+                field_row_major)
+            for x in iterate_all_recursive(struct_types[element_t],
+                                           None,
+                                           name_from_API_with_index,
+                                           name_from_shader_with_index,
+                                           packing,
+                                           o,
+                                           field_row_major):
+                yield x
+
+                a = packing.base_alignment(x.GLSL_type, row_major)
+                o = align(o, a) + packing.size(x.GLSL_type, row_major)
 
 def iterate_all_recursive(fields,
                           field_layouts,
@@ -483,7 +554,7 @@ def iterate_all_recursive(fields,
             field_row_major = row_major
 
         if isarray(field_type):
-            base_type = array_base_type(field_type)
+            base_type = without_array(field_type)
 
             if isstructure(base_type):
                 yield block_member(
@@ -494,40 +565,15 @@ def iterate_all_recursive(fields,
                     offset,
                     field_row_major)
 
-                astride = packing.array_stride(field_type, field_row_major)
-                array_member_align = packing.base_alignment(
-                    field_type,
-                    field_row_major)
-
-                for i in xrange(array_elements(field_type)):
-                    name_from_API_with_index = "{}[{}]".format(
-                        name_from_API,
-                        i)
-                    name_from_shader_with_index = "{}[{}]".format(
-                        name_from_shader,
-                        i)
-
-                    o = align(offset, array_member_align) + (astride * i)
-
-                    yield block_member(
-                        name_from_shader_with_index,
-                        name_from_API_with_index,
-                        base_type,
-                        explicit_layout,
-                        o,
-                        field_row_major)
-
-                    for x in iterate_all_recursive(struct_types[base_type],
-                                                   None,
-                                                   name_from_API_with_index,
-                                                   name_from_shader_with_index,
-                                                   packing,
-                                                   o,
-                                                   field_row_major):
-                        yield x
-
-                        a = packing.base_alignment(x.GLSL_type, row_major)
-                        o = align(o, a) + packing.size(x.GLSL_type, row_major)
+                for x in iterate_struct_array_recursive(field_type,
+                                           name_from_API,
+                                           name_from_shader,
+                                           packing,
+                                           offset,
+                                           row_major,
+                                           field_row_major,
+                                           explicit_layout):
+                    yield x
 
             elif ismatrix(base_type):
                 yield block_member(
@@ -709,19 +755,25 @@ def generate_test_vectors(fields,
         a = packing.base_alignment(m.GLSL_type, m.row_major)
 
         if isarray(m.GLSL_type):
+            size = array_elements(m.GLSL_type)
             base_type = array_base_type(m.GLSL_type)
             astride = packing.array_stride(m.GLSL_type, m.row_major)
             name = m.API_name + "[0]"
+            while(isarray(base_type)):
+                size = array_elements(base_type)
+                base_type = array_base_type(base_type)
+                name = name + "[0]"
         else:
             base_type = m.GLSL_type
             astride = 0
             name = m.API_name
+            size = m.size
 
         if ismatrix(base_type):
             test_vectors.append((
                     name,
                     m.API_type, 
-                    m.size,
+                    size,
                     align(m.offset, a),
                     astride,
                     packing.matrix_stride(base_type, m.row_major),
@@ -730,7 +782,7 @@ def generate_test_vectors(fields,
             test_vectors.append((
                     name,
                     m.API_type, 
-                    m.size,
+                    size,
                     align(m.offset, a),
                     astride,
                     0,
@@ -862,6 +914,44 @@ def bit_exact_data(raw_data, type):
     else:
         return raw_data
 
+def generate_array_data_pairs(name, api_name, element_type, row_major, offset, packing, setters, checkers):
+    base_type = array_base_type(element_type)
+
+    astride = packing.array_stride(element_type, row_major)
+
+    for i in xrange(array_elements(element_type)):
+
+        name_with_index = "{}[{}]".format(name, i)
+        api_name_with_index = "{}[{}]".format(api_name, i)
+
+        if isarray(base_type):
+            offset = offset + (i * arrays_of_arrays_size(base_type) * astride)
+            generate_array_data_pairs(name_with_index, api_name_with_index, base_type, row_major, offset, packing, setters, checkers)
+        else:
+            offset = offset + (i * astride)
+            raw_data = random_data(base_type, name_with_index, offset)
+            setters.append(
+                (fudge_type_for_setter(base_type),
+                api_name_with_index,
+                bit_exact_data(raw_data, base_type)))
+
+            data = raw_data.split(" ")
+
+            if isscalar(base_type):
+                checkers.append(scalar_derp(base_type,
+                                            name_with_index,
+                                            offset,
+                                            data[0]))
+            elif isvector(base_type):
+                checkers.extend(vector_derp(base_type,
+                                            name_with_index,
+                                            offset,
+                                            data))
+            elif ismatrix(base_type):
+                checkers.extend(matrix_derp(base_type,
+                                            name_with_index,
+                                            offset,
+                                            data))
 
 def generate_data_pairs(uniform_blocks, packing):
     """Return GLSL code to test values and shader_runner code to set them.
@@ -893,38 +983,7 @@ def generate_data_pairs(uniform_blocks, packing):
 
             if m.API_type:
                 if isarray(m.GLSL_type):
-                    base_type = array_base_type(m.GLSL_type)
-
-                    astride = packing.array_stride(m.GLSL_type, m.row_major)
-
-                    for i in xrange(array_elements(m.GLSL_type)):
-
-                        name = "{}[{}]".format(m.GLSL_name, i)
-                        offset = m.offset + (i * astride)
-
-                        raw_data = random_data(base_type, m.GLSL_name, offset)
-                        setters.append(
-                            (fudge_type_for_setter(base_type),
-                             "{}[{}]".format(m.API_name, i),
-                             bit_exact_data(raw_data, base_type)))
-
-                        data = raw_data.split(" ")
-
-                        if isscalar(base_type):
-                            checkers.append(scalar_derp(base_type,
-                                                        name,
-                                                        offset,
-                                                        data[0]))
-                        elif isvector(base_type):
-                            checkers.extend(vector_derp(base_type,
-                                                        name,
-                                                        offset,
-                                                        data))
-                        elif ismatrix(base_type):
-                            checkers.extend(matrix_derp(base_type,
-                                                        name,
-                                                        offset,
-                                                        data))
+                    generate_array_data_pairs(m.GLSL_name, m.API_name, m.GLSL_type, m.row_major, m.offset, packing, setters, checkers)
                 else:
                     raw_data = random_data(m.GLSL_type, m.GLSL_name, m.offset)
                     setters.append((fudge_type_for_setter(m.GLSL_type),
@@ -969,7 +1028,7 @@ def pretty_format_type_data(packing, type, offset, row_major):
     if isarray(type):
         astride = packing.array_stride(type, row_major)
 
-        base_type = array_base_type(type)
+        base_type = without_array(type)
         if ismatrix(base_type) and row_major:
             if row_major:
                 row_major_str = "yes"
@@ -1009,13 +1068,12 @@ def pretty_format_member(m, packing):
     # If the name ends in an array subscript, emit a special line to note that
     # the following fields are the contents of an element of an array of
     # structures.
-
     if m.GLSL_name[-1] == "]":
         n = m.struct_nesting() + 1
         indent = "//  " + ("  " * n)
 
-        return "{indent}[{index}".format(indent=indent,
-                                         index=m.GLSL_name.split("[")[-1])
+        return "{indent}{index}".format(indent=indent,
+                                         index=m.GLSL_name[m.GLSL_name.index("["):])
 
     # Strip off everything before the last period.
     name = m.GLSL_name.split(".")[-1]
@@ -1337,8 +1395,8 @@ class packing_rules(object):
         size, in bytes, of the entire data type.  This will include padded
         after the data element as required by the GLSL specification.
         """
-        if "[" in type:
-            return self.array_stride(type, row_major) * array_elements(type)
+        if isarray(type):
+            return self.array_stride(type, row_major) * arrays_of_arrays_size(type)
 
         if type in ["float", "bool", "int", "uint"]:
             return 4
@@ -1484,7 +1542,7 @@ class std140_packing_rules(packing_rules):
                 return max(16, self.base_alignment("vec{}".format(c), False))
 
     def array_stride(self, type, row_major):
-        base_type = array_base_type(type)
+        base_type = without_array(type)
 
         if not isstructure(base_type):
             # (4) If the member is an array of scalars or vectors, the base
@@ -1547,7 +1605,7 @@ class unique_name_dict:
         form.  All other type names are unmodified.
         """
         if isarray(type):
-            t = array_base_type(type)
+            t = without_array(type)
         else:
             t = type
 
@@ -1577,7 +1635,7 @@ class unique_name_dict:
         (indexed by "type") of (base_name, 1).
         """
         if isarray(type):
-            t = array_base_type(type)
+            t = without_array(type)
         else:
             t = type
 
@@ -1690,8 +1748,7 @@ class block_member(object):
         self.row_major = row_major
 
         if isarray(GLSL_type):
-            base_type = array_base_type(GLSL_type)
-
+            base_type = without_array(GLSL_type)
             if isstructure(base_type):
                 self.API_type = None
             else:
