static void ValidateClient(TcpClient socket, Shape reference, ServerInfoMessage serverInfo, CreateShapeFunction createShape, ShapeValidationFunction validate, uint timeoutSec = 10u)
        {
            Stopwatch                    timer          = new Stopwatch();
            ServerInfoMessage            readServerInfo = new ServerInfoMessage();
            Dictionary <ulong, Resource> resources      = new Dictionary <ulong, Resource>();
            PacketBuffer                 packetBuffer   = new PacketBuffer(64 * 1024);
            Shape shape          = createShape();
            bool  endMsgReceived = false;
            bool  serverInfoRead = false;
            bool  shapeMsgRead   = false;

            timer.Start();

            // Keep looping until we get a CIdEnd ControlMessage or timeoutSec elapses.
            // Timeout ignored when debugger is attached.
            while (!endMsgReceived && (Debugger.IsAttached || timer.ElapsedMilliseconds / 1000u < timeoutSec))
            {
                if (socket.Available <= 0)
                {
                    Thread.Yield();
                    continue;
                }

                // Data available. Read from the network stream into a buffer and attempt to
                // read a valid message.
                packetBuffer.Append(socket.GetStream(), socket.Available);

                PacketBuffer completedPacket;
                bool         crcOk = true;
                while ((completedPacket = packetBuffer.PopPacket(out crcOk)) != null || !crcOk)
                {
                    Assert.True(crcOk);
                    if (crcOk)
                    {
                        if (packetBuffer.DroppedByteCount != 0)
                        {
                            Console.Error.WriteLine("Dropped {0} bad bytes", packetBuffer.DroppedByteCount);
                            packetBuffer.DroppedByteCount = 0;
                        }

                        Assert.Equal(PacketHeader.PacketMarker, completedPacket.Header.Marker);
                        Assert.Equal(PacketHeader.PacketVersionMajor, completedPacket.Header.VersionMajor);
                        Assert.Equal(PacketHeader.PacketVersionMinor, completedPacket.Header.VersionMinor);

                        NetworkReader packetReader = new NetworkReader(completedPacket.CreateReadStream(true));

                        switch (completedPacket.Header.RoutingID)
                        {
                        case (int)RoutingID.ServerInfo:
                            serverInfoRead = true;
                            Assert.True(readServerInfo.Read(packetReader));

                            // Validate server info.
                            Assert.Equal(serverInfo.TimeUnit, readServerInfo.TimeUnit);
                            Assert.Equal(serverInfo.DefaultFrameTime, readServerInfo.DefaultFrameTime);
                            Assert.Equal(serverInfo.CoordinateFrame, readServerInfo.CoordinateFrame);

                            unsafe
                            {
                                for (int i = 0; i < ServerInfoMessage.ReservedBytes; ++i)
                                {
                                    Assert.Equal(serverInfo.Reserved[i], readServerInfo.Reserved[i]);
                                }
                            }
                            break;

                        case (int)RoutingID.Control:
                        {
                            // Only interested in the CIdEnd message to mark the end of the stream.
                            ControlMessage msg = new ControlMessage();
                            Assert.True(msg.Read(packetReader));

                            if (completedPacket.Header.MessageID == (int)ControlMessageID.End)
                            {
                                endMsgReceived = true;
                            }
                            break;
                        }

                        case (int)RoutingID.Mesh:
                            HandleMeshMessage(completedPacket, packetReader, resources);
                            break;

                        default:
                            if (completedPacket.Header.RoutingID == reference.RoutingID)
                            {
                                shapeMsgRead = true;
                                HandleShapeMessage(completedPacket, packetReader, shape, reference);
                            }
                            break;
                        }
                    }
                }
                // else fail?
            }

            Assert.True(serverInfoRead);
            Assert.True(shapeMsgRead);
            Assert.True(endMsgReceived);

            // Validate the shape state.
            if (shapeMsgRead)
            {
                validate(shape, reference, resources);
            }
        }
        public static void TestShape(Shape reference, CreateShapeFunction createShape, ShapeValidationFunction validate)
        {
            ServerInfoMessage info = ServerInfoMessage.Default;

            info.CoordinateFrame = CoordinateFrame.XYZ;

            ServerSettings serverSettings = ServerSettings.Default;

            serverSettings.Flags    &= ~(ServerFlag.Collate | ServerFlag.Compress);
            serverSettings.PortRange = 1000;
            IServer server = new TcpServer(serverSettings);

            // std::cout << "Start on port " << serverSettings.listenPort << std::endl;
            Assert.True(server.ConnectionMonitor.Start(ConnectionMonitorMode.Asynchronous));
            // std::cout << "Server listening on port " << server.connectionMonitor()->port() << std::endl;;

            // Create client and connect.

            TcpClient client = new TcpClient("127.0.0.1", server.ConnectionMonitor.Port);

            // Wait for connection.
            if (server.ConnectionMonitor.WaitForConnections(5000u) > 0)
            {
                server.ConnectionMonitor.CommitConnections(null);
            }

            Assert.True(server.ConnectionCount > 0);
            Assert.True(client.Connected);

            // Send server messages from another thread. Otherwise large packets may block.
            Thread sendThread = new Thread(() =>
            {
                Assert.True(server.Create(reference) > 0);
                Assert.True(server.UpdateTransfers(0) >= 0);
                Assert.True(server.UpdateFrame(0.0f) > 0);

                // Send end message.
                ControlMessage ctrlMsg = new ControlMessage();
                Assert.True(ServerUtil.SendMessage(server, (ushort)RoutingID.Control, (ushort)ControlMessageID.End, ctrlMsg) > 0);
                Assert.True(server.UpdateFrame(0.0f) > 0);
            });

            sendThread.Start();

            // Process client messages.
            ValidateClient(client, reference, info, createShape, validate);

            client.Close();

            sendThread.Join();
            server.Close();

            server.ConnectionMonitor.Stop();
            server.ConnectionMonitor.Join();
        }