text:
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;

using Grpc.Core.Logging;
using Grpc.Core.Utils;

namespace Grpc.Core.Internal
{
    /// <summary>
    /// Provides access to all native methods provided by <c>NativeExtension</c>.
    /// An extra level of indirection is added to P/Invoke calls to allow intelligent loading
    /// of the right configuration of the native extension based on current platform, architecture etc.
    /// </summary>
    internal class NativeMethods
    {
        #region Native methods

        public readonly Delegates.grpcsharp_init_delegate grpcsharp_init;
        public readonly Delegates.grpcsharp_shutdown_delegate grpcsharp_shutdown;
        public readonly Delegates.grpcsharp_version_string_delegate grpcsharp_version_string;

        public readonly Delegates.grpcsharp_batch_context_create_delegate grpcsharp_batch_context_create;
        public readonly Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate grpcsharp_batch_context_recv_initial_metadata;
        public readonly Delegates.grpcsharp_batch_context_recv_message_length_delegate grpcsharp_batch_context_recv_message_length;
        public readonly Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate grpcsharp_batch_context_recv_message_to_buffer;
        public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status;
        public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details;
        public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
        public readonly Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate grpcsharp_batch_context_recv_close_on_server_cancelled;
        public readonly Delegates.grpcsharp_batch_context_destroy_delegate grpcsharp_batch_context_destroy;

        public readonly Delegates.grpcsharp_request_call_context_create_delegate grpcsharp_request_call_context_create;
        public readonly Delegates.grpcsharp_request_call_context_call_delegate grpcsharp_request_call_context_call;
        public readonly Delegates.grpcsharp_request_call_context_method_delegate grpcsharp_request_call_context_method;
        public readonly Delegates.grpcsharp_request_call_context_host_delegate grpcsharp_request_call_context_host;
        public readonly Delegates.grpcsharp_request_call_context_deadline_delegate grpcsharp_request_call_context_deadline;
        public readonly Delegates.grpcsharp_request_call_context_request_metadata_delegate grpcsharp_request_call_context_request_metadata;
        public readonly Delegates.grpcsharp_request_call_context_destroy_delegate grpcsharp_request_call_context_destroy;

        public readonly Delegates.grpcsharp_composite_call_credentials_create_delegate grpcsharp_composite_call_credentials_create;
        public readonly Delegates.grpcsharp_call_credentials_release_delegate grpcsharp_call_credentials_release;

        public readonly Delegates.grpcsharp_call_cancel_delegate grpcsharp_call_cancel;
        public readonly Delegates.grpcsharp_call_cancel_with_status_delegate grpcsharp_call_cancel_with_status;
        public readonly Delegates.grpcsharp_call_start_unary_delegate grpcsharp_call_start_unary;
        public readonly Delegates.grpcsharp_call_start_client_streaming_delegate grpcsharp_call_start_client_streaming;
        public readonly Delegates.grpcsharp_call_start_server_streaming_delegate grpcsharp_call_start_server_streaming;
        public readonly Delegates.grpcsharp_call_start_duplex_streaming_delegate grpcsharp_call_start_duplex_streaming;
        public readonly Delegates.grpcsharp_call_send_message_delegate grpcsharp_call_send_message;
        public readonly Delegates.grpcsharp_call_send_close_from_client_delegate grpcsharp_call_send_close_from_client;
        public readonly Delegates.grpcsharp_call_send_status_from_server_delegate grpcsharp_call_send_status_from_server;
        public readonly Delegates.grpcsharp_call_recv_message_delegate grpcsharp_call_recv_message;
        public readonly Delegates.grpcsharp_call_recv_initial_metadata_delegate grpcsharp_call_recv_initial_metadata;
        public readonly Delegates.grpcsharp_call_start_serverside_delegate grpcsharp_call_start_serverside;
        public readonly Delegates.grpcsharp_call_send_initial_metadata_delegate grpcsharp_call_send_initial_metadata;
        public readonly Delegates.grpcsharp_call_set_credentials_delegate grpcsharp_call_set_credentials;
        public readonly Delegates.grpcsharp_call_get_peer_delegate grpcsharp_call_get_peer;
        public readonly Delegates.grpcsharp_call_destroy_delegate grpcsharp_call_destroy;

        public readonly Delegates.grpcsharp_channel_args_create_delegate grpcsharp_channel_args_create;
        public readonly Delegates.grpcsharp_channel_args_set_string_delegate grpcsharp_channel_args_set_string;
        public readonly Delegates.grpcsharp_channel_args_set_integer_delegate grpcsharp_channel_args_set_integer;
        public readonly Delegates.grpcsharp_channel_args_destroy_delegate grpcsharp_channel_args_destroy;

        public readonly Delegates.grpcsharp_override_default_ssl_roots grpcsharp_override_default_ssl_roots;
        public readonly Delegates.grpcsharp_ssl_credentials_create_delegate grpcsharp_ssl_credentials_create;
        public readonly Delegates.grpcsharp_composite_channel_credentials_create_delegate grpcsharp_composite_channel_credentials_create;
        public readonly Delegates.grpcsharp_channel_credentials_release_delegate grpcsharp_channel_credentials_release;

        public readonly Delegates.grpcsharp_insecure_channel_create_delegate grpcsharp_insecure_channel_create;
        public readonly Delegates.grpcsharp_secure_channel_create_delegate grpcsharp_secure_channel_create;
        public readonly Delegates.grpcsharp_channel_create_call_delegate grpcsharp_channel_create_call;
        public readonly Delegates.grpcsharp_channel_check_connectivity_state_delegate grpcsharp_channel_check_connectivity_state;
        public readonly Delegates.grpcsharp_channel_watch_connectivity_state_delegate grpcsharp_channel_watch_connectivity_state;
        public readonly Delegates.grpcsharp_channel_get_target_delegate grpcsharp_channel_get_target;
        public readonly Delegates.grpcsharp_channel_destroy_delegate grpcsharp_channel_destroy;

        public readonly Delegates.grpcsharp_sizeof_grpc_event_delegate grpcsharp_sizeof_grpc_event;

        public readonly Delegates.grpcsharp_completion_queue_create_delegate grpcsharp_completion_queue_create;
        public readonly Delegates.grpcsharp_completion_queue_shutdown_delegate grpcsharp_completion_queue_shutdown;
        public readonly Delegates.grpcsharp_completion_queue_next_delegate grpcsharp_completion_queue_next;
        public readonly Delegates.grpcsharp_completion_queue_pluck_delegate grpcsharp_completion_queue_pluck;
        public readonly Delegates.grpcsharp_completion_queue_destroy_delegate grpcsharp_completion_queue_destroy;

        public readonly Delegates.gprsharp_free_delegate gprsharp_free;

        public readonly Delegates.grpcsharp_metadata_array_create_delegate grpcsharp_metadata_array_create;
        public readonly Delegates.grpcsharp_metadata_array_add_delegate grpcsharp_metadata_array_add;
        public readonly Delegates.grpcsharp_metadata_array_count_delegate grpcsharp_metadata_array_count;
        public readonly Delegates.grpcsharp_metadata_array_get_key_delegate grpcsharp_metadata_array_get_key;
        public readonly Delegates.grpcsharp_metadata_array_get_value_delegate grpcsharp_metadata_array_get_value;
        public readonly Delegates.grpcsharp_metadata_array_destroy_full_delegate grpcsharp_metadata_array_destroy_full;

        public readonly Delegates.grpcsharp_redirect_log_delegate grpcsharp_redirect_log;

        public readonly Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate grpcsharp_metadata_credentials_create_from_plugin;
        public readonly Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate grpcsharp_metadata_credentials_notify_from_plugin;

        public readonly Delegates.grpcsharp_ssl_server_credentials_create_delegate grpcsharp_ssl_server_credentials_create;
        public readonly Delegates.grpcsharp_server_credentials_release_delegate grpcsharp_server_credentials_release;

