async Task <List <string> > PrepareTopicHashSubscriptions(TopicHashSelector selector)
        {
            Dictionary <string, List <string> > topicsByPublisher;
            Dictionary <string, List <string> > singleWildcardTopicsByPublisher;
            Dictionary <string, List <string> > multiWildcardTopicsByPublisher;

            const int NumPublishers         = 1;
            const int NumTopicsPerPublisher = 10000;

            TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);

            var topics = topicsByPublisher.FirstOrDefault().Value;
            var singleWildcardTopics = singleWildcardTopicsByPublisher.FirstOrDefault().Value;
            var multiWildcardTopics  = multiWildcardTopicsByPublisher.FirstOrDefault().Value;

            const string ClientId                = "Client1";
            var          logger                  = new Mockups.TestLogger();
            var          serverOptions           = new MQTTnet.Server.MqttServerOptions();
            var          eventContainer          = new MQTTnet.Server.MqttServerEventContainer();
            var          retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
            var          sessionManager          = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);

            _clientSession = new MQTTnet.Server.MqttSession(
                ClientId,
                false,
                new Dictionary <object, object>(),
                serverOptions,
                eventContainer,
                retainedMessagesManager,
                sessionManager
                );

            List <string> topicsToSubscribe;

            switch (selector)
            {
            case TopicHashSelector.SingleWildcard:
                topicsToSubscribe = singleWildcardTopics;
                break;

            case TopicHashSelector.MultiWildcard:
                topicsToSubscribe = multiWildcardTopics;
                break;

            default:
                topicsToSubscribe = topics;
                break;
            }
            foreach (var t in topicsToSubscribe)
            {
                var subPacket = new Packets.MqttSubscribePacket();
                var filter    = new Packets.MqttTopicFilter();
                filter.Topic = t;
                subPacket.TopicFilters.Add(filter);
                await _clientSession.SubscriptionsManager.Subscribe(subPacket, default(CancellationToken));
            }

            return(topics);
        }
        public void Check_Hash_Bucket_Depth()
        {
            Dictionary <string, List <string> > topicsByPublisher;
            Dictionary <string, List <string> > singleWildcardTopicsByPublisher;
            Dictionary <string, List <string> > multiWildcardTopicsByPublisher;

            const int NumPublishers         = 5000;
            const int NumTopicsPerPublisher = 10;

            TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);

            // There will be many 'similar' topics ending with, i.e. "sensor100", "sensor101", ...
            // Hash bucket depths should remain low.
            var    bucketDepths       = new Dictionary <UInt64, int>();
            int    maxBucketDepth     = 0;
            UInt64 maxBucketDepthHash = 0;

            var topicsByHash = new Dictionary <UInt64, List <string> >();

            foreach (var t in topicsByPublisher)
            {
                var topics = t.Value;
                foreach (var topic in topics)
                {
                    UInt64 topicHash;
                    UInt64 hashMask;
                    bool   hasWildcard;
                    MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out hashMask, out hasWildcard);

                    bucketDepths.TryGetValue(topicHash, out var currentValue);
                    ++currentValue;
                    bucketDepths[topicHash] = currentValue;

                    if (currentValue > maxBucketDepth)
                    {
                        maxBucketDepth     = currentValue;
                        maxBucketDepthHash = topicHash;
                    }

                    if (!topicsByHash.TryGetValue(topicHash, out var topicList))
                    {
                        topicList = new List <string>();
                        topicsByHash.Add(topicHash, topicList);
                    }
                    topicList.Add(topic);
                }
            }

            var maxDepthTopics = topicsByHash[maxBucketDepthHash];

            Console.Write("Max bucket depth is " + maxBucketDepth);

            // for the test case the bucket depth should be less than 100
            Assert.IsTrue(maxBucketDepth < 100, "Unexpected high topic hash bucket depth");
        }