Repository: dotnet/runtime
File: src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/ReadWrite/ByteTests.cs (C#)

Code before:
        public void ReadByte_StructWithReferenceTypes_ReturnsExpected()
        {
            int pointerOffset = Marshal.OffsetOf<StructWithReferenceTypes>(nameof(StructWithReferenceTypes.pointerValue)).ToInt32();
            int stringOffset = Marshal.OffsetOf<StructWithReferenceTypes>(nameof(StructWithReferenceTypes.stringValue)).ToInt32();
            int arrayOffset = Marshal.OffsetOf<StructWithReferenceTypes>(nameof(StructWithReferenceTypes.byValueArray)).ToInt32();

            object structure = new StructWithReferenceTypes
            {
                pointerValue = (IntPtr)100,
                stringValue = "ABC",
                byValueArray = new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
            };

            Assert.Equal(100, Marshal.ReadByte(structure, pointerOffset));
            // Unlike the Int16/32/64 tests that mirror this one, we aren't going to do any asserts on the value at stringOffset.
            // The value at stringOffset is a pointer, so it is entirely possible that the first byte is 0.
            // We tried keeping this test by summing 20 calls, but even that still resulted in 0 too often. As a result, we've
            // decided to not try to validate this case instead of just incrementally increasing the number of iterations
            // to avoid getting a 0 value.
            Assert.Equal(3, Marshal.ReadByte(structure, arrayOffset + sizeof(byte) * 2));
        }

        [Fact]
        public void ReadByte_ZeroPointer_ThrowsException()
        {
            AssertExtensions.ThrowsAny<AccessViolationException, NullReferenceException>(() => Marshal.ReadByte(IntPtr.Zero));
            if (PlatformDetection.SupportsDirtyAccessViolations)
            {
                AssertExtensions.ThrowsAny<AccessViolationException, NullReferenceException>(() => Marshal.ReadByte(IntPtr.Zero, 2));
            }
        }

        [Fact]
        [SkipOnMono("Marshal.ReadByte will not be implemented in Mono, see https://github.com/mono/mono/issues/15085.")]
        public void ReadByte_NullObject_ThrowsAccessViolationException()
        {
            Assert.Throws<AccessViolationException>(() => Marshal.ReadByte(null, 2));
        }

        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
        [SkipOnMono("Marshal.ReadByte will not be implemented in Mono, see https://github.com/mono/mono/issues/15085.")]
        public void ReadByte_NotReadable_ThrowsArgumentException()
        {
            AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Assembly"), AssemblyBuilderAccess.RunAndCollect);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");
            TypeBuilder typeBuilder = moduleBuilder.DefineType("Type");
            Type collectibleType = typeBuilder.CreateType();
            object collectibleObject = Activator.CreateInstance(collectibleType);

            AssertExtensions.Throws<ArgumentException>(null, () => Marshal.ReadByte(collectibleObject, 0));
        }

Review comment (suggestion):
You can cleanup all these tests to expect `AccessViolationException` only. It is the only expected exception with the unified CoreLib. My guess is that NullReferenceException is left-over from the diverging CoreLib impls.

Code after:
        public void ReadByte_StructWithReferenceTypes_ReturnsExpected()
        {
            int pointerOffset = Marshal.OffsetOf<StructWithReferenceTypes>(nameof(StructWithReferenceTypes.pointerValue)).ToInt32();
            int stringOffset = Marshal.OffsetOf<StructWithReferenceTypes>(nameof(StructWithReferenceTypes.stringValue)).ToInt32();
            int arrayOffset = Marshal.OffsetOf<StructWithReferenceTypes>(nameof(StructWithReferenceTypes.byValueArray)).ToInt32();

            object structure = new StructWithReferenceTypes
            {
                pointerValue = (IntPtr)100,
                stringValue = "ABC",
                byValueArray = new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
            };

            Assert.Equal(100, Marshal.ReadByte(structure, pointerOffset));
            // Unlike the Int16/32/64 tests that mirror this one, we aren't going to do any asserts on the value at stringOffset.
            // The value at stringOffset is a pointer, so it is entirely possible that the first byte is 0.
            // We tried keeping this test by summing 20 calls, but even that still resulted in 0 too often. As a result, we've
            // decided to not try to validate this case instead of just incrementally increasing the number of iterations
            // to avoid getting a 0 value.
            Assert.Equal(3, Marshal.ReadByte(structure, arrayOffset + sizeof(byte) * 2));
        }

        [Fact]
        public void ReadByte_ZeroPointer_ThrowsException()
        {
            AssertExtensions.Throws<AccessViolationException>(() => Marshal.ReadByte(IntPtr.Zero));
            if (PlatformDetection.SupportsDirtyAccessViolations)
            {
                AssertExtensions.Throws<AccessViolationException>(() => Marshal.ReadByte(IntPtr.Zero, 2));
            }
        }

        [Fact]
        [SkipOnMono("Marshal.ReadByte will not be implemented in Mono, see https://github.com/mono/mono/issues/15085.")]
        public void ReadByte_NullObject_ThrowsAccessViolationException()
        {
            Assert.Throws<AccessViolationException>(() => Marshal.ReadByte(null, 2));
        }

        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
        [SkipOnMono("Marshal.ReadByte will not be implemented in Mono, see https://github.com/mono/mono/issues/15085.")]
        public void ReadByte_NotReadable_ThrowsArgumentException()
        {
            AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Assembly"), AssemblyBuilderAccess.RunAndCollect);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");
            TypeBuilder typeBuilder = moduleBuilder.DefineType("Type");
            Type collectibleType = typeBuilder.CreateType();
            object collectibleObject = Activator.CreateInstance(collectibleType);

            AssertExtensions.Throws<ArgumentException>(null, () => Marshal.ReadByte(collectibleObject, 0));
        }