        public readonly Delegates.grpcsharp_server_create_delegate grpcsharp_server_create;
        public readonly Delegates.grpcsharp_server_register_completion_queue_delegate grpcsharp_server_register_completion_queue;
        public readonly Delegates.grpcsharp_server_add_insecure_http2_port_delegate grpcsharp_server_add_insecure_http2_port;
        public readonly Delegates.grpcsharp_server_add_secure_http2_port_delegate grpcsharp_server_add_secure_http2_port;
        public readonly Delegates.grpcsharp_server_start_delegate grpcsharp_server_start;
        public readonly Delegates.grpcsharp_server_request_call_delegate grpcsharp_server_request_call;
        public readonly Delegates.grpcsharp_server_cancel_all_calls_delegate grpcsharp_server_cancel_all_calls;
        public readonly Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate grpcsharp_server_shutdown_and_notify_callback;
        public readonly Delegates.grpcsharp_server_destroy_delegate grpcsharp_server_destroy;

        public readonly Delegates.gprsharp_now_delegate gprsharp_now;
        public readonly Delegates.gprsharp_inf_future_delegate gprsharp_inf_future;
        public readonly Delegates.gprsharp_inf_past_delegate gprsharp_inf_past;
        public readonly Delegates.gprsharp_convert_clock_type_delegate gprsharp_convert_clock_type;
        public readonly Delegates.gprsharp_sizeof_timespec_delegate gprsharp_sizeof_timespec;

        public readonly Delegates.grpcsharp_test_callback_delegate grpcsharp_test_callback;
        public readonly Delegates.grpcsharp_test_nop_delegate grpcsharp_test_nop;

        #endregion

