// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// go test *.go -bench=".*" -benchmem

package gcache_test

import (
	"context"
	"github.com/gogf/gf/util/guid"
	"math"
	"testing"
	"time"

	"github.com/gogf/gf/container/gset"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/os/gcache"
	"github.com/gogf/gf/os/grpool"
	"github.com/gogf/gf/test/gtest"
)

func TestCache_GCache_Set(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		gcache.Set(1, 11, 0)
		defer gcache.Removes(g.Slice{1, 2, 3})
		v, _ := gcache.Get(1)
		t.Assert(v, 11)
		b, _ := gcache.Contains(1)
		t.Assert(b, true)
	})
}

func TestCache_Set(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		c := gcache.New()
		defer c.Close()
		t.Assert(c.Set(1, 11, 0), nil)
		v, _ := c.Get(1)
		t.Assert(v, 11)
		b, _ := c.Contains(1)
		t.Assert(b, true)
	})
}

func TestCache_GetVar(t *testing.T) {
	c := gcache.New()
	defer c.Close()
	gtest.C(t, func(t *gtest.T) {
		t.Assert(c.Set(1, 11, 0), nil)
		v, _ := c.Get(1)
		t.Assert(v, 11)
		b, _ := c.Contains(1)
		t.Assert(b, true)
	})
	gtest.C(t, func(t *gtest.T) {
		v, _ := c.GetVar(1)
		t.Assert(v.Int(), 11)
		v, _ = c.GetVar(2)
		t.Assert(v.Int(), 0)
		t.Assert(v.IsNil(), true)
		t.Assert(v.IsEmpty(), true)
	})
}

func TestCache_Set_Expire(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		t.Assert(cache.Set(2, 22, 100*time.Millisecond), nil)
		v, _ := cache.Get(2)
		t.Assert(v, 22)
		time.Sleep(200 * time.Millisecond)
		v, _ = cache.Get(2)
		t.Assert(v, nil)
		time.Sleep(3 * time.Second)
		n, _ := cache.Size()
		t.Assert(n, 0)
		t.Assert(cache.Close(), nil)
	})

	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		t.Assert(cache.Set(1, 11, 100*time.Millisecond), nil)
		v, _ := cache.Get(1)
		t.Assert(v, 11)
		time.Sleep(200 * time.Millisecond)
		v, _ = cache.Get(1)
		t.Assert(v, nil)
	})
}

func TestCache_Update_GetExpire(t *testing.T) {
	// gcache
	gtest.C(t, func(t *gtest.T) {
		key := guid.S()
		gcache.Set(key, 11, 3*time.Second)
		expire1, _ := gcache.GetExpire(key)
		gcache.Update(key, 12)
		expire2, _ := gcache.GetExpire(key)
		v, _ := gcache.GetVar(key)
		t.Assert(v, 12)
		t.Assert(math.Ceil(expire1.Seconds()), math.Ceil(expire2.Seconds()))
	})
	// gcache.Cache
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.Set(1, 11, 3*time.Second)
		expire1, _ := cache.GetExpire(1)
		cache.Update(1, 12)
		expire2, _ := cache.GetExpire(1)
		v, _ := cache.GetVar(1)
		t.Assert(v, 12)
		t.Assert(math.Ceil(expire1.Seconds()), math.Ceil(expire2.Seconds()))
	})
}

func TestCache_UpdateExpire(t *testing.T) {
	// gcache
	gtest.C(t, func(t *gtest.T) {
		key := guid.S()
		gcache.Set(key, 11, 3*time.Second)
		defer gcache.Remove(key)
		oldExpire, _ := gcache.GetExpire(key)
		newExpire := 10 * time.Second
		gcache.UpdateExpire(key, newExpire)
		e, _ := gcache.GetExpire(key)
		t.AssertNE(e, oldExpire)
		e, _ = gcache.GetExpire(key)
		t.Assert(math.Ceil(e.Seconds()), 10)
	})
	// gcache.Cache
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.Set(1, 11, 3*time.Second)
		oldExpire, _ := cache.GetExpire(1)
		newExpire := 10 * time.Second
		cache.UpdateExpire(1, newExpire)
		e, _ := cache.GetExpire(1)
		t.AssertNE(e, oldExpire)

		e, _ = cache.GetExpire(1)
		t.Assert(math.Ceil(e.Seconds()), 10)
	})
}

