text:
using System.Buffers;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Xunit;

namespace System.Tests
{
    public partial class StringTests
    {
        [Theory]
        [InlineData(0, 0)]
        [InlineData(3, 1)]
        public static void Ctor_CharSpan_EmptyString(int length, int offset)
        {
            Assert.Same(string.Empty, new string(new ReadOnlySpan<char>(new char[length], offset, 0)));
        }

        [Fact]
        public static unsafe void Ctor_CharSpan_Empty()
        {
            Assert.Same(string.Empty, new string((ReadOnlySpan<char>)null));
            Assert.Same(string.Empty, new string(ReadOnlySpan<char>.Empty));
        }

        [Theory]
        [InlineData(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\0' }, 0, 8, "abcdefgh")]
        [InlineData(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\0', 'i', 'j', 'k' }, 0, 12, "abcdefgh\0ijk")]
        [InlineData(new char[] { 'a', 'b', 'c' }, 0, 0, "")]
        [InlineData(new char[] { 'a', 'b', 'c' }, 0, 1, "a")]
        [InlineData(new char[] { 'a', 'b', 'c' }, 2, 1, "c")]
        [InlineData(new char[] { '\u8001', '\u8002', '\ufffd', '\u1234', '\ud800', '\udfff' }, 0, 6, "\u8001\u8002\ufffd\u1234\ud800\udfff")]
        public static void Ctor_CharSpan(char[] valueArray, int startIndex, int length, string expected)
        {
            var span = new ReadOnlySpan<char>(valueArray, startIndex, length);
            Assert.Equal(expected, new string(span));
        }

        [Fact]
        public static unsafe void Ctor_CharPtr_DoesNotAccessInvalidPage()
        {
            // Allocates a buffer of all 'x' followed by a null terminator,
            // then attempts to create a string instance from this at various offsets.

            const int MaxCharCount = 128;
            using BoundedMemory<char> boundedMemory = BoundedMemory.Allocate<char>(MaxCharCount);
            boundedMemory.Span.Fill('x');
            boundedMemory.Span[MaxCharCount - 1] = '\0';
            boundedMemory.MakeReadonly();

            using MemoryHandle memoryHandle = boundedMemory.Memory.Pin();

            for (int i = 0; i < MaxCharCount; i++)
            {
                string expectedString = new string('x', MaxCharCount - i - 1);
                string actualString = new string((char*)memoryHandle.Pointer + i);
                Assert.Equal(expectedString, actualString);
            }
        }

        [ConditionalFact(nameof(IsSimpleActiveCodePage))]
        public static unsafe void Ctor_SBytePtr_DoesNotAccessInvalidPage()
        {
            // Allocates a buffer of all ' ' followed by a null terminator,
            // then attempts to create a string instance from this at various offsets.
            // We use U+0020 SPACE instead of any other character because it lives
            // at offset 0x20 across every supported code page.

            const int MaxByteCount = 128;
            using BoundedMemory<sbyte> boundedMemory = BoundedMemory.Allocate<sbyte>(MaxByteCount);
            boundedMemory.Span.Fill((sbyte)' ');
            boundedMemory.Span[MaxByteCount - 1] = (sbyte)'\0';
            boundedMemory.MakeReadonly();

            using MemoryHandle memoryHandle = boundedMemory.Memory.Pin();

            for (int i = 0; i < MaxByteCount; i++)
            {
                string expectedString = new string(' ', MaxByteCount - i - 1);
                string actualString = new string((sbyte*)memoryHandle.Pointer + i);
                Assert.Equal(expectedString, actualString);
            }
        }

        [Fact]
        public static void Create_InvalidArguments_Throw()
        {
            AssertExtensions.Throws<ArgumentNullException>("action", () => string.Create(-1, 0, null));
            AssertExtensions.Throws<ArgumentOutOfRangeException>("length", () => string.Create(-1, 0, (span, state) => { }));
        }

        [Fact]
        public static void Create_Length0_ReturnsEmptyString()
        {
            bool actionInvoked = false;
            Assert.Same(string.Empty, string.Create(0, 0, (span, state) => actionInvoked = true));
            Assert.False(actionInvoked);
        }

        [Fact]
        public static void Create_NullState_Allowed()
        {
            string result = string.Create(1, (object)null, (span, state) =>
            {
                span[0] = 'a';
                Assert.Null(state);
            });
            Assert.Equal("a", result);
        }

        [Fact]
        public static void Create_ClearsMemory()
        {
            const int Length = 10;
            string result = string.Create(Length, (object)null, (span, state) =>
            {
                for (int i = 0; i < span.Length; i++)
                {
                    Assert.Equal('\0', span[i]);
                }
            });
            Assert.Equal(new string('\0', Length), result);
        }

        [Theory]
        [InlineData("a")]
        [InlineData("this is a test")]
        [InlineData("\0\u8001\u8002\ufffd\u1234\ud800\udfff")]
        public static void Create_ReturnsExpectedString(string expected)
        {
            char[] input = expected.ToCharArray();
            string result = string.Create(input.Length, input, (span, state) =>
            {
                Assert.Same(input, state);
                for (int i = 0; i < state.Length; i++)
                {
                    span[i] = state[i];
                }
            });
            Assert.Equal(expected, result);
        }

        [Theory]
        [InlineData("Hello", 'H', true)]
        [InlineData("Hello", 'Z', false)]
        [InlineData("Hello", 'e', true)]
        [InlineData("Hello", 'E', false)]
        [InlineData("", 'H', false)]
        public static void Contains(string s, char value, bool expected)
        {
            Assert.Equal(expected, s.Contains(value));

            ReadOnlySpan<char> span = s.AsSpan();
            Assert.Equal(expected, span.Contains(value));
        }

        [Theory]
        // CurrentCulture
        [InlineData("Hello", 'H', StringComparison.CurrentCulture, true)]
        [InlineData("Hello", 'Z', StringComparison.CurrentCulture, false)]
        [InlineData("Hello", 'e', StringComparison.CurrentCulture, true)]
        [InlineData("Hello", 'E', StringComparison.CurrentCulture, false)]
        [InlineData("", 'H', StringComparison.CurrentCulture, false)]
        // CurrentCultureIgnoreCase
        [InlineData("Hello", 'H', StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("Hello", 'Z', StringComparison.CurrentCultureIgnoreCase, false)]
        [InlineData("Hello", 'e', StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("Hello", 'E', StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("", 'H', StringComparison.CurrentCultureIgnoreCase, false)]
        // InvariantCulture
        [InlineData("Hello", 'H', StringComparison.InvariantCulture, true)]
        [InlineData("Hello", 'Z', StringComparison.InvariantCulture, false)]
        [InlineData("Hello", 'e', StringComparison.InvariantCulture, true)]
        [InlineData("Hello", 'E', StringComparison.InvariantCulture, false)]
        [InlineData("", 'H', StringComparison.InvariantCulture, false)]
        // InvariantCultureIgnoreCase
        [InlineData("Hello", 'H', StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("Hello", 'Z', StringComparison.InvariantCultureIgnoreCase, false)]
        [InlineData("Hello", 'e', StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("Hello", 'E', StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("", 'H', StringComparison.InvariantCultureIgnoreCase, false)]
        // Ordinal
        [InlineData("Hello", 'H', StringComparison.Ordinal, true)]
        [InlineData("Hello", 'Z', StringComparison.Ordinal, false)]
        [InlineData("Hello", 'e', StringComparison.Ordinal, true)]
        [InlineData("Hello", 'E', StringComparison.Ordinal, false)]
        [InlineData("", 'H', StringComparison.Ordinal, false)]
        // OrdinalIgnoreCase
        [InlineData("Hello", 'H', StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("Hello", 'Z', StringComparison.OrdinalIgnoreCase, false)]
        [InlineData("Hello", 'e', StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("Hello", 'E', StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("", 'H', StringComparison.OrdinalIgnoreCase, false)]
        public static void Contains(string s, char value, StringComparison comparisionType, bool expected)
        {
            Assert.Equal(expected, s.Contains(value, comparisionType));
        }

        [Theory]
        // CurrentCulture
        [InlineData("Hello", "ello", StringComparison.CurrentCulture, true)]
        [InlineData("Hello", "ELL", StringComparison.CurrentCulture, false)]
        [InlineData("Hello", "ElLo", StringComparison.CurrentCulture, false)]
        [InlineData("Hello", "Larger Hello", StringComparison.CurrentCulture, false)]
        [InlineData("Hello", "Goodbye", StringComparison.CurrentCulture, false)]
        [InlineData("", "", StringComparison.CurrentCulture, true)]
        [InlineData("", "hello", StringComparison.CurrentCulture, false)]
        [InlineData("Hello", "", StringComparison.CurrentCulture, true)]
        [InlineData("Hello", "ell" + SoftHyphen, StringComparison.CurrentCulture, true)]
        [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.CurrentCulture, false)]
        // CurrentCultureIgnoreCase
        [InlineData("Hello", "ello", StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("Hello", "ELL", StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("Hello", "ElLo", StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("Hello", "Larger Hello", StringComparison.CurrentCultureIgnoreCase, false)]
        [InlineData("Hello", "Goodbye", StringComparison.CurrentCultureIgnoreCase, false)]
        [InlineData("", "", StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("", "hello", StringComparison.CurrentCultureIgnoreCase, false)]
        [InlineData("Hello", "", StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("Hello", "ell" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true)]
        [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true)]
        // InvariantCulture
        [InlineData("Hello", "ello", StringComparison.InvariantCulture, true)]
        [InlineData("Hello", "ELL", StringComparison.InvariantCulture, false)]
        [InlineData("Hello", "ElLo", StringComparison.InvariantCulture, false)]
        [InlineData("Hello", "Larger Hello", StringComparison.InvariantCulture, false)]
        [InlineData("Hello", "Goodbye", StringComparison.InvariantCulture, false)]
        [InlineData("", "", StringComparison.InvariantCulture, true)]
        [InlineData("", "hello", StringComparison.InvariantCulture, false)]
        [InlineData("Hello", "", StringComparison.InvariantCulture, true)]
        [InlineData("Hello", "ell" + SoftHyphen, StringComparison.InvariantCulture, true)]
        [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.InvariantCulture, false)]
        // InvariantCultureIgnoreCase
        [InlineData("Hello", "ello", StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("Hello", "ELL", StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("Hello", "ElLo", StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("Hello", "Larger Hello", StringComparison.InvariantCultureIgnoreCase, false)]
        [InlineData("Hello", "Goodbye", StringComparison.InvariantCultureIgnoreCase, false)]
        [InlineData("", "", StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("", "hello", StringComparison.InvariantCultureIgnoreCase, false)]
        [InlineData("Hello", "", StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("Hello", "ell" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true)]
        [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true)]
        // Ordinal
        [InlineData("Hello", "ello", StringComparison.Ordinal, true)]
        [InlineData("Hello", "ELL", StringComparison.Ordinal, false)]
        [InlineData("Hello", "ElLo", StringComparison.Ordinal, false)]
        [InlineData("Hello", "Larger Hello", StringComparison.Ordinal, false)]
        [InlineData("Hello", "Goodbye", StringComparison.Ordinal, false)]
        [InlineData("", "", StringComparison.Ordinal, true)]
        [InlineData("", "hello", StringComparison.Ordinal, false)]
        [InlineData("Hello", "", StringComparison.Ordinal, true)]
        [InlineData("Hello", "ell" + SoftHyphen, StringComparison.Ordinal, false)]
        [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.Ordinal, false)]
        // OrdinalIgnoreCase
        [InlineData("Hello", "ello", StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("Hello", "ELL", StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("Hello", "ElLo", StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("Hello", "Larger Hello", StringComparison.OrdinalIgnoreCase, false)]
        [InlineData("Hello", "Goodbye", StringComparison.OrdinalIgnoreCase, false)]
        [InlineData("", "", StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("", "hello", StringComparison.OrdinalIgnoreCase, false)]
        [InlineData("Hello", "", StringComparison.OrdinalIgnoreCase, true)]
        [InlineData("Hello", "ell" + SoftHyphen, StringComparison.OrdinalIgnoreCase, false)]
        [InlineData("Hello", "Ell" + SoftHyphen, StringComparison.OrdinalIgnoreCase, false)]
        public static void Contains(string s, string value, StringComparison comparisonType, bool expected)
        {
            Assert.Equal(expected, s.Contains(value, comparisonType));
            Assert.Equal(expected, s.AsSpan().Contains(value, comparisonType));
        }

        [Fact]
        public static void Contains_StringComparison_TurkishI()
        {
            string str = "\u0069\u0130";
            RemoteInvoke((source) =>
            {
                CultureInfo.CurrentCulture = new CultureInfo("tr-TR");

                Assert.True(source.Contains("\u0069\u0069", StringComparison.CurrentCultureIgnoreCase));
                Assert.True(source.AsSpan().Contains("\u0069\u0069", StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }, str).Dispose();

            RemoteInvoke((source) =>
            {
                CultureInfo.CurrentCulture = new CultureInfo("en-US");

                Assert.False(source.Contains("\u0069\u0069", StringComparison.CurrentCultureIgnoreCase));
                Assert.False(source.AsSpan().Contains("\u0069\u0069", StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }, str).Dispose();
        }

        [Fact]
        public static void Contains_Match_Char()
        {
            Assert.False("".Contains('a'));
            Assert.False("".AsSpan().Contains('a'));

            // Use a long-enough string to incur vectorization code
            const int max = 250;

            for (var length = 1; length < max; length++)
            {
                char[] ca = new char[length];
                for (int i = 0; i < length; i++)
                {
                    ca[i] = (char)(i + 1);
                }

                var span = new Span<char>(ca);
                var ros = new ReadOnlySpan<char>(ca);
                var str = new string(ca);

                for (var targetIndex = 0; targetIndex < length; targetIndex++)
                {
                    char target = ca[targetIndex];

                    // Span
                    bool found = span.Contains(target);
                    Assert.True(found);

                    // ReadOnlySpan
                    found = ros.Contains(target);
                    Assert.True(found);

                    // String
                    found = str.Contains(target);
                    Assert.True(found);
                }
            }
        }

        [Fact]
        public static void Contains_ZeroLength_Char()
        {
            // Span
            var span = new Span<char>(Array.Empty<char>());
            bool found = span.Contains((char)0);
            Assert.False(found);

            span = Span<char>.Empty;
            found = span.Contains((char)0);
            Assert.False(found);

            // ReadOnlySpan
            var ros = new ReadOnlySpan<char>(Array.Empty<char>());
            found = ros.Contains((char)0);
            Assert.False(found);

            ros = ReadOnlySpan<char>.Empty;
            found = ros.Contains((char)0);
            Assert.False(found);

            // String
            found = string.Empty.Contains((char)0);
            Assert.False(found);
        }

        [Fact]
        public static void Contains_MultipleMatches_Char()
        {
            for (int length = 2; length < 32; length++)
            {
                var ca = new char[length];
                for (int i = 0; i < length; i++)
                {
                    ca[i] = (char)(i + 1);
                }

                ca[length - 1] = (char)200;
                ca[length - 2] = (char)200;

                // Span
                var span = new Span<char>(ca);
                bool found = span.Contains((char)200);
                Assert.True(found);

                // ReadOnlySpan
                var ros = new ReadOnlySpan<char>(ca);
                found = ros.Contains((char)200);
                Assert.True(found);

                // String
                var str = new string(ca);
                found = str.Contains((char)200);
                Assert.True(found);
            }
        }

        [Fact]
        public static void Contains_EnsureNoChecksGoOutOfRange_Char()
        {
            for (int length = 0; length < 100; length++)
            {
                var ca = new char[length + 2];
                ca[0] = '9';
                ca[length + 1] = '9';

                // Span
                var span = new Span<char>(ca, 1, length);
                bool found = span.Contains('9');
                Assert.False(found);

                // ReadOnlySpan
                var ros = new ReadOnlySpan<char>(ca, 1, length);
                found = ros.Contains('9');
                Assert.False(found);

                // String
                var str = new string(ca, 1, length);
                found = str.Contains('9');
                Assert.False(found);
            }
        }

        [Theory]
        [InlineData(StringComparison.CurrentCulture)]
        [InlineData(StringComparison.CurrentCultureIgnoreCase)]
        [InlineData(StringComparison.InvariantCulture)]
        [InlineData(StringComparison.InvariantCultureIgnoreCase)]
        [InlineData(StringComparison.Ordinal)]
        [InlineData(StringComparison.OrdinalIgnoreCase)]
        public static void Contains_NullValue_ThrowsArgumentNullException(StringComparison comparisonType)
        {
            AssertExtensions.Throws<ArgumentNullException>("value", () => "foo".Contains(null, comparisonType));
        }

        [Theory]
        [InlineData(StringComparison.CurrentCulture - 1)]
        [InlineData(StringComparison.OrdinalIgnoreCase + 1)]
        public static void Contains_InvalidComparisonType_ThrowsArgumentOutOfRangeException(StringComparison comparisonType)
        {
            AssertExtensions.Throws<ArgumentException>("comparisonType", () => "ab".Contains("a", comparisonType));
        }

        [Theory]
        [InlineData("Hello", 'o', true)]
        [InlineData("Hello", 'O', false)]
        [InlineData("o", 'o', true)]
        [InlineData("o", 'O', false)]
        [InlineData("Hello", 'e', false)]
        [InlineData("Hello", '\0', false)]
        [InlineData("", '\0', false)]
        [InlineData("\0", '\0', true)]
        [InlineData("", 'a', false)]
        [InlineData("abcdefghijklmnopqrstuvwxyz", 'z', true)]
        public static void EndsWith(string s, char value, bool expected)
        {
            Assert.Equal(expected, s.EndsWith(value));
        }

        [Theory]
        [InlineData(new char[0], new int[0])] // empty
        [InlineData(new char[] { 'x', 'y', 'z' }, new int[] { 'x', 'y', 'z' })]
        [InlineData(new char[] { 'x', '\uD86D', '\uDF54', 'y' }, new int[] { 'x', 0x2B754, 'y' })] // valid surrogate pair
        [InlineData(new char[] { 'x', '\uD86D', 'y' }, new int[] { 'x', 0xFFFD, 'y' })] // standalone high surrogate
        [InlineData(new char[] { 'x', '\uDF54', 'y' }, new int[] { 'x', 0xFFFD, 'y' })] // standalone low surrogate
        [InlineData(new char[] { 'x', '\uD86D' }, new int[] { 'x', 0xFFFD })] // standalone high surrogate at end of string
        [InlineData(new char[] { 'x', '\uDF54' }, new int[] { 'x', 0xFFFD })] // standalone low surrogate at end of string
        [InlineData(new char[] { 'x', '\uD86D', '\uD86D', 'y' }, new int[] { 'x', 0xFFFD, 0xFFFD, 'y' })] // two high surrogates should be two replacement chars
        [InlineData(new char[] { 'x', '\uFFFD', 'y' }, new int[] { 'x', 0xFFFD, 'y' })] // literal U+FFFD
        public static void EnumerateRunes(char[] chars, int[] expected)
        {
            // Test data is smuggled as char[] instead of straight-up string since the test framework
            // doesn't like invalid UTF-16 literals.

            string asString = new string(chars);

            // First, use a straight-up foreach keyword to ensure pattern matching works as expected

            List<int> enumeratedScalarValues = new List<int>();
            foreach (Rune rune in asString.EnumerateRunes())
            {
                enumeratedScalarValues.Add(rune.Value);
            }
            Assert.Equal(expected, enumeratedScalarValues.ToArray());

            // Then use LINQ to ensure IEnumerator<...> works as expected

            int[] enumeratedValues = new string(chars).EnumerateRunes().Select(r => r.Value).ToArray();
            Assert.Equal(expected, enumeratedValues);
        }

        [Theory]
        [InlineData("Hello", 'H', true)]
        [InlineData("Hello", 'h', false)]
        [InlineData("H", 'H', true)]
        [InlineData("H", 'h', false)]
        [InlineData("Hello", 'e', false)]
        [InlineData("Hello", '\0', false)]
        [InlineData("", '\0', false)]
        [InlineData("\0", '\0', true)]
        [InlineData("", 'a', false)]
        [InlineData("abcdefghijklmnopqrstuvwxyz", 'a', true)]
        public static void StartsWith(string s, char value, bool expected)
        {
            Assert.Equal(expected, s.StartsWith(value));
        }

        public static IEnumerable<object[]> Join_Char_StringArray_TestData()
        {
            yield return new object[] { '|', new string[0], 0, 0, "" };
            yield return new object[] { '|', new string[] { "a" }, 0, 1, "a" };
            yield return new object[] { '|', new string[] { "a", "b", "c" }, 0, 3, "a|b|c" };
            yield return new object[] { '|', new string[] { "a", "b", "c" }, 0, 2, "a|b" };
            yield return new object[] { '|', new string[] { "a", "b", "c" }, 1, 1, "b" };
            yield return new object[] { '|', new string[] { "a", "b", "c" }, 1, 2, "b|c" };
            yield return new object[] { '|', new string[] { "a", "b", "c" }, 3, 0, "" };
            yield return new object[] { '|', new string[] { "a", "b", "c" }, 0, 0, "" };
            yield return new object[] { '|', new string[] { "", "", "" }, 0, 3, "||" };
            yield return new object[] { '|', new string[] { null, null, null }, 0, 3, "||" };
        }

        [Theory]
        [MemberData(nameof(Join_Char_StringArray_TestData))]
        public static void Join_Char_StringArray(char separator, string[] values, int startIndex, int count, string expected)
        {
            if (startIndex == 0 && count == values.Length)
            {
                Assert.Equal(expected, string.Join(separator, values));
                Assert.Equal(expected, string.Join(separator, (IEnumerable<string>)values));
                Assert.Equal(expected, string.Join(separator, (object[])values));
                Assert.Equal(expected, string.Join(separator, (IEnumerable<object>)values));
            }

            Assert.Equal(expected, string.Join(separator, values, startIndex, count));
            Assert.Equal(expected, string.Join(separator.ToString(), values, startIndex, count));
        }

        public static IEnumerable<object[]> Join_Char_ObjectArray_TestData()
        {
            yield return new object[] { '|', new object[0], "" };
            yield return new object[] { '|', new object[] { 1 }, "1" };
            yield return new object[] { '|', new object[] { 1, 2, 3 }, "1|2|3" };
            yield return new object[] { '|', new object[] { new ObjectWithNullToString(), 2, new ObjectWithNullToString() }, "|2|" };
            yield return new object[] { '|', new object[] { "1", null, "3" }, "1||3" };
            yield return new object[] { '|', new object[] { "", "", "" }, "||" };
            yield return new object[] { '|', new object[] { "", null, "" }, "||" };
            yield return new object[] { '|', new object[] { null, null, null }, "||" };
        }

        [Theory]
        [MemberData(nameof(Join_Char_ObjectArray_TestData))]
        public static void Join_Char_ObjectArray(char separator, object[] values, string expected)
        {
            Assert.Equal(expected, string.Join(separator, values));
            Assert.Equal(expected, string.Join(separator, (IEnumerable<object>)values));
        }

        [Fact]
        public static void Join_Char_NullValues_ThrowsArgumentNullException()
        {
            AssertExtensions.Throws<ArgumentNullException>("value", () => string.Join('|', (string[])null));
            AssertExtensions.Throws<ArgumentNullException>("value", () => string.Join('|', (string[])null, 0, 0));
            AssertExtensions.Throws<ArgumentNullException>("values", () => string.Join('|', (object[])null));
            AssertExtensions.Throws<ArgumentNullException>("values", () => string.Join('|', (IEnumerable<object>)null));
        }

        [Fact]
        public static void Join_Char_NegativeStartIndex_ThrowsArgumentOutOfRangeException()
        {
            AssertExtensions.Throws<ArgumentOutOfRangeException>("startIndex", () => string.Join('|', new string[] { "Foo" }, -1, 0));
        }

        [Fact]
        public static void Join_Char_NegativeCount_ThrowsArgumentOutOfRangeException()
        {
            AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => string.Join('|', new string[] { "Foo" }, 0, -1));
        }

        [Theory]
        [InlineData(2, 1)]
        [InlineData(2, 0)]
        [InlineData(1, 2)]
        [InlineData(1, 1)]
        [InlineData(0, 2)]
        [InlineData(-1, 0)]
        public static void Join_Char_InvalidStartIndexCount_ThrowsArgumentOutOfRangeException(int startIndex, int count)
        {
            AssertExtensions.Throws<ArgumentOutOfRangeException>("startIndex", () => string.Join('|', new string[] { "Foo" }, startIndex, count));
        }

        public static IEnumerable<object[]> Replace_StringComparison_TestData()
        {
            yield return new object[] { "abc", "abc", "def", StringComparison.CurrentCulture, "def" };
            yield return new object[] { "abc", "ABC", "def", StringComparison.CurrentCulture, "abc" };
            yield return new object[] { "abc", "abc", "", StringComparison.CurrentCulture, "" };
            yield return new object[] { "abc", "b", "LONG", StringComparison.CurrentCulture, "aLONGc" };
            yield return new object[] { "abc", "b", "d", StringComparison.CurrentCulture, "adc" };
            yield return new object[] { "abc", "b", null, StringComparison.CurrentCulture, "ac" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.CurrentCulture, "def" };

            yield return new object[] { "abc", "abc", "def", StringComparison.CurrentCultureIgnoreCase, "def" };
            yield return new object[] { "abc", "ABC", "def", StringComparison.CurrentCultureIgnoreCase, "def" };
            yield return new object[] { "abc", "abc", "", StringComparison.CurrentCultureIgnoreCase, "" };
            yield return new object[] { "abc", "b", "LONG", StringComparison.CurrentCultureIgnoreCase, "aLONGc" };
            yield return new object[] { "abc", "b", "d", StringComparison.CurrentCultureIgnoreCase, "adc" };
            yield return new object[] { "abc", "b", null, StringComparison.CurrentCultureIgnoreCase, "ac" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.CurrentCultureIgnoreCase, "def" };

            yield return new object[] { "abc", "abc", "def", StringComparison.Ordinal, "def" };
            yield return new object[] { "abc", "ABC", "def", StringComparison.Ordinal, "abc" };
            yield return new object[] { "abc", "abc", "", StringComparison.Ordinal, "" };
            yield return new object[] { "abc", "b", "LONG", StringComparison.Ordinal, "aLONGc" };
            yield return new object[] { "abc", "b", "d", StringComparison.Ordinal, "adc" };
            yield return new object[] { "abc", "b", null, StringComparison.Ordinal, "ac" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.Ordinal, "abc" };

            yield return new object[] { "abc", "abc", "def", StringComparison.OrdinalIgnoreCase, "def" };
            yield return new object[] { "abc", "ABC", "def", StringComparison.OrdinalIgnoreCase, "def" };
            yield return new object[] { "abc", "abc", "", StringComparison.OrdinalIgnoreCase, "" };
            yield return new object[] { "abc", "b", "LONG", StringComparison.OrdinalIgnoreCase, "aLONGc" };
            yield return new object[] { "abc", "b", "d", StringComparison.OrdinalIgnoreCase, "adc" };
            yield return new object[] { "abc", "b", null, StringComparison.OrdinalIgnoreCase, "ac" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.OrdinalIgnoreCase, "abc" };

            yield return new object[] { "abc", "abc", "def", StringComparison.InvariantCulture, "def" };
            yield return new object[] { "abc", "ABC", "def", StringComparison.InvariantCulture, "abc" };
            yield return new object[] { "abc", "abc", "", StringComparison.InvariantCulture, "" };
            yield return new object[] { "abc", "b", "LONG", StringComparison.InvariantCulture, "aLONGc" };
            yield return new object[] { "abc", "b", "d", StringComparison.InvariantCulture, "adc" };
            yield return new object[] { "abc", "b", null, StringComparison.InvariantCulture, "ac" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.InvariantCulture, "def" };

            yield return new object[] { "abc", "abc", "def", StringComparison.InvariantCultureIgnoreCase, "def" };
            yield return new object[] { "abc", "ABC", "def", StringComparison.InvariantCultureIgnoreCase, "def" };
            yield return new object[] { "abc", "abc", "", StringComparison.InvariantCultureIgnoreCase, "" };
            yield return new object[] { "abc", "b", "LONG", StringComparison.InvariantCultureIgnoreCase, "aLONGc" };
            yield return new object[] { "abc", "b", "d", StringComparison.InvariantCultureIgnoreCase, "adc" };
            yield return new object[] { "abc", "b", null, StringComparison.InvariantCultureIgnoreCase, "ac" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.InvariantCultureIgnoreCase, "def" };

            string turkishSource = "\u0069\u0130";

            yield return new object[] { turkishSource, "\u0069", "a", StringComparison.Ordinal, "a\u0130" };
            yield return new object[] { turkishSource, "\u0069", "a", StringComparison.OrdinalIgnoreCase, "a\u0130" };
            yield return new object[] { turkishSource, "\u0130", "a", StringComparison.Ordinal, "\u0069a" };
            yield return new object[] { turkishSource, "\u0130", "a", StringComparison.OrdinalIgnoreCase, "\u0069a" };

            yield return new object[] { turkishSource, "\u0069", "a", StringComparison.InvariantCulture, "a\u0130" };
            yield return new object[] { turkishSource, "\u0069", "a", StringComparison.InvariantCultureIgnoreCase, "a\u0130" };
            yield return new object[] { turkishSource, "\u0130", "a", StringComparison.InvariantCulture, "\u0069a" };
            yield return new object[] { turkishSource, "\u0130", "a", StringComparison.InvariantCultureIgnoreCase, "\u0069a" };
        }

        [Theory]
        [MemberData(nameof(Replace_StringComparison_TestData))]
        public void Replace_StringComparison_ReturnsExpected(string original, string oldValue, string newValue, StringComparison comparisonType, string expected)
        {
            Assert.Equal(expected, original.Replace(oldValue, newValue, comparisonType));
        }

        [Fact]
        public void Replace_StringComparison_TurkishI()
        {
            string src = "\u0069\u0130";

            RemoteInvoke((source) =>
            {
                CultureInfo.CurrentCulture = new CultureInfo("tr-TR");

                Assert.True("\u0069".Equals("\u0130", StringComparison.CurrentCultureIgnoreCase));

                Assert.Equal("a\u0130", source.Replace("\u0069", "a", StringComparison.CurrentCulture));
                Assert.Equal("aa", source.Replace("\u0069", "a", StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal("\u0069a", source.Replace("\u0130", "a", StringComparison.CurrentCulture));
                Assert.Equal("aa", source.Replace("\u0130", "a", StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }, src).Dispose();

            RemoteInvoke((source) =>
            {
                CultureInfo.CurrentCulture = new CultureInfo("en-US");

                Assert.False("\u0069".Equals("\u0130", StringComparison.CurrentCultureIgnoreCase));

                Assert.Equal("a\u0130", source.Replace("\u0069", "a", StringComparison.CurrentCulture));
                Assert.Equal("a\u0130", source.Replace("\u0069", "a", StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal("\u0069a", source.Replace("\u0130", "a", StringComparison.CurrentCulture));
                Assert.Equal("\u0069a", source.Replace("\u0130", "a", StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }, src).Dispose();
        }

        public static IEnumerable<object[]> Replace_StringComparisonCulture_TestData()
        {
            yield return new object[] { "abc", "abc", "def", false, null, "def" };
            yield return new object[] { "abc", "ABC", "def", false, null, "abc" };
            yield return new object[] { "abc", "abc", "def", false, CultureInfo.InvariantCulture, "def" };
            yield return new object[] { "abc", "ABC", "def", false, CultureInfo.InvariantCulture, "abc" };

            yield return new object[] { "abc", "abc", "def", true, null, "def" };
            yield return new object[] { "abc", "ABC", "def", true, null, "def" };
            yield return new object[] { "abc", "abc", "def", true, CultureInfo.InvariantCulture, "def" };
            yield return new object[] { "abc", "ABC", "def", true, CultureInfo.InvariantCulture, "def" };

            yield return new object[] { "abc", "abc" + SoftHyphen, "def", false, null, "def" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", true, null, "def" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", false, CultureInfo.InvariantCulture, "def" };
            yield return new object[] { "abc", "abc" + SoftHyphen, "def", true, CultureInfo.InvariantCulture, "def" };

            yield return new object[] { "\u0069\u0130", "\u0069", "a", false, new CultureInfo("tr-TR"), "a\u0130" };
            yield return new object[] { "\u0069\u0130", "\u0069", "a", true, new CultureInfo("tr-TR"), "aa" };
            yield return new object[] { "\u0069\u0130", "\u0069", "a", false, CultureInfo.InvariantCulture, "a\u0130" };
            yield return new object[] { "\u0069\u0130", "\u0069", "a", true, CultureInfo.InvariantCulture, "a\u0130" };
        }

        [Theory]
        [MemberData(nameof(Replace_StringComparisonCulture_TestData))]
        public void Replace_StringComparisonCulture_ReturnsExpected(string original, string oldValue, string newValue, bool ignoreCase, CultureInfo culture, string expected)
        {
            Assert.Equal(expected, original.Replace(oldValue, newValue, ignoreCase, culture));
            if (culture == null)
            {
                Assert.Equal(expected, original.Replace(oldValue, newValue, ignoreCase, CultureInfo.CurrentCulture));
            }
        }

        [Fact]
        public void Replace_StringComparison_NullOldValue_ThrowsArgumentException()
        {
            AssertExtensions.Throws<ArgumentNullException>("oldValue", () => "abc".Replace(null, "def", StringComparison.CurrentCulture));
            AssertExtensions.Throws<ArgumentNullException>("oldValue", () => "abc".Replace(null, "def", true, CultureInfo.CurrentCulture));
        }

        [Fact]
        public void Replace_StringComparison_EmptyOldValue_ThrowsArgumentException()
        {
            AssertExtensions.Throws<ArgumentException>("oldValue", () => "abc".Replace("", "def", StringComparison.CurrentCulture));
            AssertExtensions.Throws<ArgumentException>("oldValue", () => "abc".Replace("", "def", true, CultureInfo.CurrentCulture));
        }

        [Theory]
        [InlineData(StringComparison.CurrentCulture - 1)]
        [InlineData(StringComparison.OrdinalIgnoreCase + 1)]
        public void Replace_NoSuchStringComparison_ThrowsArgumentException(StringComparison comparisonType)
        {
            AssertExtensions.Throws<ArgumentException>("comparisonType", () => "abc".Replace("abc", "def", comparisonType));
        }

        private static readonly StringComparison[] StringComparisons = (StringComparison[])Enum.GetValues(typeof(StringComparison));

        [Fact]
        public static void GetHashCode_OfSpan_EmbeddedNull_ReturnsDifferentHashCodes()
        {
            Assert.NotEqual(string.GetHashCode("\0AAAAAAAAA".AsSpan()), string.GetHashCode("\0BBBBBBBBBBBB".AsSpan()));
        }

        [Fact]
        public static void GetHashCode_OfSpan_MatchesOfString()
        {
            // parameterless should be ordinal only
            Assert.Equal("abc".GetHashCode(), string.GetHashCode("abc".AsSpan()));
            Assert.NotEqual("abc".GetHashCode(), string.GetHashCode("ABC".AsSpan())); // case differences
        }

        [Fact]
        public static void GetHashCode_CompareInfo()
        {
            // ordinal
            Assert.Equal("abc".GetHashCode(), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("abc", CompareOptions.Ordinal));
            Assert.NotEqual("abc".GetHashCode(), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("ABC", CompareOptions.Ordinal));

            // ordinal ignore case
            Assert.Equal("abc".GetHashCode(StringComparison.OrdinalIgnoreCase), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("abc", CompareOptions.OrdinalIgnoreCase));
            Assert.Equal("abc".GetHashCode(StringComparison.OrdinalIgnoreCase), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("ABC", CompareOptions.OrdinalIgnoreCase));

            // culture-aware
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.CurrentCulture), CultureInfo.CurrentCulture.CompareInfo.GetHashCode("aeiXXabc", CompareOptions.None));
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.CurrentCultureIgnoreCase), CultureInfo.CurrentCulture.CompareInfo.GetHashCode("aeiXXabc", CompareOptions.IgnoreCase));

            // invariant culture
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.InvariantCulture), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("aeiXXabc", CompareOptions.None));
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.InvariantCultureIgnoreCase), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("aeiXXabc", CompareOptions.IgnoreCase));
        }

        [Fact]
        public static void GetHashCode_CompareInfo_OfSpan()
        {
            // ordinal
            Assert.Equal("abc".GetHashCode(), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("abc".AsSpan(), CompareOptions.Ordinal));
            Assert.NotEqual("abc".GetHashCode(), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("ABC".AsSpan(), CompareOptions.Ordinal));

            // ordinal ignore case
            Assert.Equal("abc".GetHashCode(StringComparison.OrdinalIgnoreCase), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("abc".AsSpan(), CompareOptions.OrdinalIgnoreCase));
            Assert.Equal("abc".GetHashCode(StringComparison.OrdinalIgnoreCase), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("ABC".AsSpan(), CompareOptions.OrdinalIgnoreCase));

            // culture-aware
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.CurrentCulture), CultureInfo.CurrentCulture.CompareInfo.GetHashCode("aeiXXabc".AsSpan(), CompareOptions.None));
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.CurrentCultureIgnoreCase), CultureInfo.CurrentCulture.CompareInfo.GetHashCode("aeiXXabc".AsSpan(), CompareOptions.IgnoreCase));

            // invariant culture
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.InvariantCulture), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("aeiXXabc".AsSpan(), CompareOptions.None));
            Assert.Equal("aeiXXabc".GetHashCode(StringComparison.InvariantCultureIgnoreCase), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("aeiXXabc".AsSpan(), CompareOptions.IgnoreCase));
        }

        public static IEnumerable<object[]> GetHashCode_StringComparison_Data => StringComparisons.Select(value => new object[] { value });

        [Theory]
        [MemberData(nameof(GetHashCode_StringComparison_Data))]
        public static void GetHashCode_StringComparison(StringComparison comparisonType)
        {
            int hashCodeFromStringComparer = StringComparer.FromComparison(comparisonType).GetHashCode("abc");
            int hashCodeFromStringGetHashCode = "abc".GetHashCode(comparisonType);
            int hashCodeFromStringGetHashCodeOfSpan = string.GetHashCode("abc".AsSpan(), comparisonType);

            Assert.Equal(hashCodeFromStringComparer, hashCodeFromStringGetHashCode);
            Assert.Equal(hashCodeFromStringComparer, hashCodeFromStringGetHashCodeOfSpan);
        }

        public static IEnumerable<object[]> GetHashCode_NoSuchStringComparison_ThrowsArgumentException_Data => new[]
        {
            new object[] { StringComparisons.Min() - 1 },
            new object[] { StringComparisons.Max() + 1 },
        };

        [Theory]
        [MemberData(nameof(GetHashCode_NoSuchStringComparison_ThrowsArgumentException_Data))]
        public static void GetHashCode_NoSuchStringComparison_ThrowsArgumentException(StringComparison comparisonType)
        {
            AssertExtensions.Throws<ArgumentException>("comparisonType", () => "abc".GetHashCode(comparisonType));
            AssertExtensions.Throws<ArgumentException>("comparisonType", () => string.GetHashCode("abc".AsSpan(), comparisonType));
        }

        [Theory]
        [InlineData("")]
        [InlineData("a")]
        [InlineData("\0")]
        [InlineData("abc")]
        public static unsafe void ImplicitCast_ResultingSpanMatches(string s)
        {
            ReadOnlySpan<char> span = s;
            Assert.Equal(s.Length, span.Length);
            fixed (char* stringPtr = s)
            fixed (char* spanPtr = &MemoryMarshal.GetReference(span))
            {
                Assert.Equal((IntPtr)stringPtr, (IntPtr)spanPtr);
            }
        }

        [Fact]
        public static void ImplicitCast_NullString_ReturnsDefaultSpan()
        {
            ReadOnlySpan<char> span = (string)null;
            Assert.True(span == default);
        }

        [Theory]
        [InlineData("Hello", 'l', StringComparison.Ordinal, 2)]
        [InlineData("Hello", 'x', StringComparison.Ordinal, -1)]
        [InlineData("Hello", 'h', StringComparison.Ordinal, -1)]
        [InlineData("Hello", 'o', StringComparison.Ordinal, 4)]
        [InlineData("Hello", 'h', StringComparison.OrdinalIgnoreCase, 0)]
        [InlineData("HelLo", 'L', StringComparison.OrdinalIgnoreCase, 2)]
        [InlineData("HelLo", 'L', StringComparison.Ordinal, 3)]
        [InlineData("HelLo", '\0', StringComparison.Ordinal, -1)]
        [InlineData("!@#$%", '%', StringComparison.Ordinal, 4)]
        [InlineData("!@#$", '!', StringComparison.Ordinal, 0)]
        [InlineData("!@#$", '@', StringComparison.Ordinal, 1)]
        [InlineData("!@#$%", '%', StringComparison.OrdinalIgnoreCase, 4)]
        [InlineData("!@#$", '!', StringComparison.OrdinalIgnoreCase, 0)]
        [InlineData("!@#$", '@', StringComparison.OrdinalIgnoreCase, 1)]
        [InlineData("_____________\u807f", '\u007f', StringComparison.Ordinal, -1)]
        [InlineData("_____________\u807f__", '\u007f', StringComparison.Ordinal, -1)]
        [InlineData("_____________\u807f\u007f_", '\u007f', StringComparison.Ordinal, 14)]
        [InlineData("__\u807f_______________", '\u007f', StringComparison.Ordinal, -1)]
        [InlineData("__\u807f___\u007f___________", '\u007f', StringComparison.Ordinal, 6)]
        [InlineData("_____________\u807f", '\u007f', StringComparison.OrdinalIgnoreCase, -1)]
        [InlineData("_____________\u807f__", '\u007f', StringComparison.OrdinalIgnoreCase, -1)]
        [InlineData("_____________\u807f\u007f_", '\u007f', StringComparison.OrdinalIgnoreCase, 14)]
        [InlineData("__\u807f_______________", '\u007f', StringComparison.OrdinalIgnoreCase, -1)]
        [InlineData("__\u807f___\u007f___________", '\u007f', StringComparison.OrdinalIgnoreCase, 6)]
        public static void IndexOf_SingleLetter(string s, char target, StringComparison stringComparison, int expected)
        {
            Assert.Equal(expected, s.IndexOf(target, stringComparison));
            var charArray = new char[1];
            charArray[0] = target;
            Assert.Equal(expected, s.AsSpan().IndexOf(charArray, stringComparison));
        }

        [Fact]
        public static void IndexOf_TurkishI_TurkishCulture_Char()
        {
            RemoteInvoke(() =>
            {
                CultureInfo.CurrentCulture = new CultureInfo("tr-TR");

                string s = "Turkish I \u0131s TROUBL\u0130NG!";
                char value = '\u0130';
                Assert.Equal(19, s.IndexOf(value));
                Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal(19, s.IndexOf(value, StringComparison.Ordinal));
                Assert.Equal(19, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));

                ReadOnlySpan<char> span = s.AsSpan();
                Assert.Equal(19, span.IndexOf(new char[] { value }, StringComparison.CurrentCulture));
                Assert.Equal(4, span.IndexOf(new char[] { value }, StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal(19, span.IndexOf(new char[] { value }, StringComparison.Ordinal));
                Assert.Equal(19, span.IndexOf(new char[] { value }, StringComparison.OrdinalIgnoreCase));

                value = '\u0131';
                Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal));
                Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));

                Assert.Equal(10, span.IndexOf(new char[] { value }, StringComparison.CurrentCulture));
                Assert.Equal(8, span.IndexOf(new char[] { value }, StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal(10, span.IndexOf(new char[] { value }, StringComparison.Ordinal));
                Assert.Equal(10, span.IndexOf(new char[] { value }, StringComparison.OrdinalIgnoreCase));

                return SuccessExitCode;
            }).Dispose();
        }

        [Fact]
        public static void IndexOf_TurkishI_InvariantCulture_Char()
        {
            RemoteInvoke(() =>
            {
                CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

                string s = "Turkish I \u0131s TROUBL\u0130NG!";
                char value = '\u0130';

                Assert.Equal(19, s.IndexOf(value));
                Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));

                value = '\u0131';
                Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }).Dispose();
        }

        [Fact]
        public static void IndexOf_TurkishI_EnglishUSCulture_Char()
        {
            RemoteInvoke(() =>
            {
                CultureInfo.CurrentCulture = new CultureInfo("en-US");

                string s = "Turkish I \u0131s TROUBL\u0130NG!";
                char value = '\u0130';

                Assert.Equal(19, s.IndexOf(value));
                Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));

                value = '\u0131';
                Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }).Dispose();
        }

        [Fact]
        public static void IndexOf_EquivalentDiacritics_EnglishUSCulture_Char()
        {
            RemoteInvoke(() =>
            {
                string s = "Exhibit a\u0300\u00C0";
                char value = '\u00C0';

                CultureInfo.CurrentCulture = new CultureInfo("en-US");
                Assert.Equal(10, s.IndexOf(value));
                Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal));
                Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));

                return SuccessExitCode;
            }).Dispose();
        }

        [Fact]
        public static void IndexOf_EquivalentDiacritics_InvariantCulture_Char()
        {
            RemoteInvoke(() =>
            {
                string s = "Exhibit a\u0300\u00C0";
                char value = '\u00C0';

                CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
                Assert.Equal(10, s.IndexOf(value));
                Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }).Dispose();
        }

        [Fact]
        public static void IndexOf_CyrillicE_EnglishUSCulture_Char()
        {
            RemoteInvoke(() =>
            {
                string s = "Foo\u0400Bar";
                char value = '\u0400';

                CultureInfo.CurrentCulture = new CultureInfo("en-US");
                Assert.Equal(3, s.IndexOf(value));
                Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
                Assert.Equal(3, s.IndexOf(value, StringComparison.Ordinal));
                Assert.Equal(3, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));

                return SuccessExitCode;
            }).Dispose();
        }

        [Fact]
        public static void IndexOf_CyrillicE_InvariantCulture_Char()
        {
            RemoteInvoke(() =>
            {
                string s = "Foo\u0400Bar";
                char value = '\u0400';

                CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
                Assert.Equal(3, s.IndexOf(value));
                Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture));
                Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));

                return SuccessExitCode;
            }).Dispose();
        }

        [Fact]
        public static void IndexOf_Invalid_Char()
        {
            // Invalid comparison type
            AssertExtensions.Throws<ArgumentException>("comparisonType", () => "foo".IndexOf('o', StringComparison.CurrentCulture - 1));
            AssertExtensions.Throws<ArgumentException>("comparisonType", () => "foo".IndexOf('o', StringComparison.OrdinalIgnoreCase + 1));
        }

        [Theory]
        [MemberData(nameof(Concat_Strings_2_3_4_TestData))]
        public static void Concat_Spans(string[] values, string expected)
        {
            Assert.InRange(values.Length, 2, 4);

            string result =
                values.Length == 2 ? string.Concat(values[0].AsSpan(), values[1].AsSpan()) :
                values.Length == 3 ? string.Concat(values[0].AsSpan(), values[1].AsSpan(), values[2].AsSpan()) :
                string.Concat(values[0].AsSpan(), values[1].AsSpan(), values[2].AsSpan(), values[3].AsSpan());

            if (result.Length == 0)
            {
                Assert.Same(string.Empty, result);
            }

            Assert.Equal(expected, result);
        }

        // [Fact]
        // public static void IndexerUsingIndexTest()
        // {
        //     Index index;
        //     string s = "0123456789ABCDEF";

        //     for (int i = 0; i < s.Length; i++)
        //     {
        //         index = Index.FromStart(i);
        //         Assert.Equal(s[i], s[index]);

        //         index = Index.FromEnd(i + 1);
        //         Assert.Equal(s[s.Length - i - 1], s[index]);
        //     }

        //     index = Index.FromStart(s.Length + 1);
        //     char c;
        //     Assert.Throws<IndexOutOfRangeException>(() => c = s[index]);

        //     index = Index.FromEnd(s.Length + 1);
        //     Assert.Throws<IndexOutOfRangeException>(() => c = s[index]);
        // }

        // [Fact]
        // public static void IndexerUsingRangeTest()
        // {
        //     Range range;
        //     string s = "0123456789ABCDEF";

        //     for (int i = 0; i < s.Length; i++)
        //     {
        //         range = new Range(Index.FromStart(0), Index.FromStart(i));
        //         Assert.Equal(s.Substring(0, i), s[range]);

        //         range = new Range(Index.FromEnd(s.Length), Index.FromEnd(i));
        //         Assert.Equal(s.Substring(0, s.Length - i), s[range]);
        //     }

        //     range = new Range(Index.FromStart(s.Length - 2), Index.FromStart(s.Length + 1));
        //     string s1;
        //     Assert.Throws<ArgumentOutOfRangeException>(() => s1 = s[range]);

        //     range = new Range(Index.FromEnd(s.Length + 1), Index.FromEnd(0));
        //     Assert.Throws<ArgumentOutOfRangeException>(() => s1 = s[range]);
        // }

        [Fact]
        public static void SubstringUsingIndexTest()
        {
            string s = "0123456789ABCDEF";

            for (int i = 0; i < s.Length; i++)
            {
                Assert.Equal(s.Substring(i), s.Substring(Index.FromStart(i)));
                Assert.Equal(s.Substring(s.Length - i - 1), s.Substring(Index.FromEnd(i + 1)));
            }

            // String.Substring allows the string length as a valid input.
            Assert.Equal(s.Substring(s.Length), s.Substring(Index.FromStart(s.Length)));

            Assert.Throws<ArgumentOutOfRangeException>(() => s.Substring(Index.FromStart(s.Length + 1)));
            Assert.Throws<ArgumentOutOfRangeException>(() => s.Substring(Index.FromEnd(s.Length + 1)));
        }

        [Fact]
        public static void SubstringUsingRangeTest()
        {
            string s = "0123456789ABCDEF";
            Range range;

            for (int i = 0; i < s.Length; i++)
            {
                range = new Range(Index.FromStart(0), Index.FromStart(i));
                Assert.Equal(s.Substring(0, i), s.Substring(range));

                range = new Range(Index.FromEnd(s.Length), Index.FromEnd(i));
                Assert.Equal(s.Substring(0, s.Length - i), s.Substring(range));
            }

            range = new Range(Index.FromStart(s.Length - 2), Index.FromStart(s.Length + 1));
            string s1;
            Assert.Throws<ArgumentOutOfRangeException>(() => s1 = s.Substring(range));

            range = new Range(Index.FromEnd(s.Length + 1), Index.FromEnd(0));
            Assert.Throws<ArgumentOutOfRangeException>(() => s1 = s.Substring(range));
        }

        /// <summary>
        /// Returns true only if U+0020 SPACE is represented as the single byte 0x20 in the active code page.
        /// </summary>
        public unsafe static bool IsSimpleActiveCodePage
        {
            get
            {
                IntPtr pAnsiStr = IntPtr.Zero;
                try
                {
                    pAnsiStr = Marshal.StringToHGlobalAnsi(" ");
                    return ((byte*)pAnsiStr)[0] == (byte)' ' && ((byte*)pAnsiStr)[1] == (byte)'\0';
                }
                finally
                {
                    if (pAnsiStr != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(pAnsiStr);
                    }
                }
            }
        }
    }
}


text:
module Propensity
  module Preferences
    module PreferableClassMethods

      def preference(name, type, *args)
        options = args.extract_options!
        options.assert_valid_keys(:default, :description)
        default = options[:default]
        description = options[:description] || name

        # cache_key will be nil for new objects, then if we check if there
        # is a pending preference before going to default
        define_method preference_getter_method(name) do
          if preference_cache_key(name) && preference_store.exist?(preference_cache_key(name))
            preference_store.get preference_cache_key(name)
          else
            if get_pending_preference(name)
              get_pending_preference(name)
            else
              send self.class.preference_default_getter_method(name)
            end
          end
        end
        alias_method prefers_getter_method(name), preference_getter_method(name)

        define_method preference_setter_method(name) do |value|
          value = convert_preference_value(value, type)
          if preference_cache_key(name)
            preference_store.set preference_cache_key(name), value, type
          else
            add_pending_preference(name, value)
          end
        end
        alias_method prefers_setter_method(name), preference_setter_method(name)

        define_method preference_default_getter_method(name) do
          default
        end

        define_method preference_type_getter_method(name) do
          type
        end

        define_method preference_description_getter_method(name) do
          description
        end
      end

      def remove_preference(name)
        remove_method preference_getter_method(name) if method_defined? preference_getter_method(name)
        remove_method preference_setter_method(name) if method_defined? preference_setter_method(name)
        remove_method prefers_getter_method(name) if method_defined? prefers_getter_method(name)
        remove_method prefers_setter_method(name) if method_defined? prefers_setter_method(name)
        remove_method preference_default_getter_method(name) if method_defined? preference_default_getter_method(name)
        remove_method preference_type_getter_method(name) if method_defined? preference_type_getter_method(name)
        remove_method preference_description_getter_method(name) if method_defined? preference_description_getter_method(name)
      end

      def preference_getter_method(name)
        "preferred_#{name}".to_sym
      end

      def preference_setter_method(name)
         "preferred_#{name}=".to_sym
      end

      def prefers_getter_method(name)
        "prefers_#{name}?".to_sym
      end

      def prefers_setter_method(name)
         "prefers_#{name}=".to_sym
      end

      def preference_default_getter_method(name)
        "preferred_#{name}_default".to_sym
      end

      def preference_type_getter_method(name)
        "preferred_#{name}_type".to_sym
      end

      def preference_description_getter_method(name)
        "preferred_#{name}_description".to_sym
      end

    end
  end
end

redpajama_set_name: RedPajamaGithub


text:
Week04: Classroom Exercises
===========================

This directory contains materials for use during the Hands-On (Thursday)
session in Week 04 of the IDEaS Summer 2017 Skills Workshop.


text:
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2013 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mapdemo" >

    <!--
         The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
         Google Maps Android API v2, but you must specify either coarse or fine
         location permissions for the 'MyLocation' functionality.
    -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- EXTERNAL_STORAGE permissions are optional for Android 6.0 onwards. -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="22" />
    <uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="22" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/demo_title"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >

        <!--
             The API key for Google Maps-based APIs is defined as a string resource.
             (See the file "res/values/google_maps_api.xml").
             Note that the API key is linked to the encryption key used to sign the APK.
             You need a different API key for each encryption key, including the release key that is used to
             sign the APK for publishing.
             You can define the keys for the debug and release targets in src/debug/ and src/release/.
        -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".LegalInfoActivity"
            android:label="@string/legal_info"/>
        <activity
            android:name=".BasicMapDemoActivity"
            android:label="@string/basic_map_demo_label"/>
        <activity
            android:name=".CameraDemoActivity"
            android:label="@string/camera_demo_label"/>
        <activity
            android:name=".CircleDemoActivity"
            android:label="@string/circle_demo_label"/>
        <activity
            android:name=".EventsDemoActivity"
            android:label="@string/events_demo_label"/>
        <activity
            android:name=".GroundOverlayDemoActivity"
            android:label="@string/ground_overlay_demo_label"/>
        <activity
            android:name=".IndoorDemoActivity"
            android:label="@string/indoor_demo_label"/>
        <activity
            android:name=".LayersDemoActivity"
            android:label="@string/layers_demo_label"/>
        <activity
            android:name=".LiteDemoActivity"
            android:label="@string/lite_demo_label"/>
        <activity
            android:name=".LiteListDemoActivity"
            android:label="@string/lite_list_demo_label"/>
        <activity
            android:name=".LocationSourceDemoActivity"
            android:label="@string/location_source_demo_label"/>
        <activity
            android:name=".MapInPagerDemoActivity"
            android:label="@string/map_in_pager_demo_label"/>
        <activity
            android:name=".MarkerDemoActivity"
            android:label="@string/marker_demo_label"/>
        <activity
            android:name=".MultiMapDemoActivity"
            android:label="@string/multi_map_demo_label"/>
        <activity
            android:name=".MyLocationDemoActivity"
            android:label="@string/my_location_demo_label"/>
        <activity
            android:name=".OptionsDemoActivity"
            android:label="@string/options_demo_label"/>
        <activity
            android:name=".PolygonDemoActivity"
            android:label="@string/polygon_demo_label"/>
        <activity
            android:name=".PolylineDemoActivity"
            android:label="@string/polyline_demo_label"/>
        <activity
            android:name=".ProgrammaticDemoActivity"
            android:label="@string/programmatic_demo_label"/>
        <activity
            android:name=".RawMapViewDemoActivity"
            android:label="@string/raw_map_view_demo_label"/>
        <activity
            android:name=".RetainMapDemoActivity"
            android:label="@string/retain_map_demo_label"/>
        <activity
            android:name=".SaveStateDemoActivity"
            android:label="@string/save_state_demo_label"/>
        <activity
            android:name=".SnapshotDemoActivity"
            android:label="@string/snapshot_demo_label"/>
        <activity
            android:name=".SplitStreetViewPanoramaAndMapDemoActivity"
            android:label="@string/split_street_view_panorama_and_map_demo_label"/>
        <activity
            android:name=".StreetViewPanoramaBasicDemoActivity"
            android:label="@string/street_view_panorama_basic_demo_label"/>
        <activity
            android:name=".StreetViewPanoramaEventsDemoActivity"
            android:label="@string/street_view_panorama_events_demo_label"/>
        <activity
            android:name=".StreetViewPanoramaNavigationDemoActivity"
            android:label="@string/street_view_panorama_navigation_demo_label"/>
        <activity
            android:name=".StreetViewPanoramaOptionsDemoActivity"
            android:label="@string/street_view_panorama_options_demo_label"/>
        <activity
            android:name=".StreetViewPanoramaViewDemoActivity"
            android:label="@string/street_view_panorama_view_demo_label"/>
        <activity
            android:name=".TileCoordinateDemoActivity"
            android:label="@string/tile_coordinate_demo_label"/>
        <activity
            android:name=".TileOverlayDemoActivity"
            android:label="@string/tile_overlay_demo_label"/>
        <activity
            android:name=".UiSettingsDemoActivity"
            android:label="@string/ui_settings_demo_label"/>
        <activity
            android:name=".VisibleRegionDemoActivity"
            android:label="@string/visible_region_demo_label"/>

    </application>

</manifest>


text:
FOUNDATION_EXPORT double Pods_CocoaAsyncSocketDemoUITestsVersionNumber;
FOUNDATION_EXPORT const unsigned char Pods_CocoaAsyncSocketDemoUITestsVersionString[];


text:
#include <aws/workspaces/model/RebuildWorkspacesResult.h>
#include <aws/core/utils/json/JsonSerializer.h>
#include <aws/core/AmazonWebServiceResult.h>
#include <aws/core/utils/UnreferencedParam.h>

#include <utility>

using namespace Aws::WorkSpaces::Model;
using namespace Aws::Utils::Json;
using namespace Aws::Utils;
using namespace Aws;

RebuildWorkspacesResult::RebuildWorkspacesResult()
{
}

RebuildWorkspacesResult::RebuildWorkspacesResult(const AmazonWebServiceResult<JsonValue>& result)
{
  *this = result;
}

RebuildWorkspacesResult& RebuildWorkspacesResult::operator =(const AmazonWebServiceResult<JsonValue>& result)
{
  const JsonValue& jsonValue = result.GetPayload();
  if(jsonValue.ValueExists("FailedRequests"))
  {
    Array<JsonValue> failedRequestsJsonList = jsonValue.GetArray("FailedRequests");
    for(unsigned failedRequestsIndex = 0; failedRequestsIndex < failedRequestsJsonList.GetLength(); ++failedRequestsIndex)
    {
      m_failedRequests.push_back(failedRequestsJsonList[failedRequestsIndex].AsObject());
    }
  }

  return *this;
}


text:
<!DOCTYPE HTML>

		<meta http-equiv="Content-Type" content="html/html; charset=utf-8" />
		<title>BITUpdateManager Class Reference</title>
		<meta id="xcode-display" name="xcode-display" content="render"/>
		<meta name="viewport" content="width=550" />
		<link rel="stylesheet" type="text/css" href="../css/styles.css" media="all" />
		<link rel="stylesheet" type="text/css" media="print" href="../css/stylesPrint.css" />
		<meta name="generator" content="appledoc 2.1 (build 858)" />

HockeySDK-iOS 3.0.0

				BitStadium GmbH

BITUpdateManager Class Reference

					<button aria-label="Show Table of Contents" role="checkbox" class="open" id="table_of_contents">Table of Contents</button>

					<select id="jumpTo">
	<option value="top">Jump To…</option>

	<option value="overview">Overview</option>

	<option value="tasks">Tasks</option>

	<option value="properties">Properties</option>

	<option value="//api/name/alwaysShowUpdateReminder">    alwaysShowUpdateReminder</option>

	<option value="//api/name/authenticationSecret">    authenticationSecret</option>

	<option value="//api/name/checkForUpdateOnLaunch">    checkForUpdateOnLaunch</option>

	<option value="//api/name/delegate">    delegate</option>

	<option value="//api/name/expiryDate">    expiryDate</option>

	<option value="//api/name/requireAuthorization">    requireAuthorization</option>

	<option value="//api/name/showDirectInstallOption">    showDirectInstallOption</option>

	<option value="//api/name/updateSetting">    updateSetting</option>

	<option value="instance_methods">Instance Methods</option>

	<option value="//api/name/checkForUpdate">    - checkForUpdate</option>

	<option value="//api/name/hockeyViewController:">    - hockeyViewController:</option>

	<option value="//api/name/showUpdateView">    - showUpdateView</option>

</select>

Overview

Tasks

Delegate

Update Checking

Update Notification

Authorization

Expiry

User Interface

Properties

alwaysShowUpdateReminder

authenticationSecret

checkForUpdateOnLaunch

delegate

expiryDate

requireAuthorization

showDirectInstallOption

updateSetting

Instance Methods

checkForUpdate

hockeyViewController:

showUpdateView

Index

Hierarchy

BITUpdateManager Class Reference

	Inherits from
	BITHockeyBaseManager : NSObject

	Conforms to
	UIAlertViewDelegate

	Declared in
	BITUpdateManager.h

Overview

The update manager module.

This is the HockeySDK module for handling app updates when using Ad-Hoc or Enterprise provisioning profiles.
This modul handles version updates, presents update and version information in a App Store like user interface,
collects usage information and provides additional authorization options when using Ad-Hoc provisioning profiles.

This module automatically disables itself when running in an App Store build by default! If you integrate the
Atlassian JMC client this module is used to automatically configure JMC, but will not do anything else.

To use this module, it is important to implement set the delegate property and implement
[BITUpdateManagerDelegate customDeviceIdentifierForUpdateManager:].

Example implementation if your Xcode configuration for the App Store is called “AppStore”:

- (NSString *)customDeviceIdentifierForUpdateManager:(BITUpdateManager *)updateManager {
#ifndef (CONFIGURATION_AppStore)
  if ([[UIDevice currentDevice] respondsToSelector:@selector(uniqueIdentifier)])
    return [[UIDevice currentDevice] performSelector:@selector(uniqueIdentifier)];
#endif

  return nil;
}

[[BITHockeyManager sharedHockeyManager].updateManager setDelegate:self];

Tasks

Delegate

		  delegate

Sets the BITUpdateManagerDelegate delegate.

	property

Update Checking

		  updateSetting

When to check for new updates.

	property

		  checkForUpdateOnLaunch

Flag that determines whether the automatic update checks should be done.

	property

		– checkForUpdate

Check for an update

Update Notification

		  alwaysShowUpdateReminder

Flag that determines if updates alert should be repeatedly shown

	property

		  showDirectInstallOption

Flag that determines if the update alert should show an direct install option

	property

Authorization

		  requireAuthorization

Flag that determines if each update should be authenticated

	property

		  authenticationSecret

The authentication token from HockeyApp.

	property

Expiry

		  expiryDate

Expiry date of the current app version

	property

User Interface

		– showUpdateView

Present the modal update user interface.

		– hockeyViewController:

Create an update view

Properties

alwaysShowUpdateReminder

Flag that determines if updates alert should be repeatedly shown

@property (nonatomic, assign) BOOL alwaysShowUpdateReminder

Discussion

If enabled the update alert shows on every startup and whenever the app becomes active,
until the update is installed.
If disabled the update alert is only shown once ever and it is up to you to provide an
alternate way for the user to navigate to the update UI or update in another way.

When running the app from the App Store, this setting is ignored.

Default: YES

Declared In

		BITUpdateManager.h

authenticationSecret

The authentication token from HockeyApp.

@property (nonatomic, strong) NSString *authenticationSecret

Discussion

Set the token to the Secret ID which HockeyApp provides for every app.

When running the app from the App Store, this setting is ignored.

See Also

@property requireAuthorization

Declared In

		BITUpdateManager.h

checkForUpdateOnLaunch

Flag that determines whether the automatic update checks should be done.

@property (nonatomic, assign, getter=isCheckForUpdateOnLaunch) BOOL checkForUpdateOnLaunch

Discussion

If this is enabled the update checks will be performed automatically depending on the
updateSetting property. If this is disabled the updateSetting property will have
no effect, and checking for updates is totally up to be done by yourself.

When running the app from the App Store, this setting is ignored.

Default: YES

Warning: When setting this to NO you need to invoke update checks yourself!

See Also

@property updateSetting

– checkForUpdate

Declared In

		BITUpdateManager.h

delegate

Sets the BITUpdateManagerDelegate delegate.

@property (nonatomic, weak) id delegate

Discussion

When using BITUpdateManager to distribute updates of your beta or enterprise
application, it is REQUIRED to set this delegate and implement
[BITUpdateManagerDelegate customDeviceIdentifierForUpdateManager:]!

Declared In

		BITUpdateManager.h

expiryDate

Expiry date of the current app version

@property (nonatomic, strong) NSDate *expiryDate

Discussion

If set, the app will get unusable at the given date by presenting a blocking view on
top of the apps UI so that no interaction is possible. To present a custom you, check
the documentation of the
[BITUpdateManagerDelegate shouldDisplayExpiryAlertForUpdateManager:] delegate.

Once the expiry date is reached, the app will no longer check for updates or
send any usage data to the server!

When running the app from the App Store, this setting is ignored.

Default: nil

Warning: This only works when using Ad-Hoc provisioning profiles!

See Also

[BITUpdateManagerDelegate shouldDisplayExpiryAlertForUpdateManager:]

[BITUpdateManagerDelegate didDisplayExpiryAlertForUpdateManager:]

Declared In

		BITUpdateManager.h

requireAuthorization

Flag that determines if each update should be authenticated

@property (nonatomic, assign, getter=isRequireAuthorization) BOOL requireAuthorization

Discussion

If enabled each update will be authenticated on startup against the HockeyApp servers.
The process will basically validate if the current device is part of the provisioning
profile on the server. If not, it will present a blocking view on top of the apps UI
so that no interaction is possible.

When running the app from the App Store, this setting is ignored.

Default: NO

Warning: This only works when using Ad-Hoc provisioning profiles!

See Also

@property authenticationSecret

Declared In

		BITUpdateManager.h

showDirectInstallOption

Flag that determines if the update alert should show an direct install option

@property (nonatomic, assign, getter=isShowingDirectInstallOption) BOOL showDirectInstallOption

Discussion

If enabled the update alert shows an additional option which allows to invoke the update
installation process directly, instead of viewing the update UI first.
By default the alert only shows a Show and Ignore option.

When running the app from the App Store, this setting is ignored.

Default: NO

Declared In

		BITUpdateManager.h

updateSetting

When to check for new updates.

@property (nonatomic, assign) BITUpdateSetting updateSetting

Discussion

Defines when a the SDK should check if there is a new update available on the
server. This must be assigned one of the following:

BITUpdateCheckStartup: On every startup or or when the app comes to the foreground

BITUpdateCheckDaily: Once a day

BITUpdateCheckManually: Manually

When running the app from the App Store, this setting is ignored.

Default: BITUpdateCheckStartup

Warning: When setting this to BITUpdateCheckManually you need to either
invoke the update checking process yourself with checkForUpdate somehow, e.g. by
proving an update check button for the user or integrating the Update View into your
user interface.

See Also

@property checkForUpdateOnLaunch

– checkForUpdate

Declared In

		BITUpdateManager.h

Instance Methods

checkForUpdate

Check for an update

- (void)checkForUpdate

Discussion

Call this to trigger a check if there is a new update available on the HockeyApp servers.

When running the app from the App Store, this setting is ignored.

See Also

@property updateSetting

@property checkForUpdateOnLaunch

Declared In

		BITUpdateManager.h

hockeyViewController:

Create an update view

- (BITUpdateViewController *)hockeyViewController:(BOOL)modal

Parameters

modal

Return a view ready for modal presentation with integrated navigation bar

Return Value

BITUpdateViewController The update user interface view controller,
e.g. to push it onto a navigation stack.

Declared In

		BITUpdateManager.h

showUpdateView

Present the modal update user interface.

- (void)showUpdateView

Declared In

		BITUpdateManager.h

Index

Hierarchy

© 2013 BitStadium GmbH. All rights reserved. (Last updated: 2013-02-12)

						Generated by appledoc 2.1 (build 858).


text:
package scray.common.properties.predefined;

import scray.common.properties.PropertyException;
import scray.common.properties.ScrayProperties;
import scray.common.properties.ScrayPropertyRegistration;

public class CommonCassandraRegistrar implements ScrayPropertyRegistration.PropertyRegistrar {

	@Override
	public void register() throws PropertyException {
		ScrayProperties.registerProperty(PredefinedProperties.CASSANDRA_QUERY_SEED_IPS);
		ScrayProperties.registerProperty(PredefinedProperties.CASSANDRA_QUERY_CLUSTER_NAME);
		ScrayProperties.registerProperty(PredefinedProperties.CASSANDRA_QUERY_CLUSTER_DC);
	    ScrayProperties.registerProperty(PredefinedProperties.CASSANDRA_QUERY_KEYSPACE);
	    ScrayProperties.registerProperty(PredefinedProperties.QUERY_READ_CONSISTENCY);
	}
}


text:
created: 2018-11-09
last updated: 2018-11-09
status: Implemented
reviewers:
  - serynth
title: Config Setting Chaining
authors:
  - gregestren
discussion thread: https://groups.google.com/d/topic/bazel-dev/HWBijMHRTrg/discussion
---

# Config Setting Chaining

## Abstract

This document proposes extensions to
[`config_setting`](https://docs.bazel.build/versions/master/be/general.html#config_setting)
that enable `AND` and `OR` chaining, i.e. the ability to express *"this condition
matches if `config_settings` A, B, **and** C are true"* and *"this condition matches
if `config settings` A, B, **o**r C are true"*.

This addresses long-standing user feedback on the awkwardness of expressing
these combinations with the current API.

These extensions will be added to a standard Starlark library rather than core
Bazel.

## Background

[Configurable
attributes](https://docs.bazel.build/versions/master/configurable-attributes.html)
is a powerful Bazel feature that lets rules customize their settings based on how
the build is invoked.

For example, a C++ binary may choose to include ARM-specific dependencies for
builds that target ARM architectures.

The criteria that determine which "choice" a rule should make are modeled with
the
[`config_setting`](https://docs.bazel.build/versions/master/be/general.html#config_setting)
rule:

```python
config_setting(
    name = "if_arm",
    values = {"cpu": "arm"}
)
```

This rule "matches" only for builds that are invoked with `--cpu=arm`. This
means

```python
cc_binary(
    name = "mybinary",
    srcs = ["mybinary.cc"],
    deps = select({
        ":if_arm": [":arm_deps"],
        "//conditions:default": []
  })
)
```

declares a C++ binary that links `":arm_deps"` for `--cpu=arm` builds and no
additional deps otherwise.

`config_setting` is an intentionally simple rule: it matches if every entry in
its `values` attribute matches. This keeps both the model and its implementation
logic simple and adds less runtime overhead than more sophisticated models
like regex parsing.

Experience shows that this simplicity sometimes makes it hard for users to
express what they want. One of the most commonly expressed limitations is lack
of boolean chaining. If you have `config_setting`s `:config1`, `:config2`, and
`:config3` and want a `select` branch to trigger if any of them are true, you can
write:

```python
select({
    ":config1": [":desired_branch"],
    ":config2": [":desired_branch"],
    ":config3": [":desired_branch"],
    "//conditions:default": [":other_branch"],
})
```

This is obviously redundant, scales poorly, and risks bugs if you, for example,
change the value of `":desired_branch"` but accidentally forget to update a
line.

If you want the `select` to trigger when *all* conditions are true, the story is
even worse. There are basically two practical options. One is to define a new
`config_setting` `:config1_and_2_and_3` and manually copy all `values` entries
from the original ones there. This has the same scalability and bug issues as
above. The other is to "chain" `select`s by having an initial `select` trigger
on `:config1`, have that branch resolve to a dependency with its own `select`
triggering on `:config2`, and so on. This is extremely verbose, hard to read,
and doesn't work for non-label-based attributes.

## `selects.with_or`

The [Skylib](https://github.com/bazelbuild/bazel-skylib) module
[`selects`](https://github.com/bazelbuild/bazel-skylib/blob/master/lib/selects.bzl)
provides a partial solution to this problem. This defines a Starlark macro that
replaces `select` with an ehanced version that supports `OR` chaining:

```python
sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = selects.with_or({
        (":config1", ":config2", ":config3"): [":standard_lib"],
        ":config4": [":special_lib"],
    }),
)
```

This works well for embedding `OR` chains directly into `select` statements. But
it's not reusable (since it binds the conditions and values they select together,
so other `select`s can't re-use them) and doesn't offer a solution for `AND`.

## Proposal

This document proposes new Starlark macros that leverage the
[`alias`](https://docs.bazel.build/versions/master/be/general.html#alias) rule
to create new  `config_setting`s that `OR` or `AND`-wrap other
`config_setting`s. Because these are real `config_setting`s, they can be used
anywhere `config_setting`s are accepted. This makes them protable and easy to
use.

This proposal is inspired by ideas expressed by [@jfancher](https://github.com/jfancher) in
[https://github.com/bazelbuild/bazel/issues/6449](https://github.com/bazelbuild/bazel/issues/6449#issuecomment-431471309).

### API

Given `config_setting`s `:config1`, `:config2`, and `:config3`, a new macro in
the [Skylib](https://github.com/bazelbuild/bazel-skylib)
[`selects`](https://github.com/bazelbuild/bazel-skylib/blob/master/lib/selects.bzl)
module called `config_setting_group` provides `OR` or `AND` chaining as follows:

**OR:**
```python
load("@bazel_skylib//:lib.bzl", "selects")

selects.config_setting_group(
    name = "any_config",
    match_any = [":config1", ":config2", ":config3"]
)
```

**AND:**
```python
load("@bazel_skylib//:lib.bzl", "selects")

selects.config_setting_group(
    name = "all_configs",
    match_all = [":config1", ":config2", ":config3"]
)
```

This reduces the original example above to the following:

**OR:**
```python
select({
    ":any_config": [":desired_branch"],
    "//conditions:default": [":other_branch"],
})
```

**AND:**
```python
select({
    ":all_configs": [":desired_branch"],
    "//conditions:default": [":other_branch"],
})
```

Setting both `match_any` and `match_all` in the same `config_setting_group`
triggers an error. There's no technical obstacle to doing this, but this
proposal aims to add as little extra complexity as necessary to serve
known needs. If the need for this extra logic is subsequently demonstrated, this
can be added as a followup.

### Implementation

The following is added to
[`selects.bzl`](https://github.com/bazelbuild/bazel-skylib/blob/master/lib/selects.bzl). It
exploits the fact that
[`alias`](https://docs.bazel.build/versions/master/be/general.html#alias) rules
can use a `select` to determine which rules they resolve to.

```python
def config_setting_group(name, match_any = [], match_all = []):
    """ TODO: document.
    """
    empty1 = not bool(len(match_any))
    empty2 = not bool(len(match_all))
    if (empty1 and empty2) or (not empty1 and not empty2):
        fail('Either "match_any" or "match_all" must be set, but not both.')
    _check_duplicates(match_any)
    _check_duplicates(match_all)

    if ((len(match_any) == 1 and match_any[0] == "//conditions:default") or
        (len(match_all) == 1 and match_all[0] == "//conditions:default")):
        # If the only entry is "//conditions:default", the condition is
        # automatically true.
        _config_setting_always_true(name)
    elif not empty1:
        _config_setting_or_group(name, match_any)
    else:
        _config_setting_and_group(name, match_all)

def _check_duplicates(settings):
    """ Fails if any entry in settings appears more than once.
    """
    seen = {}
    for setting in settings:
        if setting in seen:
            fail(setting + " appears more than once. Duplicates not allowed.")
        seen[setting] = True

def _remove_default_condition(settings):
    """ Returns settings with "//conditions:default" entries filtered out.
    """
    new_settings = []
    for setting in settings:
        if settings != "//conditions:default":
            new_settings.append(setting)
    return new_settings

def _config_setting_or_group(name, settings):
    """ ORs multiple config_settings together (inclusively).

    The core idea is to create a sequential chain of alias targets where each is
    select-resolved as follows: If alias n matches config_setting n, the chain
    is true so it resolves to config_setting n. Else  it resolves to alias n+1
    (which checks config_setting n+1, and so on). If none of the config_settings
    match, the final alias resolves to one of them arbitrarily, which by
    definition doesn't match.
    """

    # "//conditions:default" is present, the whole chain is automatically true.
    if len(_remove_default_condition(settings)) < len(settings):
        _config_setting_always_true(name)
        return

    # One entry? Just alias directly to it.
    elif len(settings) == 1:
        native.alias(
            name = name,
            actual = settings[0],
        )
        return

    # First alias adopts the core name so user references start here.
    native.alias(
        name = name,
        actual = select({
            settings[0]: settings[0],
            "//conditions:default": name + "_2",
        }),
    )

    # Second through (n-2)nd aliases:
    for i in range(2, len(settings) - 1):
        cur_setting = settings[i - 1]
        native.alias(
            name = name + "_" + str(i),
            actual = select({
                cur_setting: cur_setting,
                "//conditions:default": name + "_" + str(i + 1),
            }),
        )

    # (n-1)st alias: if true it can resolve directly to the final config_setting
    # (which doesn't need an equivalent alias).
    native.alias(
        name = name + "_" + str(len(settings) - 1),
        actual = select({
            settings[-2]: settings[-2],
            "//conditions:default": settings[-1],
        }),
    )

def _config_setting_and_group(name, settings):
    """ ANDs multiple config_settings together.

    The core idea is to create a sequential chain of alias targets where each is
    select-resolved as follows: If alias n matches config_setting n, it resolves to
    alias n+1 (which evaluates config_setting n+1, and so on). Else it resolves to
    config_setting n, which doesn't match by definition. The only way to get a
    matching final result is if all config_settings match.
    """

    # "//conditions:default" is automatically true so doesn't need checking.
    settings = _remove_default_condition(settings)

    # One config_setting input? Just alias directly to it.
    if len(settings) == 1:
        native.alias(
            name = name,
            actual = settings[0],
        )
        return

    # First alias adopts the core name so user references start here.
    native.alias(
        name = name,
        actual = select({
            settings[0]: name + "_2",
            "//conditions:default": settings[0],
        }),
    )

    # Second through (n-2)nd aliases:
    for i in range(2, len(settings) - 1):
        cur_setting = settings[i - 1]
        native.alias(
            name = name + "_" + str(i),
            actual = select({
                cur_setting: name + "_" + str(i + 1),
                "//conditions:default": cur_setting,
            }),
        )

    # (n-1)st alias: if true it can resolve directly to the final config_setting
    # (which doesn't need an equivalent alias).
    native.alias(
        name = name + "_" + str(len(settings) - 1),
        actual = select({
            settings[-2]: settings[-1],
            "//conditions:default": settings[-2],
        }),
    )

def _config_setting_always_true(name):
    """ Returns a config_setting with the given name that's always true.

    This is achieved by constructing a two-entry OR chain where each
    config_setting takes opposite values of a boolean flag.
    """
    name_on = name + "_stamp_binary_on_check"
    name_off = name + "_stamp_binary_off_check"
    native.config_setting(
        name = name_on,
        values = {"stamp": True},
    )
    native.config_setting(
        name = name_off,
        values = {"stamp": False},
    )
    return _config_setting_or_group(name, [":" + name_on, ":" + name_off])
```

### Error Messages

This approach produces good, but not perfect, errors on `select` fails.

A failed `AND` evaluation of

```python
selects.config_setting_group(
    name = "all_configs",
    match_all = [":config1", ":config2", ":config3"]
)

select({
    ":all_configs": [":desired_branch"],
})
```

produces the error

```shell
ERROR: /home/me/workspace/BUILD:10:10:
Configurable attribute "cmd" doesn't match this configuration (would a default
condition help?).
Conditions checked:
 //:config2
```

This is pretty *good*: it reports the first condition in the `AND` chain that
doesn't match. This is precise, easily traceable, and actionable.

`OR` chaining isn't quite as clear. When the equivalent `select` on `:any_config`
fails, this produces

```shell
ERROR: /home/me/workspace/BUILD:10:10:
Configurable attribute "cmd" doesn't match this configuration (would a default
condition help?).
Conditions checked:
 //:config3
```

This only mentions the final condition. It makes no reference to any of the
other conditions that were checked. There's no obvious way to improve this. One
option is to create a new `config_setting` for the final chain link called
`:config1_or_2_or_3`. But this also seems confusing and not worth the mild
benefit.

## Alternatives

The main alternative approach would be to ad chaining support directly to
`config_setting`. This proposal rejects that approach for the following reasons:

1. We value keeping `config_setting` as simple as possible. This keeps the core
logic simple, clean, efficient, and easy to maintain. It also keeps it
accessible to new users who are learning the feature. Experience shows that more
complicated models invite more complicated uses. For example, `select` replaced
an older Google-only attribute on C++ rules that was modeled on regexes. When
`select` was introduced, it replaced every instance of that attribute with
simpler logic and no loss in functionality - a net-win for everybody. It turned
out that no practical uses of the feature required the full might of regexes.

1. Bazel in general is moving toward a simple, focused core with most rule
logic writtern in Starlark. This proposal continues that momentum.

1. Putting the logic in Starlark makes it easy for users to understand it, fork
it for project-specific needs, and contribute patches. Logic in Bazel core is a
huge barrier to wide community participation.

1. This proposal doesn't complicate `select` *at all* for users who don't need
these features.

1. Keeping the core logic simple helps maintain efficiency guarantees. Even the
current logic has contributed to Bazel freezes and OOMs with rogue
`select`s (which thankfully have since been bug-fixed). More powerful models
make it harder to reign these corner cases in.

## Followups

The following extensions can be added to this proposal on request:

* `config_setting_group` can be expanded to support both `match_any`
and `match_all` in the same instance. This would be interpreted as "*this group
matches if any setting in `match_any` matches and all settings in  `match_all`
match*".

* We can add a peer macro to
[`selects.with_or`](https://github.com/bazelbuild/bazel-skylib/blob/8cecf885c8bf4c51e82fd6b50b9dd68d2c98f757/lib/selects.bzl#L17)
called `selects.with_and`. This would provide `AND`-style embedded chaining
directly in `select`s without having to define any new rules. This could work by
implicitly calling `config_setting_group` with `match_all` and passing a
reference to that alias to the `select`.

## Testing

Since `select` users heavily depend on the correctness of `config_setting` to
control their build flow, thorough unit tests will be added to the `selects`
module's [test
script](https://github.com/bazelbuild/bazel-skylib/blob/master/tests/selects_tests.bzl).

## References

* [Configurable Build
Attributes](https://docs.bazel.build/versions/master/configurable-attributes.html)
* Original idea: ["Documentation/behavior mismatch with alias +
config_setting"](https://github.com/bazelbuild/bazel/issues/6449)