/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

package org.apache.cassandra.transport;

import org.junit.After;
import org.junit.Assert;
import org.junit.Test;

import accord.utils.Gens;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import org.assertj.core.api.Assertions;

import static accord.utils.Property.qt;

public class CBUtilTest
{
    private static final ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
    private ByteBuf buf;

    @After
    public void tearDown()
    {
        if (buf != null && buf.refCnt() > 0)
            buf.release(buf.refCnt());
    }

    @Test
    public void stringList()
    {
        qt().forAll(Gens.lists(Gens.strings().all().ofLengthBetween(0, 20)).ofSizeBetween(0, 10)).check(list -> {
            int expectedSize = CBUtil.sizeOfStringList(list);
            ByteBuf body = CBUtil.allocator.buffer(expectedSize * 2);
            CBUtil.writeStringList(list, body);
            Assertions.assertThat(body.readableBytes()).isEqualTo(expectedSize);
            // In CASSANDRA-20234 the sizeOf method now reflects the write method, but the work to do this in read
            // was not done; read took a stance that it wants to limit things in CASSANDRA-8101 but that never made
            // it to the other methods... write and read are not compatable with the full domain of UTF-8, and fixing
            // that was higher bar than CASSANDRA-20234 wanted to tackle out of fear of unknown regressions it would cause.
        });
    }

    @Test
    public void writeAndReadString()
    {
        final String text = "if you're happy and you know it, write your tests";
        int size = CBUtil.sizeOfString(text);

        buf = allocator.heapBuffer(size);
        CBUtil.writeString(text, buf);
        Assert.assertEquals(size, buf.writerIndex());
        Assert.assertEquals(0, buf.readerIndex());
        Assert.assertEquals(text, CBUtil.readString(buf));
        Assert.assertEquals(buf.writerIndex(), buf.readerIndex());
    }

    @Test
    public void writeAndReadLongString()
    {
        final String text = "if you're happy and you know it, write your tests";
        int size = CBUtil.sizeOfLongString(text);

        buf = allocator.heapBuffer(size);
        CBUtil.writeLongString(text, buf);
        Assert.assertEquals(size, buf.writerIndex());
        Assert.assertEquals(0, buf.readerIndex());
        Assert.assertEquals(text, CBUtil.readLongString(buf));
        Assert.assertEquals(buf.writerIndex(), buf.readerIndex());
    }

    @Test
    public void writeAndReadAsciiString()
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < 128; i++)
            sb.append((char) i);
        String write = sb.toString();
        int size = CBUtil.sizeOfString(write);
        buf = allocator.heapBuffer(size);
        CBUtil.writeAsciiString(write, buf);
        String read = CBUtil.readString(buf);
        Assert.assertEquals(write, read);
    }

    @Test
    public void writeAndReadAsciiStringMismatchWithNonUSAscii()
    {
        String invalidAsciiStr = "\u0080 \u0123 \u0321"; // a valid string contains no char > 0x007F
        int size = CBUtil.sizeOfString(invalidAsciiStr);
        buf = allocator.heapBuffer(size);
        CBUtil.writeAsciiString(invalidAsciiStr, buf);
        Assert.assertNotEquals("Characters (> 0x007F) is considered as 2 bytes in sizeOfString, meanwhile writeAsciiString writes just 1 byte",
                               size,
                               buf.writerIndex());
    }
}
