add member_status #4

Merged
daniel.w merged 1 commits from feature/member_online into main 2024-09-04 14:19:56 +00:00
12 changed files with 466 additions and 47 deletions
Showing only changes of commit dc454307be - Show all commits

View File

@ -117,6 +117,15 @@ issues:
- gocognit
- contextcheck
exclude-dirs:
- internal/model
- internal/mock
exclude-files:
- .*_test.go
linters-settings:
gci:
sections:

View File

@ -16,6 +16,7 @@ test: # 進行測試
fmt: # 格式優化
$(GOFMT) -w $(GOFILES)
goimports -w ./
golangci-lint run
.PHONY: gen-rpc
gen-rpc: # 建立 rpc code

4
go.mod
View File

@ -4,10 +4,11 @@ go 1.22.3
require (
code.30cm.net/digimon/library-go/errors v1.0.1
code.30cm.net/digimon/library-go/utils/invited_code v1.0.2
code.30cm.net/digimon/library-go/validator v1.0.0
code.30cm.net/wanderland/library-go/errors v1.0.1
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.9.0
github.com/zeromicro/go-zero v1.7.0
go.uber.org/mock v0.4.0
google.golang.org/grpc v1.65.0
@ -53,6 +54,7 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect

View File

@ -3,8 +3,8 @@ package domain
import (
mts "app-cloudep-permission-server/internal/lib/metric"
ers "code.30cm.net/wanderland/library-go/errors"
"code.30cm.net/wanderland/library-go/errors/code"
ers "code.30cm.net/digimon/library-go/errors"
"code.30cm.net/digimon/library-go/errors/code"
)
// 12 represents Scope
@ -32,52 +32,58 @@ const (
)
// TokenUnexpectedSigningErr 30001 Token 簽名錯誤
func TokenUnexpectedSigningErr(msg string) *ers.Err {
func TokenUnexpectedSigningErr(msg string) *ers.LibError {
mts.AppErrorMetrics.AddFailure("token", "token_unexpected_sign")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenUnexpectedSigningErrorCode, msg)
}
// TokenTokenValidateErr 30002 Token 驗證錯誤
func TokenTokenValidateErr(msg string) *ers.Err {
func TokenTokenValidateErr(msg string) *ers.LibError {
mts.AppErrorMetrics.AddFailure("token", "token_validate_ilegal")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenValidateErrorCode, msg)
}
// TokenClaimError 30003 Token 驗證錯誤
func TokenClaimError(msg string) *ers.Err {
func TokenClaimError(msg string) *ers.LibError {
mts.AppErrorMetrics.AddFailure("token", "token_claim_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg)
}
// RedisDelError 30020 Redis 刪除錯誤
func RedisDelError(msg string) *ers.Err {
func RedisDelError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "del_error")
return ers.NewErr(code.CloudEPPermission, code.CatDB, RedisDelErrorCode, msg)
}
// RedisPipLineError 30021 Redis PipLine 錯誤
func RedisPipLineError(msg string) *ers.Err {
func RedisPipLineError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "pip_line_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisPipLineErrorCode, msg)
}
// RedisError 30022 Redis 錯誤
func RedisError(msg string) *ers.Err {
func RedisError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisErrorCode, msg)
}
// PermissionNotFoundError 30030 權限錯誤
func PermissionNotFoundError(msg string) *ers.Err {
func PermissionNotFoundError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
return ers.NewErr(code.CloudEPPermission, code.Forbidden, PermissionNotFoundCode, msg)
}
// PermissionGetDataError 30031 解析權限時錯誤
func PermissionGetDataError(msg string) *ers.Err {
func PermissionGetDataError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
return ers.NewErr(code.CloudEPPermission, code.InvalidFormat, PermissionGetDataErrorCode, msg)
}

View File

@ -23,18 +23,21 @@ const (
// TokenUnexpectedSigningErr 30001 Token 簽名錯誤
func TokenUnexpectedSigningErr(msg string) *ers.LibError {
mts.AppErrorMetrics.AddFailure("token", "token_unexpected_sign")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenUnexpectedSigningErrorCode, msg)
}
// TokenTokenValidateErr 30002 Token 驗證錯誤
func TokenTokenValidateErr(msg string) *ers.LibError {
mts.AppErrorMetrics.AddFailure("token", "token_validate_ilegal")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenValidateErrorCode, msg)
}
// TokenClaimError 30003 Token 驗證錯誤
func TokenClaimError(msg string) *ers.LibError {
mts.AppErrorMetrics.AddFailure("token", "token_claim_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg)
}
@ -42,6 +45,7 @@ func TokenClaimError(msg string) *ers.LibError {
func RedisDelError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "del_error")
return ers.NewErr(code.CloudEPPermission, code.CatDB, RedisDelErrorCode, msg)
}
@ -49,6 +53,7 @@ func RedisDelError(msg string) *ers.LibError {
func RedisPipLineError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "pip_line_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisPipLineErrorCode, msg)
}
@ -56,5 +61,6 @@ func RedisPipLineError(msg string) *ers.LibError {
func RedisError(msg string) *ers.LibError {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisErrorCode, msg)
}