        public NativeMethods(UnmanagedLibrary library)
        {
            this.grpcsharp_init = GetMethodDelegate<Delegates.grpcsharp_init_delegate>(library);
            this.grpcsharp_shutdown = GetMethodDelegate<Delegates.grpcsharp_shutdown_delegate>(library);
            this.grpcsharp_version_string = GetMethodDelegate<Delegates.grpcsharp_version_string_delegate>(library);

            this.grpcsharp_batch_context_create = GetMethodDelegate<Delegates.grpcsharp_batch_context_create_delegate>(library);
            this.grpcsharp_batch_context_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate>(library);
            this.grpcsharp_batch_context_recv_message_length = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_length_delegate>(library);
            this.grpcsharp_batch_context_recv_message_to_buffer = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate>(library);
            this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library);
            this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library);
            this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library);
            this.grpcsharp_batch_context_recv_close_on_server_cancelled = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate>(library);
            this.grpcsharp_batch_context_destroy = GetMethodDelegate<Delegates.grpcsharp_batch_context_destroy_delegate>(library);

            this.grpcsharp_request_call_context_create = GetMethodDelegate<Delegates.grpcsharp_request_call_context_create_delegate>(library);
            this.grpcsharp_request_call_context_call = GetMethodDelegate<Delegates.grpcsharp_request_call_context_call_delegate>(library);
            this.grpcsharp_request_call_context_method = GetMethodDelegate<Delegates.grpcsharp_request_call_context_method_delegate>(library);
            this.grpcsharp_request_call_context_host = GetMethodDelegate<Delegates.grpcsharp_request_call_context_host_delegate>(library);
            this.grpcsharp_request_call_context_deadline = GetMethodDelegate<Delegates.grpcsharp_request_call_context_deadline_delegate>(library);
            this.grpcsharp_request_call_context_request_metadata = GetMethodDelegate<Delegates.grpcsharp_request_call_context_request_metadata_delegate>(library);
            this.grpcsharp_request_call_context_destroy = GetMethodDelegate<Delegates.grpcsharp_request_call_context_destroy_delegate>(library);

            this.grpcsharp_composite_call_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_call_credentials_create_delegate>(library);
            this.grpcsharp_call_credentials_release = GetMethodDelegate<Delegates.grpcsharp_call_credentials_release_delegate>(library);

            this.grpcsharp_call_cancel = GetMethodDelegate<Delegates.grpcsharp_call_cancel_delegate>(library);
            this.grpcsharp_call_cancel_with_status = GetMethodDelegate<Delegates.grpcsharp_call_cancel_with_status_delegate>(library);
            this.grpcsharp_call_start_unary = GetMethodDelegate<Delegates.grpcsharp_call_start_unary_delegate>(library);
            this.grpcsharp_call_start_client_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_client_streaming_delegate>(library);
            this.grpcsharp_call_start_server_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_server_streaming_delegate>(library);
            this.grpcsharp_call_start_duplex_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_duplex_streaming_delegate>(library);
            this.grpcsharp_call_send_message = GetMethodDelegate<Delegates.grpcsharp_call_send_message_delegate>(library);
            this.grpcsharp_call_send_close_from_client = GetMethodDelegate<Delegates.grpcsharp_call_send_close_from_client_delegate>(library);
            this.grpcsharp_call_send_status_from_server = GetMethodDelegate<Delegates.grpcsharp_call_send_status_from_server_delegate>(library);
            this.grpcsharp_call_recv_message = GetMethodDelegate<Delegates.grpcsharp_call_recv_message_delegate>(library);
            this.grpcsharp_call_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_recv_initial_metadata_delegate>(library);
            this.grpcsharp_call_start_serverside = GetMethodDelegate<Delegates.grpcsharp_call_start_serverside_delegate>(library);
            this.grpcsharp_call_send_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_send_initial_metadata_delegate>(library);
            this.grpcsharp_call_set_credentials = GetMethodDelegate<Delegates.grpcsharp_call_set_credentials_delegate>(library);
            this.grpcsharp_call_get_peer = GetMethodDelegate<Delegates.grpcsharp_call_get_peer_delegate>(library);
            this.grpcsharp_call_destroy = GetMethodDelegate<Delegates.grpcsharp_call_destroy_delegate>(library);

            this.grpcsharp_channel_args_create = GetMethodDelegate<Delegates.grpcsharp_channel_args_create_delegate>(library);
            this.grpcsharp_channel_args_set_string = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_string_delegate>(library);
            this.grpcsharp_channel_args_set_integer = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_integer_delegate>(library);
            this.grpcsharp_channel_args_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_args_destroy_delegate>(library);

            this.grpcsharp_override_default_ssl_roots = GetMethodDelegate<Delegates.grpcsharp_override_default_ssl_roots>(library);
            this.grpcsharp_ssl_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_credentials_create_delegate>(library);
            this.grpcsharp_composite_channel_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_channel_credentials_create_delegate>(library);
            this.grpcsharp_channel_credentials_release = GetMethodDelegate<Delegates.grpcsharp_channel_credentials_release_delegate>(library);

            this.grpcsharp_insecure_channel_create = GetMethodDelegate<Delegates.grpcsharp_insecure_channel_create_delegate>(library);
            this.grpcsharp_secure_channel_create = GetMethodDelegate<Delegates.grpcsharp_secure_channel_create_delegate>(library);
            this.grpcsharp_channel_create_call = GetMethodDelegate<Delegates.grpcsharp_channel_create_call_delegate>(library);
            this.grpcsharp_channel_check_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_check_connectivity_state_delegate>(library);
            this.grpcsharp_channel_watch_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_watch_connectivity_state_delegate>(library);
            this.grpcsharp_channel_get_target = GetMethodDelegate<Delegates.grpcsharp_channel_get_target_delegate>(library);
            this.grpcsharp_channel_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_destroy_delegate>(library);

            this.grpcsharp_sizeof_grpc_event = GetMethodDelegate<Delegates.grpcsharp_sizeof_grpc_event_delegate>(library);

            this.grpcsharp_completion_queue_create = GetMethodDelegate<Delegates.grpcsharp_completion_queue_create_delegate>(library);
            this.grpcsharp_completion_queue_shutdown = GetMethodDelegate<Delegates.grpcsharp_completion_queue_shutdown_delegate>(library);
            this.grpcsharp_completion_queue_next = GetMethodDelegate<Delegates.grpcsharp_completion_queue_next_delegate>(library);
            this.grpcsharp_completion_queue_pluck = GetMethodDelegate<Delegates.grpcsharp_completion_queue_pluck_delegate>(library);
            this.grpcsharp_completion_queue_destroy = GetMethodDelegate<Delegates.grpcsharp_completion_queue_destroy_delegate>(library);

            this.gprsharp_free = GetMethodDelegate<Delegates.gprsharp_free_delegate>(library);

            this.grpcsharp_metadata_array_create = GetMethodDelegate<Delegates.grpcsharp_metadata_array_create_delegate>(library);
            this.grpcsharp_metadata_array_add = GetMethodDelegate<Delegates.grpcsharp_metadata_array_add_delegate>(library);
            this.grpcsharp_metadata_array_count = GetMethodDelegate<Delegates.grpcsharp_metadata_array_count_delegate>(library);
            this.grpcsharp_metadata_array_get_key = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_key_delegate>(library);
            this.grpcsharp_metadata_array_get_value = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_value_delegate>(library);
            this.grpcsharp_metadata_array_destroy_full = GetMethodDelegate<Delegates.grpcsharp_metadata_array_destroy_full_delegate>(library);

            this.grpcsharp_redirect_log = GetMethodDelegate<Delegates.grpcsharp_redirect_log_delegate>(library);

            this.grpcsharp_metadata_credentials_create_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate>(library);
            this.grpcsharp_metadata_credentials_notify_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate>(library);

            this.grpcsharp_ssl_server_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_server_credentials_create_delegate>(library);
            this.grpcsharp_server_credentials_release = GetMethodDelegate<Delegates.grpcsharp_server_credentials_release_delegate>(library);

            this.grpcsharp_server_create = GetMethodDelegate<Delegates.grpcsharp_server_create_delegate>(library);
            this.grpcsharp_server_register_completion_queue = GetMethodDelegate<Delegates.grpcsharp_server_register_completion_queue_delegate>(library);
            this.grpcsharp_server_add_insecure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_insecure_http2_port_delegate>(library);
            this.grpcsharp_server_add_secure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_secure_http2_port_delegate>(library);
            this.grpcsharp_server_start = GetMethodDelegate<Delegates.grpcsharp_server_start_delegate>(library);
            this.grpcsharp_server_request_call = GetMethodDelegate<Delegates.grpcsharp_server_request_call_delegate>(library);
            this.grpcsharp_server_cancel_all_calls = GetMethodDelegate<Delegates.grpcsharp_server_cancel_all_calls_delegate>(library);
            this.grpcsharp_server_shutdown_and_notify_callback = GetMethodDelegate<Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate>(library);
            this.grpcsharp_server_destroy = GetMethodDelegate<Delegates.grpcsharp_server_destroy_delegate>(library);

            this.gprsharp_now = GetMethodDelegate<Delegates.gprsharp_now_delegate>(library);
            this.gprsharp_inf_future = GetMethodDelegate<Delegates.gprsharp_inf_future_delegate>(library);
            this.gprsharp_inf_past = GetMethodDelegate<Delegates.gprsharp_inf_past_delegate>(library);
            this.gprsharp_convert_clock_type = GetMethodDelegate<Delegates.gprsharp_convert_clock_type_delegate>(library);
            this.gprsharp_sizeof_timespec = GetMethodDelegate<Delegates.gprsharp_sizeof_timespec_delegate>(library);

            this.grpcsharp_test_callback = GetMethodDelegate<Delegates.grpcsharp_test_callback_delegate>(library);
            this.grpcsharp_test_nop = GetMethodDelegate<Delegates.grpcsharp_test_nop_delegate>(library);
        }

        /// <summary>
        /// Gets singleton instance of this class.
        /// </summary>
        public static NativeMethods Get()
        {
            return NativeExtension.Get().NativeMethods;
        }

        static T GetMethodDelegate<T>(UnmanagedLibrary library)
            where T : class
        {
            var methodName = RemoveStringSuffix(typeof(T).Name, "_delegate");
            return library.GetNativeMethodDelegate<T>(methodName);
        }

        static string RemoveStringSuffix(string str, string toRemove)
        {
            if (!str.EndsWith(toRemove))
            {
                return str;
            }
            return str.Substring(0, str.Length - toRemove.Length);
        }

        /// <summary>
        /// Delegate types for all published native methods. Declared under inner class to prevent scope pollution.
        /// </summary>
        public class Delegates
        {
            public delegate void grpcsharp_init_delegate();
            public delegate void grpcsharp_shutdown_delegate();
            public delegate IntPtr grpcsharp_version_string_delegate();  // returns not-owned const char*

            public delegate BatchContextSafeHandle grpcsharp_batch_context_create_delegate();
            public delegate IntPtr grpcsharp_batch_context_recv_initial_metadata_delegate(BatchContextSafeHandle ctx);
            public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx);
            public delegate void grpcsharp_batch_context_recv_message_to_buffer_delegate(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
            public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx);
            public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx, out UIntPtr detailsLength);
            public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx);
            public delegate int grpcsharp_batch_context_recv_close_on_server_cancelled_delegate(BatchContextSafeHandle ctx);
            public delegate void grpcsharp_batch_context_destroy_delegate(IntPtr ctx);

            public delegate RequestCallContextSafeHandle grpcsharp_request_call_context_create_delegate();
            public delegate CallSafeHandle grpcsharp_request_call_context_call_delegate(RequestCallContextSafeHandle ctx);
            public delegate IntPtr grpcsharp_request_call_context_method_delegate(RequestCallContextSafeHandle ctx, out UIntPtr methodLength);
            public delegate IntPtr grpcsharp_request_call_context_host_delegate(RequestCallContextSafeHandle ctx, out UIntPtr hostLength);
            public delegate Timespec grpcsharp_request_call_context_deadline_delegate(RequestCallContextSafeHandle ctx);
            public delegate IntPtr grpcsharp_request_call_context_request_metadata_delegate(RequestCallContextSafeHandle ctx);
            public delegate void grpcsharp_request_call_context_destroy_delegate(IntPtr ctx);

            public delegate CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create_delegate(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2);
            public delegate void grpcsharp_call_credentials_release_delegate(IntPtr credentials);

            public delegate CallError grpcsharp_call_cancel_delegate(CallSafeHandle call);
            public delegate CallError grpcsharp_call_cancel_with_status_delegate(CallSafeHandle call, StatusCode status, string description);
            public delegate CallError grpcsharp_call_start_unary_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
            public delegate CallError grpcsharp_call_start_client_streaming_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
            public delegate CallError grpcsharp_call_start_server_streaming_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags,
                MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
            public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
            public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
            public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx);
            public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata,
                byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags);
            public delegate CallError grpcsharp_call_recv_message_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx);
            public delegate CallError grpcsharp_call_recv_initial_metadata_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx);
            public delegate CallError grpcsharp_call_start_serverside_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx);
            public delegate CallError grpcsharp_call_send_initial_metadata_delegate(CallSafeHandle call,
                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
            public delegate CallError grpcsharp_call_set_credentials_delegate(CallSafeHandle call, CallCredentialsSafeHandle credentials);
            public delegate CStringSafeHandle grpcsharp_call_get_peer_delegate(CallSafeHandle call);
            public delegate void grpcsharp_call_destroy_delegate(IntPtr call);

            public delegate ChannelArgsSafeHandle grpcsharp_channel_args_create_delegate(UIntPtr numArgs);
            public delegate void grpcsharp_channel_args_set_string_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, string value);
            public delegate void grpcsharp_channel_args_set_integer_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, int value);
            public delegate void grpcsharp_channel_args_destroy_delegate(IntPtr args);

            public delegate void grpcsharp_override_default_ssl_roots(string pemRootCerts);
            public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
            public delegate ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create_delegate(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds);
            public delegate void grpcsharp_channel_credentials_release_delegate(IntPtr credentials);

            public delegate ChannelSafeHandle grpcsharp_insecure_channel_create_delegate(string target, ChannelArgsSafeHandle channelArgs);
            public delegate ChannelSafeHandle grpcsharp_secure_channel_create_delegate(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs);
            public delegate CallSafeHandle grpcsharp_channel_create_call_delegate(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline);
            public delegate ChannelState grpcsharp_channel_check_connectivity_state_delegate(ChannelSafeHandle channel, int tryToConnect);
            public delegate void grpcsharp_channel_watch_connectivity_state_delegate(ChannelSafeHandle channel, ChannelState lastObservedState,
                Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
            public delegate CStringSafeHandle grpcsharp_channel_get_target_delegate(ChannelSafeHandle call);
            public delegate void grpcsharp_channel_destroy_delegate(IntPtr channel);

            public delegate int grpcsharp_sizeof_grpc_event_delegate();

            public delegate CompletionQueueSafeHandle grpcsharp_completion_queue_create_delegate();
            public delegate void grpcsharp_completion_queue_shutdown_delegate(CompletionQueueSafeHandle cq);
            public delegate CompletionQueueEvent grpcsharp_completion_queue_next_delegate(CompletionQueueSafeHandle cq);
            public delegate CompletionQueueEvent grpcsharp_completion_queue_pluck_delegate(CompletionQueueSafeHandle cq, IntPtr tag);
            public delegate void grpcsharp_completion_queue_destroy_delegate(IntPtr cq);

            public delegate void gprsharp_free_delegate(IntPtr ptr);

            public delegate MetadataArraySafeHandle grpcsharp_metadata_array_create_delegate(UIntPtr capacity);
            public delegate void grpcsharp_metadata_array_add_delegate(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength);
            public delegate UIntPtr grpcsharp_metadata_array_count_delegate(IntPtr metadataArray);
            public delegate IntPtr grpcsharp_metadata_array_get_key_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr keyLength);
            public delegate IntPtr grpcsharp_metadata_array_get_value_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength);
            public delegate void grpcsharp_metadata_array_destroy_full_delegate(IntPtr array);

            public delegate void grpcsharp_redirect_log_delegate(GprLogDelegate callback);

            public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(NativeMetadataInterceptor interceptor);
            public delegate void grpcsharp_metadata_credentials_notify_from_plugin_delegate(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);

            public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth);
            public delegate void grpcsharp_server_credentials_release_delegate(IntPtr credentials);

            public delegate ServerSafeHandle grpcsharp_server_create_delegate(ChannelArgsSafeHandle args);
            public delegate void grpcsharp_server_register_completion_queue_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq);
            public delegate int grpcsharp_server_add_insecure_http2_port_delegate(ServerSafeHandle server, string addr);
            public delegate int grpcsharp_server_add_secure_http2_port_delegate(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds);
            public delegate void grpcsharp_server_start_delegate(ServerSafeHandle server);
            public delegate CallError grpcsharp_server_request_call_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, RequestCallContextSafeHandle ctx);
            public delegate void grpcsharp_server_cancel_all_calls_delegate(ServerSafeHandle server);
            public delegate void grpcsharp_server_shutdown_and_notify_callback_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
            public delegate void grpcsharp_server_destroy_delegate(IntPtr server);

            public delegate Timespec gprsharp_now_delegate(ClockType clockType);
            public delegate Timespec gprsharp_inf_future_delegate(ClockType clockType);
            public delegate Timespec gprsharp_inf_past_delegate(ClockType clockType);

            public delegate Timespec gprsharp_convert_clock_type_delegate(Timespec t, ClockType targetClock);
            public delegate int gprsharp_sizeof_timespec_delegate();

            public delegate CallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback);
            public delegate IntPtr grpcsharp_test_nop_delegate(IntPtr ptr);
        }
    }
}