func TestCache_Keys_Values(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		c := gcache.New()
		for i := 0; i < 10; i++ {
			t.Assert(c.Set(i, i*10, 0), nil)
		}
		var (
			keys, _   = c.Keys()
			values, _ = c.Values()
		)
		t.Assert(len(keys), 10)
		t.Assert(len(values), 10)
		t.AssertIN(0, keys)
		t.AssertIN(90, values)
	})
}

func TestCache_LRU(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New(2)
		for i := 0; i < 10; i++ {
			cache.Set(i, i, 0)
		}
		n, _ := cache.Size()
		t.Assert(n, 10)
		v, _ := cache.Get(6)
		t.Assert(v, 6)
		time.Sleep(4 * time.Second)
		n, _ = cache.Size()
		t.Assert(n, 2)
		v, _ = cache.Get(6)
		t.Assert(v, 6)
		v, _ = cache.Get(1)
		t.Assert(v, nil)
		t.Assert(cache.Close(), nil)
	})
}

func TestCache_LRU_expire(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New(2)
		t.Assert(cache.Set(1, nil, 1000), nil)
		n, _ := cache.Size()
		t.Assert(n, 1)
		v, _ := cache.Get(1)

		t.Assert(v, nil)
	})
}

func TestCache_SetIfNotExist(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.SetIfNotExist(1, 11, 0)
		v, _ := cache.Get(1)
		t.Assert(v, 11)
		cache.SetIfNotExist(1, 22, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)
		cache.SetIfNotExist(2, 22, 0)
		v, _ = cache.Get(2)
		t.Assert(v, 22)

		gcache.Removes(g.Slice{1, 2, 3})
		gcache.SetIfNotExist(1, 11, 0)
		v, _ = gcache.Get(1)
		t.Assert(v, 11)
		gcache.SetIfNotExist(1, 22, 0)
		v, _ = gcache.Get(1)
		t.Assert(v, 11)
	})
}

func TestCache_Sets(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
		v, _ := cache.Get(1)
		t.Assert(v, 11)

		gcache.Removes(g.Slice{1, 2, 3})
		gcache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)
	})
}

func TestCache_GetOrSet(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.GetOrSet(1, 11, 0)
		v, _ := cache.Get(1)
		t.Assert(v, 11)
		cache.GetOrSet(1, 111, 0)

		v, _ = cache.Get(1)
		t.Assert(v, 11)
		gcache.Removes(g.Slice{1, 2, 3})
		gcache.GetOrSet(1, 11, 0)

		v, _ = cache.Get(1)
		t.Assert(v, 11)

		gcache.GetOrSet(1, 111, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)
	})
}

func TestCache_GetOrSetFunc(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.GetOrSetFunc(1, func() (interface{}, error) {
			return 11, nil
		}, 0)
		v, _ := cache.Get(1)
		t.Assert(v, 11)

		cache.GetOrSetFunc(1, func() (interface{}, error) {
			return 111, nil
		}, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)

		gcache.Removes(g.Slice{1, 2, 3})

		gcache.GetOrSetFunc(1, func() (interface{}, error) {
			return 11, nil
		}, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)

		gcache.GetOrSetFunc(1, func() (interface{}, error) {
			return 111, nil
		}, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)
	})
}