View File

@ -0,0 +1,16 @@
package repository
import "context"
// MemberOnlineStatusRepository 會員上限狀態使用Bitmap
type MemberOnlineStatusRepository interface {
SetMemberOnline(ctx context.Context, uid string) (bool, error)
SetMemberOffline(ctx context.Context, uid string) (bool, error)
IsMemberOnline(ctx context.Context, uid string) (bool, error)
QueryMemberOnlineList(ctx context.Context, uids []string) ([]MemberOnlineStatusResp, error)
}
type MemberOnlineStatusResp struct {
UID string
Status bool
}

View File

@ -0,0 +1,16 @@
package usecase
type BitMapUseCase interface {
// SetTrue 設定該 Bit 狀態為 true
SetTrue(bitPos uint32)
// SetFalse 設定該Bit 狀態為 false
SetFalse(bitPos uint32)
// IsTrue 確認是否為真
IsTrue(bitPos uint32) bool
// Reset 重設 BitMap
Reset()
// ByteSize 最大 Byte 數
ByteSize() int
// BitSize 最大 Byte * 8
BitSize() int
}

View File

@ -1,38 +1,33 @@
package usecase
import (
"ark-permission/internal/domain"
"ark-permission/internal/entity"
)
// PermissionTreeManager 定義一組操作權限樹的接口
// 這個名稱說明它是專門負責管理和操作權限樹的管理器
type PermissionTreeManager interface {
// AddPermission 將一個新的權限節點插入到樹中
// key 是父節點的IDvalue 是要插入的 Permission 資料
// 此方法應該能處理節點是否存在於父節點下的情況
AddPermission(parentID int64, permission entity.Permission) error
// FindPermissionByID 根據權限 ID 查詢樹中的某個節點
// 如果節點存在,返回對應的 Permission 資料,否則返回 nil
FindPermissionByID(permissionID int64) (*Permission, error)
// GetAllParentPermissionIDs 根據傳入的 permissions 列表
// 找出每個權限的完整父節點權限 ID 路徑
// 例如,如果 B 的父權限是 A並且給了 B 權限,則返回 A 和 B 的權限 ID
GetAllParentPermissionIDs(permissions domain.Permissions) ([]int64, error)
// GetAllParentPermissionStatuses 返回給定權限下的所有完整父節點權限狀態
// 例如,若給 B 權限,該方法將返回所有與 B 相關的父權限的狀態
GetAllParentPermissionStatuses(permissions domain.Permissions) (domain.Permissions, error)
// GetRolePermissionTree 根據角色權限找出所有父節點和子節點權限狀態
// 角色權限是傳入的一個列表,該方法會根據每個角色的權限,返回所有相關的權限狀態
GetRolePermissionTree(rolePermissions []entity.RolePermission) domain.Permissions
}
type Permission struct {
ID int64 `json:"-"`
Name string `json:"name"`
HTTPMethod string `json:"http_method"`
HTTPPath string `json:"http_path"`
Parent *Permission `json:"-"`
Children []*Permission `json:"children"`
PathIDs []int64 `json:"-"` // full path id
}
// // PermissionTreeManager 定義一組操作權限樹的接口
// // 這個名稱說明它是專門負責管理和操作權限樹的管理器
// type PermissionTreeManager interface {
// // AddPermission 將一個新的權限節點插入到樹中
// // key 是父節點的IDvalue 是要插入的 Permission 資料
// // 此方法應該能處理節點是否存在於父節點下的情況
// AddPermission(parentID int64, permission entity.Permission) error
// // FindPermissionByID 根據權限 ID 查詢樹中的某個節點
// // 如果節點存在,返回對應的 Permission 資料,否則返回 nil
// FindPermissionByID(permissionID int64) (*Permission, error)
// // GetAllParentPermissionIDs 根據傳入的 permissions 列表
// // 找出每個權限的完整父節點權限 ID 路徑
// // 例如,如果 B 的父權限是 A並且給了 B 權限,則返回 A 和 B 的權限 ID
// GetAllParentPermissionIDs(permissions domain.Permissions) ([]int64, error)
// // GetAllParentPermissionStatuses 返回給定權限下的所有完整父節點權限狀態
// // 例如,若給 B 權限,該方法將返回所有與 B 相關的父權限的狀態
// GetAllParentPermissionStatuses(permissions domain.Permissions) (domain.Permissions, error)
// // GetRolePermissionTree 根據角色權限找出所有父節點和子節點權限狀態
// // 角色權限是傳入的一個列表,該方法會根據每個角色的權限,返回所有相關的權限狀態
// GetRolePermissionTree(rolePermissions []entity.RolePermission) domain.Permissions
// }
//
// type Permission struct {
// ID int64 `json:"-"`
// Name string `json:"name"`
// HTTPMethod string `json:"http_method"`
// HTTPPath string `json:"http_path"`
// Parent *Permission `json:"-"`
// Children []*Permission `json:"children"`
// PathIDs []int64 `json:"-"` // full path id
// }