text:
import os
import conda_smithy.configure_feedstock as cnfgr_fdstk

import pytest
import copy
import yaml
import textwrap

def test_noarch_skips_appveyor(noarch_recipe, jinja_env):
    noarch_recipe.config["provider"]["win"] = "appveyor"
    cnfgr_fdstk.render_appveyor(
        jinja_env=jinja_env,
        forge_config=noarch_recipe.config,
        forge_dir=noarch_recipe.recipe,
    )
    # this configuration should be skipped
    assert not noarch_recipe.config["appveyor"]["enabled"]
    assert not os.path.isdir(os.path.join(noarch_recipe.recipe, ".ci_support"))

def test_noarch_skips_travis(noarch_recipe, jinja_env):
    cnfgr_fdstk.render_travis(
        jinja_env=jinja_env,
        forge_config=noarch_recipe.config,
        forge_dir=noarch_recipe.recipe,
    )
    # this configuration should be skipped
    assert not noarch_recipe.config["travis"]["enabled"]
    assert not os.path.isdir(os.path.join(noarch_recipe.recipe, ".ci_support"))

@pytest.mark.legacy_circle
def test_noarch_runs_on_circle(noarch_recipe, jinja_env):
    noarch_recipe.config["provider"]["linux"] = "circle"

    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env,
        forge_config=noarch_recipe.config,
        forge_dir=noarch_recipe.recipe,
    )

    # this configuration should be run
    assert noarch_recipe.config["circle"]["enabled"]
    matrix_dir = os.path.join(noarch_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # single matrix entry - readme is generated later in main function
    assert len(os.listdir(matrix_dir)) == 1

@pytest.mark.parametrize("recipe_dirname", ["recipe", "custom_recipe_dir"])
def test_noarch_runs_on_azure(noarch_recipe, jinja_env):
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=noarch_recipe.config,
        forge_dir=noarch_recipe.recipe,
    )
    # this configuration should be run
    assert noarch_recipe.config["azure"]["enabled"]
    matrix_dir = os.path.join(noarch_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # single matrix entry - readme is generated later in main function
    assert len(os.listdir(matrix_dir)) == 1

def test_r_skips_appveyor(r_recipe, jinja_env):
    r_recipe.config["provider"]["win"] = "appveyor"
    cnfgr_fdstk.render_appveyor(
        jinja_env=jinja_env,
        forge_config=r_recipe.config,
        forge_dir=r_recipe.recipe,
    )
    # this configuration should be skipped
    assert not r_recipe.config["appveyor"]["enabled"]
    assert not os.path.isdir(os.path.join(r_recipe.recipe, ".ci_support"))

@pytest.mark.legacy_travis
def test_r_matrix_travis(r_recipe, jinja_env):
    r_recipe.config["provider"]["osx"] = "travis"

    cnfgr_fdstk.render_travis(
        jinja_env=jinja_env,
        forge_config=r_recipe.config,
        forge_dir=r_recipe.recipe,
    )
    # this configuration should be run
    assert r_recipe.config["travis"]["enabled"]
    matrix_dir = os.path.join(r_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # single matrix entry - readme is generated later in main function
    assert len(os.listdir(matrix_dir)) == 2

@pytest.mark.legacy_circle
def test_r_matrix_on_circle(r_recipe, jinja_env):
    r_recipe.config["provider"]["linux"] = "circle"

    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env,
        forge_config=r_recipe.config,
        forge_dir=r_recipe.recipe,
    )
    # this configuration should be run
    assert r_recipe.config["circle"]["enabled"]
    matrix_dir = os.path.join(r_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # single matrix entry - readme is generated later in main function
    assert len(os.listdir(matrix_dir)) == 2

def test_r_matrix_azure(r_recipe, jinja_env):
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=r_recipe.config,
        forge_dir=r_recipe.recipe,
    )
    # this configuration should be run
    assert r_recipe.config["azure"]["enabled"]
    matrix_dir = os.path.join(r_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # single matrix entry - readme is generated later in main function
    assert len(os.listdir(matrix_dir)) == 4

def test_py_matrix_appveyor(py_recipe, jinja_env):
    py_recipe.config["provider"]["win"] = "appveyor"
    cnfgr_fdstk.render_appveyor(
        jinja_env=jinja_env,
        forge_config=py_recipe.config,
        forge_dir=py_recipe.recipe,
    )
    # this configuration should be skipped
    assert py_recipe.config["appveyor"]["enabled"]
    matrix_dir = os.path.join(py_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # 2 python versions, 2 target_platforms.  Recipe uses c_compiler, but this is a zipped key
    #     and shouldn't add extra configurations
    assert len(os.listdir(matrix_dir)) == 4

@pytest.mark.legacy_travis
def test_py_matrix_travis(py_recipe, jinja_env):
    py_recipe.config["provider"]["osx"] = "travis"

    cnfgr_fdstk.render_travis(
        jinja_env=jinja_env,
        forge_config=py_recipe.config,
        forge_dir=py_recipe.recipe,
    )
    # this configuration should be run
    assert py_recipe.config["travis"]["enabled"]
    matrix_dir = os.path.join(py_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # two matrix enties - one per py ver
    assert len(os.listdir(matrix_dir)) == 2

@pytest.mark.legacy_circle
def test_py_matrix_on_circle(py_recipe, jinja_env):
    py_recipe.config["provider"]["linux"] = "circle"

    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env,
        forge_config=py_recipe.config,
        forge_dir=py_recipe.recipe,
    )
    # this configuration should be run
    assert py_recipe.config["circle"]["enabled"]
    matrix_dir = os.path.join(py_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # single matrix entry - readme is generated later in main function
    assert len(os.listdir(matrix_dir)) == 2

def test_py_matrix_on_azure(py_recipe, jinja_env):
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=py_recipe.config,
        forge_dir=py_recipe.recipe,
    )
    # this configuration should be run
    assert py_recipe.config["azure"]["enabled"]
    matrix_dir = os.path.join(py_recipe.recipe, ".ci_support")
    assert os.path.isdir(matrix_dir)
    # single matrix entry - readme is generated later in main function
    assert len(os.listdir(matrix_dir)) == 8

def test_upload_on_branch_azure(upload_on_branch_recipe, jinja_env):
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=upload_on_branch_recipe.config,
        forge_dir=upload_on_branch_recipe.recipe,
    )
    # Check that the parameter is in the configuration.
    assert "upload_on_branch" in upload_on_branch_recipe.config
    assert upload_on_branch_recipe.config["upload_on_branch"] == "foo-branch"
    # Check that the parameter is in the generated file.
    with open(
        os.path.join(
            upload_on_branch_recipe.recipe,
            ".azure-pipelines",
            "azure-pipelines-osx.yml",
        )
    ) as fp:
        content_osx = yaml.load(fp)
    assert (
        'UPLOAD_ON_BRANCH="foo-branch"'
        in content_osx["jobs"][0]["steps"][0]["script"]
    )
    assert (
        "BUILD_SOURCEBRANCHNAME"
        in content_osx["jobs"][0]["steps"][0]["script"]
    )

    with open(
        os.path.join(
            upload_on_branch_recipe.recipe,
            ".azure-pipelines",
            "azure-pipelines-win.yml",
        )
    ) as fp:
        content_win = yaml.load(fp)
    assert (
        "UPLOAD_ON_BRANCH=foo-branch"
        in content_win["jobs"][0]["steps"][-1]["script"]
    )
    assert (
        "BUILD_SOURCEBRANCHNAME"
        in content_win["jobs"][0]["steps"][-1]["script"]
    )

    with open(
        os.path.join(
            upload_on_branch_recipe.recipe,
            ".azure-pipelines",
            "azure-pipelines-linux.yml",
        )
    ) as fp:
        content_lin = yaml.load(fp)
    assert (
        'UPLOAD_ON_BRANCH="foo-branch"'
        in content_lin["jobs"][0]["steps"][2]["script"]
    )
    assert (
        "BUILD_SOURCEBRANCHNAME"
        in content_lin["jobs"][0]["steps"][2]["script"]
    )

def test_upload_on_branch_appveyor(upload_on_branch_recipe, jinja_env):
    upload_on_branch_recipe.config["provider"]["win"] = "appveyor"
    cnfgr_fdstk.render_appveyor(
        jinja_env=jinja_env,
        forge_config=upload_on_branch_recipe.config,
        forge_dir=upload_on_branch_recipe.recipe,
    )
    # Check that the parameter is in the configuration.
    assert "upload_on_branch" in upload_on_branch_recipe.config
    assert upload_on_branch_recipe.config["upload_on_branch"] == "foo-branch"

    # Check that the parameter is in the generated file.
    with open(
        os.path.join(upload_on_branch_recipe.recipe, ".appveyor.yml")
    ) as fp:
        content = yaml.load(fp)
    assert "%APPVEYOR_REPO_BRANCH%" in content["deploy_script"][0]
    assert "UPLOAD_ON_BRANCH=foo-branch" in content["deploy_script"][-2]

def test_circle_with_yum_reqs(py_recipe, jinja_env):
    with open(
        os.path.join(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w"
    ) as f:
        f.write("nano\n")
    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env,
        forge_config=py_recipe.config,
        forge_dir=py_recipe.recipe,
    )

@pytest.mark.legacy_circle
def test_circle_with_empty_yum_reqs_raises(py_recipe, jinja_env):
    py_recipe.config["provider"]["linux"] = "circle"

    with open(
        os.path.join(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w"
    ) as f:
        f.write("# effectively empty")
    with pytest.raises(ValueError):
        cnfgr_fdstk.render_circle(
            jinja_env=jinja_env,
            forge_config=py_recipe.config,
            forge_dir=py_recipe.recipe,
        )

def test_azure_with_empty_yum_reqs_raises(py_recipe, jinja_env):
    with open(
        os.path.join(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w"
    ) as f:
        f.write("# effectively empty")
    with pytest.raises(ValueError):
        cnfgr_fdstk.render_azure(
            jinja_env=jinja_env,
            forge_config=py_recipe.config,
            forge_dir=py_recipe.recipe,
        )

@pytest.mark.legacy_circle
@pytest.mark.legacy_travis
def test_circle_osx(py_recipe, jinja_env):
    # Set legacy providers
    py_recipe.config["provider"]["osx"] = "travis"
    py_recipe.config["provider"]["linux"] = "circle"

    forge_dir = py_recipe.recipe
    travis_yml_file = os.path.join(forge_dir, ".travis.yml")
    circle_osx_file = os.path.join(forge_dir, ".scripts", "run_osx_build.sh")
    circle_linux_file = os.path.join(
        forge_dir, ".scripts", "run_docker_build.sh"
    )
    circle_config_file = os.path.join(forge_dir, ".circleci", "config.yml")

    cnfgr_fdstk.clear_scripts(forge_dir)
    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env, forge_config=py_recipe.config, forge_dir=forge_dir
    )
    assert not os.path.exists(circle_osx_file)
    assert os.path.exists(circle_linux_file)
    assert os.path.exists(circle_config_file)
    cnfgr_fdstk.render_travis(
        jinja_env=jinja_env, forge_config=py_recipe.config, forge_dir=forge_dir
    )
    assert os.path.exists(travis_yml_file)

    cnfgr_fdstk.clear_scripts(forge_dir)
    config = copy.deepcopy(py_recipe.config)
    config["provider"]["osx"] = "circle"
    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir
    )
    assert os.path.exists(circle_osx_file)
    assert os.path.exists(circle_linux_file)
    assert os.path.exists(circle_config_file)
    cnfgr_fdstk.render_travis(
        jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir
    )
    assert not os.path.exists(travis_yml_file)

    cnfgr_fdstk.clear_scripts(forge_dir)
    config = copy.deepcopy(py_recipe.config)
    config["provider"]["linux"] = "dummy"
    config["provider"]["osx"] = "circle"
    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir
    )
    assert os.path.exists(circle_osx_file)
    assert not os.path.exists(circle_linux_file)
    assert os.path.exists(circle_config_file)

def test_circle_skipped(linux_skipped_recipe, jinja_env):
    forge_dir = linux_skipped_recipe.recipe
    circle_osx_file = os.path.join(forge_dir, ".scripts", "run_osx_build.sh")
    circle_linux_file = os.path.join(
        forge_dir, ".scripts", "run_docker_build.sh"
    )
    circle_config_file = os.path.join(forge_dir, ".circleci", "config.yml")

    config = copy.deepcopy(linux_skipped_recipe.config)
    cnfgr_fdstk.copy_feedstock_content(config, forge_dir)
    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env,
        forge_config=linux_skipped_recipe.config,
        forge_dir=forge_dir,
    )
    assert not os.path.exists(circle_osx_file)
    assert not os.path.exists(circle_linux_file)
    assert os.path.exists(circle_config_file)

    config["provider"]["osx"] = "circle"

    cnfgr_fdstk.copy_feedstock_content(config, forge_dir)
    cnfgr_fdstk.render_circle(
        jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir
    )
    assert os.path.exists(circle_osx_file)
    assert not os.path.exists(circle_linux_file)
    assert os.path.exists(circle_config_file)

