Repository: dotnet/runtime
File: src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveAssemblyBuilderTests.cs (C#)

Code before:
            MetadataReader metadataReader = peReader.GetMetadataReader();
            AssemblyDefinition assemblyDefinition = metadataReader.GetAssemblyDefinition();

            Assert.Equal((AssemblyFlags)assemblyName.Flags | (AssemblyFlags)((int)assemblyName.ContentType << ContentTypeFlagShift), assemblyDefinition.Flags);
            Assert.True((assemblyDefinition.Flags & AssemblyFlags.PublicKey) != 0);
            Assert.Equal(((int)assemblyName.Flags) & ProcessorArchitectureMask, ((int)assemblyDefinition.Flags) & ProcessorArchitectureMask);
            Assert.Equal(expectedPublicKey, metadataReader.GetBlobBytes(assemblyDefinition.PublicKey));
        }

        [Fact]
        public void PersistedAssemblyBuilder_SaveGenerateMetadataLifecycleValidation()
        {
            PersistedAssemblyBuilder afterGenerateMetadata = CreateSimplePersistedAssembly(new AssemblyName("GenerateThenSave"));
            afterGenerateMetadata.GenerateMetadata(out BlobBuilder generatedIlStream, out BlobBuilder generatedFieldData);
            Assert.Throws<InvalidOperationException>(() => afterGenerateMetadata.Save(new MemoryStream()));
            Assert.NotNull(generatedIlStream);
            Assert.NotNull(generatedFieldData);

            PersistedAssemblyBuilder afterSave = CreateSimplePersistedAssembly(new AssemblyName("SaveThenGenerate"));
            afterSave.Save(new MemoryStream());
            Assert.Throws<InvalidOperationException>(() => afterSave.GenerateMetadata(out BlobBuilder _, out BlobBuilder _));
            Assert.Throws<InvalidOperationException>(() => afterSave.Save(new MemoryStream()));
        }

        [Fact]
        public void PersistedAssemblyBuilder_SaveToStreamWithOffset()
        {
            PersistedAssemblyBuilder ab = CreateSimplePersistedAssembly(new AssemblyName("SaveToStreamWithOffset"));
            byte[] prefix = [10, 20, 30];
            using MemoryStream stream = new MemoryStream();

            stream.Write(prefix);
            long saveStartPosition = stream.Position;

            ab.Save(stream);

            Assert.Equal(stream.Length, stream.Position);
            Assert.True(stream.Length > saveStartPosition);
            Assert.Equal(prefix, stream.GetBuffer().AsSpan(0, prefix.Length).ToArray());
        }

        [Fact]
        public void PersistedAssemblyBuilder_SaveToStreamValidation()
        {
            PersistedAssemblyBuilder nonWritableAssembly = CreateSimplePersistedAssembly(new AssemblyName("SaveToNonWritableStream"));
            using MemoryStream nonWritableStream = new MemoryStream(Array.Empty<byte>(), writable: false);
            Assert.Throws<NotSupportedException>(() => nonWritableAssembly.Save(nonWritableStream));

            PersistedAssemblyBuilder disposedAssembly = CreateSimplePersistedAssembly(new AssemblyName("SaveToDisposedStream"));
            MemoryStream disposedStream = new MemoryStream();
            disposedStream.Dispose();

Review comment (suggestion):
The implementation passes the stream to `BlobBuilder.WriteContentTo`. These tests for various corner cases of Stream handling would be more appropriate in System.Reflection.Metadata tests if they are not there already.

Code after:
            TypeBuilder typeBuilder = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
            typeBuilder.CreateType();
            using MemoryStream stream = new MemoryStream();
            ab.Save(stream);

            stream.Position = 0;
            using PEReader peReader = new PEReader(stream);
            MetadataReader metadataReader = peReader.GetMetadataReader();
            AssemblyDefinition assemblyDefinition = metadataReader.GetAssemblyDefinition();
            AssemblyName loadedAssemblyName = assemblyDefinition.GetAssemblyNameInfo().ToAssemblyName();

            Assert.Equal(assemblyName.Name, loadedAssemblyName.Name);
            Assert.Equal(assemblyName.Version, loadedAssemblyName.Version);
            Assert.Equal(assemblyName.CultureName, loadedAssemblyName.CultureName);
            Assert.Equal(assemblyName.ContentType, loadedAssemblyName.ContentType);
            Assert.Equal(assemblyName.Flags, loadedAssemblyName.Flags);
            Assert.Equal(expectedPublicKey, loadedAssemblyName.GetPublicKey());
        }

        [Fact]
        public void PersistedAssemblyBuilder_GenerateMetadataWithEntryPoint()
        {
            PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(_assemblyName);
            TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
            MethodBuilder mb1 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]);
            ILGenerator il = mb1.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Add);
            il.Emit(OpCodes.Ret);
            MethodBuilder entryPoint = tb.DefineMethod("Main", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, typeof(int), null);
            ILGenerator il2 = entryPoint.GetILGenerator();
            il2.Emit(OpCodes.Ldc_I4_S, 10);
            il2.Emit(OpCodes.Ldc_I4_2);
            il2.Emit(OpCodes.Call, mb1);
            il2.Emit(OpCodes.Ret);
            tb.CreateType();

            MetadataBuilder metadataBuilder = ab.GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData);
            PEHeaderBuilder peHeaderBuilder = new PEHeaderBuilder(
                            imageCharacteristics: Characteristics.ExecutableImage,
                            subsystem: Subsystem.WindowsCui);

            ManagedPEBuilder peBuilder = new ManagedPEBuilder(
                            header: peHeaderBuilder,
                            metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
                            ilStream: ilStream,
                            mappedFieldData: fieldData,
                            entryPoint: MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken));

            BlobBuilder peBlob = new BlobBuilder();