View File

@ -0,0 +1,104 @@
package repository
import (
"app-cloudep-permission-server/internal/domain/repository"
"code.30cm.net/digimon/library-go/utils/invited_code"
"context"
"fmt"
"github.com/zeromicro/go-zero/core/stores/redis"
)
type MemberOnlineStatusRepositoryParam struct {
Store *redis.Redis `name:"redis"`
}
type memberOnlineStatusRepository struct {
store *redis.Redis
}
// 使用 UID 計算 Bitmap 的 offset
func (t *memberOnlineStatusRepository) uidToOffset(uid string) (int64, error) {
converter := invited_code.MustConverter(10, invited_code.DefaultCodeLen, invited_code.ConvertTable)
// 將 UID 轉換為整數型,並減去基礎 UID (1000000)
uidInt, err := converter.DecodeFromCode(uid)
if err != nil {
return 0, fmt.Errorf("invalid UID: %w", err)
}
// 以 1000000 作為基準
baseUID := invited_code.InitAutoId
if uidInt < int64(baseUID) {
return 0, fmt.Errorf("UID smaller than base: %d", baseUID)
}
return uidInt - int64(baseUID), nil
}
func (t *memberOnlineStatusRepository) SetMemberOnline(ctx context.Context, uid string) (bool, error) {
offset, err := t.uidToOffset(uid)
if err != nil {
return false, err
}
// 使用 SET BIT 設置對應的位為 1
_, err = t.store.SetBit("member_status", offset, 1)
if err != nil {
return false, fmt.Errorf("failed to set member online: %w", err)
}
return true, nil
}
func (t *memberOnlineStatusRepository) SetMemberOffline(ctx context.Context, uid string) (bool, error) {
offset, err := t.uidToOffset(uid)
if err != nil {
return false, err
}
// 使用 SET BIT 設置對應的位為 1
_, err = t.store.SetBit("member_status", offset, 0)
if err != nil {
return false, fmt.Errorf("failed to set member offline: %w", err)
}
return true, nil
}
func (t *memberOnlineStatusRepository) IsMemberOnline(ctx context.Context, uid string) (bool, error) {
offset, err := t.uidToOffset(uid)
if err != nil {
return false, err
}
// 使用 GET BIT 獲取對應的位1 表示在線0 表示離線
status, err := t.store.GetBit("member_status", offset)
if err != nil {
return false, fmt.Errorf("failed to get member status: %w", err)
}
return status == 1, nil
}
func (t *memberOnlineStatusRepository) QueryMemberOnlineList(ctx context.Context, uids []string) ([]repository.MemberOnlineStatusResp, error) {
statusMap := make([]repository.MemberOnlineStatusResp, 0, len(uids))
// 遍歷所有用戶的 UID查詢他們的在線狀態
for _, uid := range uids {
isOnline, err := t.IsMemberOnline(ctx, uid)
if err != nil {
return nil, fmt.Errorf("failed to query member status for UID %s: %w", uid, err)
}
statusMap = append(statusMap, repository.MemberOnlineStatusResp{
UID: uid,
Status: isOnline,
})
}
return statusMap, nil
}
func NewMemberOnlineStatusRepository(param MemberOnlineStatusRepositoryParam) repository.MemberOnlineStatusRepository {
return &memberOnlineStatusRepository{
store: param.Store,
}
}