def test_render_with_all_skipped_generates_readme(skipped_recipe, jinja_env):
    cnfgr_fdstk.render_README(
        jinja_env=jinja_env,
        forge_config=skipped_recipe.config,
        forge_dir=skipped_recipe.recipe,
    )
    readme_path = os.path.join(skipped_recipe.recipe, "README.md")
    assert os.path.exists(readme_path)
    with open(readme_path, "rb") as readme_file:
        content = readme_file.read()
    assert b"skip-test-meta" in content

def test_render_windows_with_skipped_python(python_skipped_recipe, jinja_env):
    config = python_skipped_recipe.config
    config["provider"]["win"] = "appveyor"
    config["exclusive_config_file"] = os.path.join(
        python_skipped_recipe.recipe, "recipe", "long_config.yaml"
    )
    cnfgr_fdstk.render_appveyor(
        jinja_env=jinja_env,
        forge_config=config,
        forge_dir=python_skipped_recipe.recipe,
    )
    # this configuration should be skipped
    assert python_skipped_recipe.config["appveyor"]["enabled"]

    matrix_dir = os.path.join(python_skipped_recipe.recipe, ".ci_support")
    # matrix has 2.7, 3.5, 3.6, but 3.6 is skipped.  Should be 2 entries.
    assert len(os.listdir(matrix_dir)) == 2