func TestCache_GetOrSetFuncLock(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.GetOrSetFuncLock(1, func() (interface{}, error) {
			return 11, nil
		}, 0)
		v, _ := cache.Get(1)
		t.Assert(v, 11)

		cache.GetOrSetFuncLock(1, func() (interface{}, error) {
			return 111, nil
		}, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)

		gcache.Removes(g.Slice{1, 2, 3})
		gcache.GetOrSetFuncLock(1, func() (interface{}, error) {
			return 11, nil
		}, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)

		gcache.GetOrSetFuncLock(1, func() (interface{}, error) {
			return 111, nil
		}, 0)
		v, _ = cache.Get(1)
		t.Assert(v, 11)
	})
}

func TestCache_Clear(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
		cache.Clear()
		n, _ := cache.Size()
		t.Assert(n, 0)
	})
}

func TestCache_SetConcurrency(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		pool := grpool.New(4)
		go func() {
			for {
				pool.Add(func() {
					cache.SetIfNotExist(1, 11, 10)
				})
			}
		}()
		select {
		case <-time.After(2 * time.Second):
			//t.Log("first part end")
		}

		go func() {
			for {
				pool.Add(func() {
					cache.SetIfNotExist(1, nil, 10)
				})
			}
		}()
		select {
		case <-time.After(2 * time.Second):
			//t.Log("second part end")
		}
	})
}

func TestCache_Basic(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		{
			cache := gcache.New()
			cache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
			b, _ := cache.Contains(1)
			t.Assert(b, true)
			v, _ := cache.Get(1)
			t.Assert(v, 11)
			data, _ := cache.Data()
			t.Assert(data[1], 11)
			t.Assert(data[2], 22)
			t.Assert(data[3], nil)
			n, _ := cache.Size()
			t.Assert(n, 2)
			keys, _ := cache.Keys()
			t.Assert(gset.NewFrom(g.Slice{1, 2}).Equal(gset.NewFrom(keys)), true)
			keyStrs, _ := cache.KeyStrings()
			t.Assert(gset.NewFrom(g.Slice{"1", "2"}).Equal(gset.NewFrom(keyStrs)), true)
			values, _ := cache.Values()
			t.Assert(gset.NewFrom(g.Slice{11, 22}).Equal(gset.NewFrom(values)), true)
			removeData1, _ := cache.Remove(1)
			t.Assert(removeData1, 11)
			n, _ = cache.Size()
			t.Assert(n, 1)
			cache.Removes(g.Slice{2})
			n, _ = cache.Size()
			t.Assert(n, 0)
		}

		gcache.Remove(g.Slice{1, 2, 3}...)
		{
			gcache.Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
			b, _ := gcache.Contains(1)
			t.Assert(b, true)
			v, _ := gcache.Get(1)
			t.Assert(v, 11)
			data, _ := gcache.Data()
			t.Assert(data[1], 11)
			t.Assert(data[2], 22)
			t.Assert(data[3], nil)
			n, _ := gcache.Size()
			t.Assert(n, 2)
			keys, _ := gcache.Keys()
			t.Assert(gset.NewFrom(g.Slice{1, 2}).Equal(gset.NewFrom(keys)), true)
			keyStrs, _ := gcache.KeyStrings()
			t.Assert(gset.NewFrom(g.Slice{"1", "2"}).Equal(gset.NewFrom(keyStrs)), true)
			values, _ := gcache.Values()
			t.Assert(gset.NewFrom(g.Slice{11, 22}).Equal(gset.NewFrom(values)), true)
			removeData1, _ := gcache.Remove(1)
			t.Assert(removeData1, 11)
			n, _ = gcache.Size()
			t.Assert(n, 1)
			gcache.Removes(g.Slice{2})
			n, _ = gcache.Size()
			t.Assert(n, 0)
		}
	})
}

func TestCache_Ctx(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		cache := gcache.New()
		cache.Ctx(context.Background()).Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
		b, _ := cache.Contains(1)
		t.Assert(b, true)
		v, _ := cache.Get(1)
		t.Assert(v, 11)
	})
}