117
internal/usecase/bitmap.go Normal file
View File

@ -0,0 +1,117 @@
package usecase
// Bitmap 基礎結構
// Bitmap 是一個位圖結構,使用 byte slice 來表示大量的位bit
// 每個 byte 由 8 個位組成因此可以高效地管理大量的開關狀態true/false
// Bitmap 的優點在於它能節省空間,尤其是在需要大量布爾值的場合。
// 缺點是,如果需要動態擴充 Bitmap 的大小,會導致效率下降,因為需要重新分配和移動內存。
// 因此,最好在初始化時就規劃好所需的位數大小,避免在之後頻繁擴充。
type Bitmap []byte
// MakeBitmapWithBitSize 通過指定的 bit 數創建一個新的 Bitmap。
// 參數 nBits 表示所需的位bit數。
// 如果指定的位數少於 64則默認將 Bitmap 初始化為 64 位(這是最低的限制)。
// 此外位數nBits會被自動調整為 8 的倍數,以適配 byte 的長度(每 8 位為一個 byte
// 返回值是一個 Bitmapbyte slice其大小根據位數確定。
func MakeBitmapWithBitSize(nBits int) Bitmap {
// 如果指定的位數少於 64則設置為 64 位8 個 byte
if nBits < 64 {
nBits = 64
}
// 計算需要的 byte 數,確保每 8 位為一個 byte並調整 nBits 以達到 8 的倍數
return MustBitMap((nBits + 7) / 8)
}
// MustBitMap 根據指定的 byte 數創建一個 Bitmapbyte slice
// 參數 nBytes 表示所需的 byte 數。
// 返回值是一個長度為 nBytes 的 Bitmap。
func MustBitMap(nBytes int) Bitmap {
// 使用 make 函數創建一個 byte slice大小為 nBytes。
return make([]byte, nBytes)
}
// SetTrue 設置指定位置的 bit 為 true1
// 參數 bitPos 是需要設置的位的位置(以 0 為基準的位索引)。
// 這個操作會找到該 bit 所在的 byte然後通過位運算將該位置的 bit 設置為 1。
func (b Bitmap) SetTrue(bitPos uint32) {
// |= 是一種位運算的複合賦值運算符,表示將左邊的變數與右邊的值進行 位或運算bitwise OR並將結果賦值
b[bitPos/8] |= 1 << (bitPos % 8)
}
// SetFalse 設置指定位置的 bit 為 false0
// 參數 bitPos 是需要設置的位的位置(以 0 為基準的位索引)。
// 這個操作會找到該 bit 所在的 byte然後通過位運算將該位置的 bit 設置為 0。
func (b Bitmap) SetFalse(bitPos uint32) {
// 取出對應 byte使用位與和取反運算將對應的 bit 設置為 0
// 假設我們有以下情況:
// • b[1](即 b[bitPos/8])是 10101111十進制 175
// • bitPos = 10也就是我們想清除第 10 位的值。
//
// 操作步驟:
//
// 1. bitPos/8 = 1所以我們要修改 b[1] 這個 byte。
// 2. bitPos % 8 = 2表示我們要清除的位是這個 byte 中的第 3 位(從右數起第 3 位)。
// 3. 1 << (bitPos % 8) = 1 << 2 = 00000100生成位掩碼 00000100。
// 4. 取反:^(1 << 2) = ^00000100 = 11111011這樣的掩碼表示除了第 3 位,其他位都是 1。
// 5. 位與運算10101111 & 11111011 = 10101011結果將第 3 位清除其餘位保持不變。即b[1] 變成了 10101011十進制 171
// &= 是一種 位與運算的複合賦值運算符,表示將左邊的變數與右邊的值進行 位與運算bitwise AND然後將結果賦值給左邊的變數。
b[bitPos/8] &= ^(1 << (bitPos % 8))
}
// IsTrue 檢查指定位置的 bit 是否為 true1
// 參數 bitPos 是要檢查的位的位置(以 0 為基準的位索引)。
// 如果該 bit 是 1則返回 true否則返回 false。
func (b Bitmap) IsTrue(bitPos uint32) bool {
/*
這一行程式碼 b[bitPos/8]&(1<<(bitPos%8)) != 0 是用來檢查 指定位bit 是否為 true1
它的核心是位運算讓我們逐步拆解並解釋這一行程式碼
1. 背景知識
位運算 是在二進制層面操作數字每個 byte 8 個位bit所以位圖結構是以 byte 來表示位的集合
b 是一個 Bitmap 結構也就是 []byte byte 的切片
bitPos 是一個 uint32 類型的變數表示我們想要檢查的位bit在整個位圖中的索引位置
3. 完整流程舉例
假設
b = []byte{0b10101010, 0b01010101} 即二進制表示的兩個 byte分別是 10101010 01010101
bitPos = 10我們要檢查第 10 位是否為 1
操作順序
1. 計算 bitPos/8 = 10/8 = 1所以我們要檢查的是第二個 byte0b01010101
2. 計算 bitPos%8 = 10%8 = 2所以我們要檢查的是該 byte 中的第 3 從右數起第 3
3. 位移1 << 2 = 00000100
4. 位與0b01010101 & 0b00000100 = 0b00000100因為該 byte 的第 3 位是 1結果不等於 0
5. 判斷結果結果不等於 0因此第 10 位是 1true
4. 總結
b[bitPos/8]&(1<<(bitPos%8)) != 0 是一個經典的位操作用來檢查位圖中某一個位是否為 1
bitPos/8 找到對應的 bytebitPos % 8 找到該位在這個 byte 中的具體位置
最後的位與運算和比較用來確定該位的狀態是 true1還是 false0
*/
return b[bitPos/8]&(1<<(bitPos%8)) != 0
}
// Reset 重置 Bitmap使所有的 bit 都設置為 false0
// 這個操作會將整個 Bitmap 的所有 byte 都設置為 0從而達到重置的效果。
func (b Bitmap) Reset() {
// 迭代 Bitmap 中的每個 byte並將其設置為 0
for i := range b {
b[i] = 0
}
}
// ByteSize 返回 Bitmap 的 byte 長度。
// 這個函數返回 Bitmap 目前占用的 byte 數量,該值等於 Bitmap 的長度slice 的長度)。
func (b Bitmap) ByteSize() int {
return len(b)
}
// BitSize 返回 Bitmap 的位bit長度。
// 這個函數通過將 byte 長度乘以 8 來計算 Bitmap 中的總位數。
func (b Bitmap) BitSize() int {
// 每個 byte 包含 8 個 bit因此將 byte 長度乘以 8
return len(b) * 8
}