def test_readme_has_terminating_newline(noarch_recipe, jinja_env):
    cnfgr_fdstk.render_README(
        jinja_env=jinja_env,
        forge_config=noarch_recipe.config,
        forge_dir=noarch_recipe.recipe,
    )
    readme_path = os.path.join(noarch_recipe.recipe, "README.md")
    assert os.path.exists(readme_path)
    with open(readme_path, "rb") as readme_file:
        readme_file.seek(-1, os.SEEK_END)
        assert readme_file.read() == b"\n"

def test_secrets(py_recipe, jinja_env):
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=py_recipe.config,
        forge_dir=py_recipe.recipe,
    )

    run_docker_build = os.path.join(
        py_recipe.recipe, ".scripts", "run_docker_build.sh"
    )
    with open(run_docker_build, "rb") as run_docker_build_file:
        content = run_docker_build_file.read()
    assert b"-e BINSTAR_TOKEN" in content

    for config_yaml in os.listdir(
        os.path.join(py_recipe.recipe, ".azure-pipelines")
    ):
        if config_yaml.endswith(".yaml"):
            with open(config_yaml) as fo:
                config = yaml.safe_load(fo)
                if "jobs" in config:
                    assert any(
                        any(
                            step.get("env", {}).get("BINSTAR_TOKEN", None)
                            == "$(BINSTAR_TOKEN)"
                            for step in job["steps"]
                        )
                        for job in config["jobs"]
                    )

    py_recipe.config["provider"]["linux_aarch64"] = "drone"
    cnfgr_fdstk.render_drone(
        jinja_env=jinja_env,
        forge_config=py_recipe.config,
        forge_dir=py_recipe.recipe,
    )

    with open(os.path.join(py_recipe.recipe, ".drone.yml")) as fo:
        config = list(yaml.safe_load_all(fo))[-1]
        assert any(
            step.get("environment", {})
            .get("BINSTAR_TOKEN", {})
            .get("from_secret", None)
            == "BINSTAR_TOKEN"
            for step in config["steps"]
        )

def test_migrator_recipe(recipe_migration_cfep9, jinja_env):
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=recipe_migration_cfep9.config,
        forge_dir=recipe_migration_cfep9.recipe,
    )

    with open(
        os.path.join(
            recipe_migration_cfep9.recipe,
            ".ci_support",
            "linux_64_python2.7.yaml",
        )
    ) as fo:
        variant = yaml.safe_load(fo)
        assert variant["zlib"] == ["1000"]

def test_migrator_cfp_override(recipe_migration_cfep9, jinja_env):
    cfp_file = recipe_migration_cfep9.config["exclusive_config_file"]
    cfp_migration_dir = os.path.join(
        os.path.dirname(cfp_file), "share", "conda-forge", "migrations"
    )
    os.makedirs(cfp_migration_dir, exist_ok=True)
    with open(os.path.join(cfp_migration_dir, "zlib2.yaml"), "w") as f:
        f.write(
            textwrap.dedent(
                """
                migrator_ts: 1
                zlib:
                   - 1001
                """
            )
        )
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=recipe_migration_cfep9.config,
        forge_dir=recipe_migration_cfep9.recipe,
    )

    with open(
        os.path.join(
            recipe_migration_cfep9.recipe,
            ".ci_support",
            "linux_64_python2.7.yaml",
        )
    ) as fo:
        variant = yaml.safe_load(fo)
        assert variant["zlib"] == ["1001"]

def test_migrator_delete_old(recipe_migration_cfep9, jinja_env):
    cfp_file = recipe_migration_cfep9.config["exclusive_config_file"]
    cfp_migration_dir = os.path.join(
        os.path.dirname(cfp_file), "share", "conda-forge", "migrations"
    )
    assert os.path.exists(
        os.path.join(
            recipe_migration_cfep9.recipe,
            ".ci_support",
            "migrations",
            "zlib.yaml",
        )
    )
    os.makedirs(cfp_migration_dir, exist_ok=True)
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=recipe_migration_cfep9.config,
        forge_dir=recipe_migration_cfep9.recipe,
    )
    assert not os.path.exists(
        os.path.join(
            recipe_migration_cfep9.recipe,
            ".ci_support",
            "migrations",
            "zlib.yaml",
        )
    )

def test_migrator_downgrade_recipe(
    recipe_migration_cfep9_downgrade, jinja_env
):
    """
    Assert that even when we have two migrations targeting the same file the correct one wins.
    """
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=recipe_migration_cfep9_downgrade.config,
        forge_dir=recipe_migration_cfep9_downgrade.recipe,
    )
    assert (
        len(
            os.listdir(
                os.path.join(
                    recipe_migration_cfep9_downgrade.recipe,
                    ".ci_support",
                    "migrations",
                )
            )
        )
        == 2
    )

    with open(
        os.path.join(
            recipe_migration_cfep9_downgrade.recipe,
            ".ci_support",
            "linux_64_python2.7.yaml",
        )
    ) as fo:
        variant = yaml.safe_load(fo)
        assert variant["zlib"] == ["1000"]

def test_migrator_compiler_version_recipe(
    recipe_migration_win_compiled, jinja_env
):
    """
    Assert that even when we have two migrations targeting the same file the correct one wins.
    """
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=recipe_migration_win_compiled.config,
        forge_dir=recipe_migration_win_compiled.recipe,
    )
    assert (
        len(
            os.listdir(
                os.path.join(
                    recipe_migration_win_compiled.recipe,
                    ".ci_support",
                    "migrations",
                )
            )
        )
        == 1
    )

    rendered_variants = os.listdir(
        os.path.join(recipe_migration_win_compiled.recipe, ".ci_support")
    )

    # TODO: remove multiple target_platform entries
    assert (
        "win_64_c_compilervs2008python2.7target_platformwin-32.yaml"
        in rendered_variants
    )
    assert "win_64_c_compilervs2008python2.7.yaml" in rendered_variants
    assert (
        "win_64_c_compilervs2017python3.5target_platformwin-32.yaml"
        in rendered_variants
    )
    assert "win_64_c_compilervs2017python3.5.yaml" in rendered_variants

