/*
 * Copyright 2021 CloudWeGo Authors
 *
 * 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.
 */

package netpollmux

import (
	"sync"
	"sync/atomic"
	"testing"

	"github.com/cloudwego/kitex/internal/test"
)

func TestShardMap(t *testing.T) {
	m := newShardMap(32)
	var key int32
	var mod int32 = 1023
	// check success
	for i := int32(0); i <= mod; i++ {
		m.store(i, nil)
	}
	// check range
	var sum int32
	m.rangeMap(func(seqID int32, msg EventHandler) {
		sum++
		test.Assert(t, msg == nil)
		test.Assert(t, 0 < seqID && seqID <= mod, seqID)
	})
	test.Assert(t, sum == mod, sum)
	// check key=0
	msg, ok := m.load(key)
	test.Assert(t, msg == nil)
	test.Assert(t, ok == false)
	// check key<0
	msg, ok = m.load(-1)
	test.Assert(t, msg == nil)
	test.Assert(t, ok == false)
	// check key>0
	for i := int32(1); i <= mod; i++ {
		key := i
		// load, load&delete, check
		msg, ok := m.load(key)
		test.Assert(t, msg == nil)
		test.Assert(t, ok == true)

		// store, delete, check
		m.store(key, nil)
		m.delete(key)
		msg, ok = m.load(key)
		test.Assert(t, msg == nil)
		test.Assert(t, ok == false)
	}

	// check nil
	for i := int32(0); i <= mod; i++ {
		msg, ok := m.load(key)
		test.Assert(t, msg == nil)
		test.Assert(t, ok == false)
		m.delete(key)
	}
}

func BenchmarkSyncMap(b *testing.B) {
	var m sync.Map
	var key uint32
	var mod uint32 = 1023
	for i := uint32(0); i <= mod; i++ {
		m.Store(i, nil)
	}

	b.ReportAllocs()
	b.StartTimer()
	b.SetParallelism(1024)
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			key := atomic.AddUint32(&key, 1) & mod
			m.Store(key, nil)
			m.Load(key)
			m.Delete(key)
		}
	})
}

func BenchmarkShardMap(b *testing.B) {
	m := newShardMap(32)
	var key int32
	var mod int32 = 1023
	for i := int32(0); i <= mod; i++ {
		m.store(i, nil)
	}

	b.ReportAllocs()
	b.StartTimer()
	b.SetParallelism(1024)
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			key := atomic.AddInt32(&key, 1) & mod
			m.store(key, nil)
			m.load(key)
			m.delete(key)
		}
	})
}