View File

@ -0,0 +1,69 @@
package usecase
import "testing"
// 基準測試 SetTrue 函數,測試在不同大小的 Bitmap 上設置位元為 true 的效能
func BenchmarkBitmapSetTrue(b *testing.B) {
// 以 10^6 位元作為基準測試的 Bitmap 大小
bitmap := MakeBitmapWithBitSize(1000000)
b.ResetTimer() // 重設計時器,排除初始化的時間
for i := 0; i < b.N; i++ {
bitmap.SetTrue(uint32(i % 1000000)) // 設置位元為 true
}
}
// 基準測試 SetFalse 函數,測試在不同大小的 Bitmap 上清除位元為 false 的效能
func BenchmarkBitmapSetFalse(b *testing.B) {
// 以 10^6 位元作為基準測試的 Bitmap 大小
bitmap := MakeBitmapWithBitSize(1000000)
b.ResetTimer() // 重設計時器,排除初始化的時間
for i := 0; i < b.N; i++ {
bitmap.SetFalse(uint32(i % 1000000)) // 清除位元為 false
}
}
// 基準測試 IsTrue 函數,測試在不同大小的 Bitmap 上檢查位元狀態的效能
func BenchmarkBitmapIsTrue(b *testing.B) {
// 以 10^6 位元作為基準測試的 Bitmap 大小
bitmap := MakeBitmapWithBitSize(1000000)
b.ResetTimer() // 重設計時器,排除初始化的時間
for i := 0; i < b.N; i++ {
_ = bitmap.IsTrue(uint32(i % 1000000)) // 檢查位元是否為 true
}
}
// 基準測試 Reset 函數,測試重置不同大小的 Bitmap 的效能
func BenchmarkBitmapReset(b *testing.B) {
// 以 10^6 位元作為基準測試的 Bitmap 大小
bitmap := MakeBitmapWithBitSize(1000000)
b.ResetTimer() // 重設計時器,排除初始化的時間
for i := 0; i < b.N; i++ {
bitmap.Reset() // 重置 Bitmap
}
}
// 基準測試 BitSize 函數,測試返回位圖的 bit 長度的效能
func BenchmarkBitmapBitSize(b *testing.B) {
// 以 10^6 位元作為基準測試的 Bitmap 大小
bitmap := MakeBitmapWithBitSize(1000000)
b.ResetTimer() // 重設計時器,排除初始化的時間
for i := 0; i < b.N; i++ {
_ = bitmap.BitSize(0) // 測試返回位圖的 bit 長度
}
}
// 基準測試 ByteSize 函數,測試返回位圖的 byte 長度的效能
func BenchmarkBitmapByteSize(b *testing.B) {
// 以 10^6 位元作為基準測試的 Bitmap 大小
bitmap := MakeBitmapWithBitSize(1000000)
b.ResetTimer() // 重設計時器,排除初始化的時間
for i := 0; i < b.N; i++ {
_ = bitmap.ByteSize(0) // 測試返回位圖的 byte 長度
}
}