def test_files_skip_render(render_skipped_recipe, jinja_env):
    cnfgr_fdstk.render_README(
        jinja_env=jinja_env,
        forge_config=render_skipped_recipe.config,
        forge_dir=render_skipped_recipe.recipe,
    )
    cnfgr_fdstk.copy_feedstock_content(
        render_skipped_recipe.config, render_skipped_recipe.recipe
    )
    skipped_files = [
        ".gitignore",
        ".gitattributes",
        "README.md",
        "LICENSE.txt",
        ".github/workflows/webservices.yml",
    ]
    for f in skipped_files:
        fpath = os.path.join(render_skipped_recipe.recipe, f)
        assert not os.path.exists(fpath)

def test_choco_install(choco_recipe, jinja_env):
    cnfgr_fdstk.render_azure(
        jinja_env=jinja_env,
        forge_config=choco_recipe.config,
        forge_dir=choco_recipe.recipe,
    )
    azure_file = os.path.join(
        os.path.join(
            choco_recipe.recipe, ".azure-pipelines", "azure-pipelines-win.yml"
        )
    )
    assert os.path.isfile(azure_file)
    with open(azure_file) as f:
        contents = f.read()
    exp = """
    - script: |
        choco install pkg0 -fdv -y --debug
      displayName: "Install Chocolatey Package: pkg0"

    - script: |
        choco install pkg1 --version=X.Y.Z -fdv -y --debug
      displayName: "Install Chocolatey Package: pkg1 --version=X.Y.Z"
""".strip()
    assert exp in contents

def test_webservices_action_exists(py_recipe, jinja_env):
    cnfgr_fdstk.copy_feedstock_content(py_recipe.config, py_recipe.recipe)
    assert os.path.exists(
        os.path.join(py_recipe.recipe, ".github/workflows/webservices.yml")
    )
    with open(
        os.path.join(py_recipe.recipe, ".github/workflows/webservices.yml")
    ) as f:
        action_config = yaml.safe_load(f)
    assert "jobs" in action_config
    assert "webservices" in action_config["jobs"]

def test_automerge_action_exists(py_recipe, jinja_env):
    cfg = copy.deepcopy(py_recipe.config)
    cnfgr_fdstk.copy_feedstock_content(cfg, py_recipe.recipe)
    assert os.path.exists(
        os.path.join(py_recipe.recipe, ".github/workflows/automerge.yml")
    )
    with open(
        os.path.join(py_recipe.recipe, ".github/workflows/automerge.yml")
    ) as f:
        action_config = yaml.safe_load(f)
    assert "jobs" in action_config
    assert "automerge-action" in action_config["jobs"]

def test_conda_forge_yaml_empty(config_yaml):
    load_forge_config = lambda: cnfgr_fdstk._load_forge_config(
        config_yaml,
        exclusive_config_file=os.path.join(
            config_yaml, "recipe", "default_config.yaml"
        ),
    )

    assert ["conda-forge", "main"] in load_forge_config()["channels"][
        "targets"
    ]

    os.unlink(os.path.join(config_yaml, "conda-forge.yml"))
    with pytest.raises(RuntimeError):
        load_forge_config()

    with open(os.path.join(config_yaml, "conda-forge.yml"), "w"):
        pass
    assert ["conda-forge", "main"] in load_forge_config()["channels"][
        "targets"
    ]

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wikidot="http://www.wikidot.com/rss-namespace">

	<channel>
		<title>Comments for page "Hard of Hearing"</title>
		<link>http://bvs.wikidot.com/forum/t-251605/hard-of-hearing</link>
		<description>Posts in the discussion thread "Hard of Hearing"</description>
				<copyright></copyright>
		<lastBuildDate>Sun, 10 Jul 2022 04:01:37 +0000</lastBuildDate>

				</channel>
</rss>


text:
package org.zeroturnaround.zip;

import junit.framework.TestCase;

import java.io.File;

/**
 * @author Innokenty Shuvalov
 */
public class FileSourceTest extends TestCase {

  /** @noinspection ConstantConditions*/
  private final File testFile
      = new File(getClass().getClassLoader().getResource("TestFile.txt").getPath());

  /** @noinspection ConstantConditions*/
  private final File testFileII
      = new File(getClass().getClassLoader().getResource("TestFile-II.txt").getPath());

  private final String name1 = "Changed-TestFile.txt";
  private final String name2 = "Changed-TestFile-II.txt";

  public void testPair() throws Exception {
    FileSource[] pairs = FileSource.pair(
        new File[]{testFile, testFileII},
        new String[]{name1, name2}
    );

    assertEquals(pairs.length, 2);
    assertEquals(pairs[0].getEntry().getName(), name1);
    assertEquals(pairs[1].getEntry().getName(), name2);
  }

  public void testPairThrowsExceptionWhenNotEnoughNames() throws Exception {
    try {
      FileSource.pair(
          new File[]{testFile, testFileII},
          new String[]{name1}
      );
    } catch (IllegalArgumentException e) {
      return;
    }
    fail("FileSource.pair must throw an IllegalArgumentException " +
        "if the names array is less than files array");
  }

  public void testPairDoesNotThrowExceptionWhenTooManyNames() throws Exception {
    try {
      FileSource.pair(
          new File[]{testFile},
          new String[]{name1, name2}
      );
    } catch (IllegalArgumentException e) {
      fail("FileSource.pair must not throw an IllegalArgumentException " +
          "if the names array is bigger than files array");
    }
  }
}


text:
package laika.helium.config

import cats.data.NonEmptyList
import laika.ast.RelativePath.CurrentDocument
import laika.ast._
import laika.config.LaikaKeys
import laika.parse.{GeneratedSource, SourceFragment}
import laika.rewrite.Versions

/** A Helium link type available for navigation bars and the landing page.
  */
sealed trait ThemeLink extends Unresolved {

  val source: SourceFragment = GeneratedSource

  type Self <: ThemeLink

  def unresolvedMessage: String = s"Unresolved theme link: $this"

  def runsIn (phase: RewritePhase): Boolean = phase.isInstanceOf[RewritePhase.Render]

}

sealed trait ThemeLinkSpan extends ThemeLink with SpanResolver {
  type Self <: ThemeLinkSpan
}
sealed trait ThemeLinkBlock extends ThemeLink with BlockResolver {
  type Self <: ThemeLinkBlock
}

sealed trait SingleTargetLink extends ThemeLinkSpan {

  /** The target of the link, either internal or external. */
  def target: Target

  def resolve (cursor: DocumentCursor): Span = target match {
    case et: ExternalTarget => createLink(et)
    case it: InternalTarget => cursor.validateAndRecover(createLink(it), source)
  }

  protected def createLink (target: Target): Link

}

sealed trait MultiTargetLink extends ThemeLink {

  def links: Seq[SingleTargetLink]

}

/** A link consisting of an icon and optional text.
  */
sealed abstract case class IconLink (target: Target, icon: Icon, text: Option[String] = None, options: Options = NoOpt) extends SingleTargetLink {
  type Self = IconLink
  protected def createLink (target: Target): Link = {
    val iconTypeStyle = icon match {
      case _: IconGlyph => "glyph-link"
      case _: IconStyle => "style-link"
      case _: InlineSVGIcon => "svg-link"
      case _: SVGSymbolIcon => "svg-link"
    }
    val allOptions = HeliumStyles.iconLink + Styles(iconTypeStyle) + options
    SpanLink(icon +: text.map(Text(_)).toSeq, target, options = allOptions)
  }
  def withOptions(newOptions: Options): IconLink = new IconLink(target, icon, text, newOptions) {}
}

