Support creating FileDescriptors dynamically from binary data.

Related to #658 and #5007.
diff --git a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
index 29a376e..9abee95 100644
--- a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
@@ -30,10 +30,11 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
-using System.Linq;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
-using UnitTest.Issues.TestProtos;
+using System;
+using System.Collections.Generic;
+using System.Linq;
 
 namespace Google.Protobuf.Reflection
 {
@@ -44,9 +45,32 @@
     public class DescriptorsTest
     {
         [Test]
-        public void FileDescriptor()
+        public void FileDescriptor_GeneratedCode()
         {
-            FileDescriptor file = UnittestProto3Reflection.Descriptor;
+            TestFileDescriptor(
+                UnittestProto3Reflection.Descriptor,
+                UnittestImportProto3Reflection.Descriptor,
+                UnittestImportPublicProto3Reflection.Descriptor);
+        }
+
+        [Test]
+        public void FileDescriptor_BuildFromByteStrings()
+        {
+            // The descriptors have to be supplied in an order such that all the
+            // dependencies come before the descriptors depending on them.
+            var descriptorData = new List<ByteString>
+            {
+                UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+                UnittestImportProto3Reflection.Descriptor.Proto.ToByteString(),
+                UnittestProto3Reflection.Descriptor.Proto.ToByteString()
+            };
+            var converted = FileDescriptor.BuildFromByteStrings(descriptorData);
+            Assert.AreEqual(3, converted.Count);
+            TestFileDescriptor(converted[2], converted[1], converted[0]);
+        }
+
+        private void TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile)
+        {
 
             Assert.AreEqual("unittest_proto3.proto", file.Name);
             Assert.AreEqual("protobuf_unittest3", file.Package);
@@ -56,17 +80,12 @@
 
             // unittest_proto3.proto doesn't have any public imports, but unittest_import_proto3.proto does.
             Assert.AreEqual(0, file.PublicDependencies.Count);
-            Assert.AreEqual(1, UnittestImportProto3Reflection.Descriptor.PublicDependencies.Count);
-            Assert.AreEqual(UnittestImportPublicProto3Reflection.Descriptor, UnittestImportProto3Reflection.Descriptor.PublicDependencies[0]);
+            Assert.AreEqual(1, importedFile.PublicDependencies.Count);
+            Assert.AreEqual(importedPublicFile, importedFile.PublicDependencies[0]);
 
             Assert.AreEqual(1, file.Dependencies.Count);
-            Assert.AreEqual(UnittestImportProto3Reflection.Descriptor, file.Dependencies[0]);
+            Assert.AreEqual(importedFile, file.Dependencies[0]);
 
-            MessageDescriptor messageType = TestAllTypes.Descriptor;
-            Assert.AreSame(typeof(TestAllTypes), messageType.ClrType);
-            Assert.AreSame(TestAllTypes.Parser, messageType.Parser);
-            Assert.AreEqual(messageType, file.MessageTypes[0]);
-            Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
             Assert.Null(file.FindTypeByName<MessageDescriptor>("NoSuchType"));
             Assert.Null(file.FindTypeByName<MessageDescriptor>("protobuf_unittest3.TestAllTypes"));
             for (int i = 0; i < file.MessageTypes.Count; i++)
@@ -77,8 +96,8 @@
             Assert.AreEqual(file.EnumTypes[0], file.FindTypeByName<EnumDescriptor>("ForeignEnum"));
             Assert.Null(file.FindTypeByName<EnumDescriptor>("NoSuchType"));
             Assert.Null(file.FindTypeByName<EnumDescriptor>("protobuf_unittest3.ForeignEnum"));
-            Assert.AreEqual(1, UnittestImportProto3Reflection.Descriptor.EnumTypes.Count);
-            Assert.AreEqual("ImportEnum", UnittestImportProto3Reflection.Descriptor.EnumTypes[0].Name);
+            Assert.AreEqual(1, importedFile.EnumTypes.Count);
+            Assert.AreEqual("ImportEnum", importedFile.EnumTypes[0].Name);
             for (int i = 0; i < file.EnumTypes.Count; i++)
             {
                 Assert.AreEqual(i, file.EnumTypes[i].Index);
@@ -98,6 +117,56 @@
         }
 
         [Test]
+        public void FileDescriptor_BuildFromByteStrings_MissingDependency()
+        {
+            var descriptorData = new List<ByteString>
+            {
+                UnittestImportProto3Reflection.Descriptor.Proto.ToByteString(),
+                UnittestProto3Reflection.Descriptor.Proto.ToByteString(),
+            };
+            // This will fail, because we're missing UnittestImportPublicProto3Reflection
+            Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
+        }
+
+        [Test]
+        public void FileDescriptor_BuildFromByteStrings_DuplicateNames()
+        {
+            var descriptorData = new List<ByteString>
+            {
+                UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+                UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+            };
+            // This will fail due to the same name being used twice
+            Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
+        }
+
+        [Test]
+        public void FileDescriptor_BuildFromByteStrings_IncorrectOrder()
+        {
+            var descriptorData = new List<ByteString>
+            {
+                UnittestProto3Reflection.Descriptor.Proto.ToByteString(),
+                UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+                UnittestImportProto3Reflection.Descriptor.Proto.ToByteString()
+            };
+            // This will fail, because the dependencies should come first
+            Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
+
+        }
+
+        [Test]
+        public void MessageDescriptorFromGeneratedCodeFileDescriptor()
+        {
+            var file = UnittestProto3Reflection.Descriptor;
+
+            MessageDescriptor messageType = TestAllTypes.Descriptor;
+            Assert.AreSame(typeof(TestAllTypes), messageType.ClrType);
+            Assert.AreSame(TestAllTypes.Parser, messageType.Parser);
+            Assert.AreEqual(messageType, file.MessageTypes[0]);
+            Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
+        }
+
+        [Test]
         public void MessageDescriptor()
         {
             MessageDescriptor messageType = TestAllTypes.Descriptor;
@@ -163,7 +232,7 @@
             
             Assert.AreEqual("single_nested_enum", enumField.Name);
             Assert.AreEqual(FieldType.Enum, enumField.FieldType);
-            // Assert.AreEqual(TestAllTypes.Types.NestedEnum.DescriptorProtoFile, enumField.EnumType);
+            Assert.AreEqual(messageType.EnumTypes[0], enumField.EnumType);
 
             Assert.AreEqual("single_foreign_message", messageField.Name);
             Assert.AreEqual(FieldType.Message, messageField.FieldType);