View File

@ -0,0 +1,78 @@
package usecase
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBitmap_SetTrueAndIsTrue(t *testing.T) {
tests := []struct {
name string
bitPos uint32
expected bool
}{
{"Set bit 0 to true", 0, true},
{"Set bit 1 to true", 1, true},
{"Set bit 63 to true", 63, true},
{"Set bit 64 to true", 64, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap
bitmap.SetTrue(tt.bitPos)
result := bitmap.IsTrue(tt.bitPos)
assert.Equal(t, tt.expected, result)
})
}
}
func TestBitmap_SetFalse(t *testing.T) {
tests := []struct {
name string
bitPos uint32
expected bool
}{
{"Set bit 0 to false", 0, false},
{"Set bit 1 to false", 1, false},
{"Set bit 63 to false", 63, false},
{"Set bit 64 to false", 64, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap
bitmap.SetTrue(tt.bitPos) // 先設置該 bit 為 true
bitmap.SetFalse(tt.bitPos) // 然後設置該 bit 為 false
result := bitmap.IsTrue(tt.bitPos)
assert.Equal(t, tt.expected, result)
})
}
}
func TestBitmap_Reset(t *testing.T) {
bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap
bitmap.SetTrue(0)
bitmap.SetTrue(64)
// 確認 bit 0 和 bit 64 是 true
assert.True(t, bitmap.IsTrue(0))
assert.True(t, bitmap.IsTrue(64))
bitmap.Reset() // 重置位圖
// 確認所有的位都已經重置為 false
assert.False(t, bitmap.IsTrue(0))
assert.False(t, bitmap.IsTrue(64))
}
func TestBitmap_ByteSize(t *testing.T) {
bitmap := MakeBitmapWithBitSize(64) // 初始化一個 64 位的 Bitmap
assert.Equal(t, 8, bitmap.ByteSize(0)) // 64 位應該佔用 8 個 byte
}
func TestBitmap_BitSize(t *testing.T) {
bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap
assert.Equal(t, 128, bitmap.BitSize(0)) // 128 位應該有 128 個 bit
}