object IconLink {
  /** Creates an icon link to an external target, consisting of an icon and optional text. */
  def external (url: String, icon: Icon, text: Option[String] = None, options: Options = NoOpt): IconLink =
    new IconLink(ExternalTarget(url), icon, text, options) {}
  /** Creates an icon link to an internal target, consisting of an icon and optional text. */
  def internal (path: Path, icon: Icon, text: Option[String] = None, options: Options = NoOpt): IconLink =
    new IconLink(InternalTarget(path), icon, text, options) {}
}

/** A link consisting of text and an optional icon, by default rendered in a rounded rectangle.
  */
sealed abstract case class ButtonLink (target: Target, text: String, icon: Option[Icon] = None, options: Options = NoOpt) extends SingleTargetLink {
  type Self = ButtonLink
  protected def createLink (target: Target): Link = SpanLink(icon.toSeq :+ Text(text), target, options = HeliumStyles.buttonLink + options)
  def withOptions(newOptions: Options): ButtonLink = new ButtonLink(target, text, icon, newOptions) {}
}

object ButtonLink {
  /** Creates a button link to an external target, consisting of text and an optional icon rendered in a rounded rectangle. */
  def external (url: String, text: String, icon: Option[Icon] = None, options: Options = NoOpt): ButtonLink =
    new ButtonLink(ExternalTarget(url), text, icon, options) {}
  /** Creates a button link to an internal target, consisting of text and an optional icon rendered in a rounded rectangle. */
  def internal (path: Path, text: String, icon: Option[Icon] = None, options: Options = NoOpt): ButtonLink =
    new ButtonLink(InternalTarget(path), text, icon, options) {}
}

/** A simple text link.
  */
sealed abstract case class TextLink (target: Target, text: String, options: Options = NoOpt) extends SingleTargetLink {
  type Self = TextLink
  protected def createLink (target: Target): Link = SpanLink(Seq(Text(text)), target, options = HeliumStyles.textLink + options)
  def withOptions(newOptions: Options): TextLink = new TextLink(target, text, newOptions) {}
}

object TextLink {
  /** Creates a simple text link to an external target. */
  def external (url: String, text: String, options: Options = NoOpt): TextLink =
    new TextLink(ExternalTarget(url), text, options) {}
  /** Creates a simple text link to an internal target. */
  def internal (path: Path, text: String, options: Options = NoOpt): TextLink =
    new TextLink(InternalTarget(path), text, options) {}
}

/** A simple image link.
  */
sealed abstract case class ImageLink (target: Target, image: Image, options: Options = NoOpt) extends SingleTargetLink {
  type Self = ImageLink
  protected def createLink (target: Target): Link = SpanLink(Seq(image), target, options = HeliumStyles.imageLink + options)
  def withOptions(newOptions: Options): ImageLink =
    new ImageLink(target, image, newOptions) {}
}

object ImageLink {
  /** Creates a simple image link to an external target. */
  def external (url: String, image: Image, options: Options = NoOpt): ImageLink =
    new ImageLink(ExternalTarget(url), image, options) {}
  /** Creates a simple image link to an internal target. */
  def internal (path: Path, image: Image, options: Options = NoOpt): ImageLink =
    new ImageLink(InternalTarget(path), image, options) {}
}

/** A home link that inserts a link to the title page of the root document tree (if available)
  * or otherwise an invalid element.
  */
final case class DynamicHomeLink (options: Options = NoOpt) extends ThemeLinkSpan {
  type Self = DynamicHomeLink

  def resolve (cursor: DocumentCursor): Span = {
    cursor.root.tree.titleDocument match {
      case Some(homePage) =>
        IconLink.internal(homePage.path, HeliumIcon.home).resolve(cursor)
      case None =>
        val message = "No target for home link found - for options see 'Theme Settings / Top Navigation Bar' in the manual"
        InvalidSpan(message, GeneratedSource)
    }
  }

  def withOptions(newOptions: Options): DynamicHomeLink = copy(options = newOptions)
}

object DynamicHomeLink {
  val default: DynamicHomeLink = DynamicHomeLink()
}

/** A generic group of theme links.
  *
  * Can be used to create structures like a row of icon links in a vertical column of text links.
  */
sealed abstract case class LinkGroup (links: Seq[SingleTargetLink], options: Options = NoOpt) extends ThemeLinkSpan with MultiTargetLink {
  type Self = LinkGroup
  def resolve (cursor: DocumentCursor): Span = SpanSequence(links.map(_.resolve(cursor)), HeliumStyles.linkRow + options)
  def withOptions(newOptions: Options): LinkGroup = new LinkGroup(links, newOptions) {}
}

object LinkGroup {
  def create (link: SingleTargetLink, links: SingleTargetLink*): LinkGroup = new LinkGroup(link +: links) {}
}

/** A menu for the top navigation bar or the landing page.
  */
sealed abstract case class Menu (label: Seq[Span], links: Seq[SingleTargetLink], options: Options = NoOpt) extends ThemeLinkBlock with MultiTargetLink {
  type Self = Menu

  def resolve (cursor: DocumentCursor): Block = {

    val toggle = SpanLink(label, InternalTarget(CurrentDocument()))
      .withOptions(HeliumStyles.textLink + HeliumStyles.menuToggle)

    val navLinks = links.map(_.resolve(cursor)).collect {
      case sl: SpanLink =>
        NavigationItem(SpanSequence(sl.content), Nil, Some(NavigationLink(sl.target)))
          .withOptions(Style.level(1))
    }

    val content = BlockSequence(NavigationList(navLinks)).withOptions(HeliumStyles.menuContent)

    BlockSequence(SpanSequence(toggle), content).withOptions(HeliumStyles.menuContainer + options)
  }
  def withOptions(newOptions: Options): Menu = new Menu(label, links, newOptions) {}
}

object Menu {
  def create (label: String, link: SingleTargetLink, links: SingleTargetLink*): Menu =
    new Menu(Seq(Text(label)), link +: links) {}
  def create (label: Seq[Span], link: SingleTargetLink, links: SingleTargetLink*): Menu =
    new Menu(label, link +: links) {}
}

sealed abstract case class VersionMenu (versionedLabelPrefix: String,
                                        unversionedLabel: String,
                                        links: Seq[SingleTargetLink],
                                        options: Options = NoOpt) extends ThemeLinkBlock with MultiTargetLink {
  type Self = VersionMenu

  def resolve (cursor: DocumentCursor): Block = {
    cursor.config.get[Versions].toOption.fold[Block](BlockSequence.empty) { versions =>
      val isVersioned = cursor.config.get[Boolean](LaikaKeys.versioned).getOrElse(false)
      val labelText =
        if (isVersioned) s"$versionedLabelPrefix ${versions.currentVersion.displayValue}"
        else unversionedLabel
      val menu = new Menu(Seq(Text(labelText)), links, HeliumStyles.versionMenu) {}
      menu.resolve(cursor)
    }
  }

  def withOptions(newOptions: Options): VersionMenu =
    new VersionMenu(versionedLabelPrefix, unversionedLabel, links, newOptions) {}
}

object VersionMenu {

  def create (versionedLabelPrefix: String = default.versionedLabelPrefix,
              unversionedLabel: String = default.unversionedLabel,
              additionalLinks: Seq[SingleTargetLink] = Nil): VersionMenu =
    new VersionMenu(versionedLabelPrefix, unversionedLabel, additionalLinks) {}

  val default: VersionMenu = create("Version", "Choose Version")

}

final case class ThemeNavigationSection (title: String, links: NonEmptyList[TextLink])

object ThemeNavigationSection {
  def apply (title: String, link: TextLink, links: TextLink*): ThemeNavigationSection =
    ThemeNavigationSection(title, NonEmptyList.of(link, links:_*))
}


text:
- add notification banner not to surprise the user too much... (it is rebranding)
- rename github or move repositories
- redirect personal to "my-bookmarks"
- add bookmarks similar to "snippets" (redirect where necessary)

### Later
- rename realm from **bookmarks** to **codever**