feat(namepool): Add themed name pools for polecats
Polecats now get themed names from the Mad Max universe by default
(furiosa, nux, slit, etc.) instead of generic polecat-01, polecat-02.
Changes:
- Add NamepoolConfig to config/types.go for per-rig theme configuration
- Update namepool.go with three built-in themes:
- mad-max (default): furiosa, nux, imperator, etc.
- minerals: obsidian, quartz, ruby, etc.
- wasteland: rust, chrome, fury, etc.
- Add gt namepool commands: themes, set, add, reset
- Update manager.go to load namepool config from rig settings
Configuration in .gastown/config.json:
```json
{
"namepool": {
"style": "minerals",
"max_before_numbering": 50
}
}
```
Issue: beads-rs0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
310
internal/cmd/namepool.go
Normal file
310
internal/cmd/namepool.go
Normal file
@@ -0,0 +1,310 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/polecat"
|
||||
"github.com/steveyegge/gastown/internal/workspace"
|
||||
)
|
||||
|
||||
var (
|
||||
namepoolListFlag bool
|
||||
namepoolThemeFlag string
|
||||
)
|
||||
|
||||
var namepoolCmd = &cobra.Command{
|
||||
Use: "namepool",
|
||||
Short: "Manage polecat name pools",
|
||||
Long: `Manage themed name pools for polecats in Gas Town.
|
||||
|
||||
By default, polecats get themed names from the Mad Max universe
|
||||
(furiosa, nux, slit, etc.). You can change the theme or add custom names.
|
||||
|
||||
Examples:
|
||||
gt namepool # Show current pool status
|
||||
gt namepool --list # List available themes
|
||||
gt namepool themes # Show theme names
|
||||
gt namepool set minerals # Set theme to 'minerals'
|
||||
gt namepool add ember # Add custom name to pool
|
||||
gt namepool reset # Reset pool state`,
|
||||
RunE: runNamepool,
|
||||
}
|
||||
|
||||
var namepoolThemesCmd = &cobra.Command{
|
||||
Use: "themes [theme]",
|
||||
Short: "List available themes and their names",
|
||||
RunE: runNamepoolThemes,
|
||||
}
|
||||
|
||||
var namepoolSetCmd = &cobra.Command{
|
||||
Use: "set <theme>",
|
||||
Short: "Set the namepool theme for this rig",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runNamepoolSet,
|
||||
}
|
||||
|
||||
var namepoolAddCmd = &cobra.Command{
|
||||
Use: "add <name>",
|
||||
Short: "Add a custom name to the pool",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runNamepoolAdd,
|
||||
}
|
||||
|
||||
var namepoolResetCmd = &cobra.Command{
|
||||
Use: "reset",
|
||||
Short: "Reset the pool state (release all names)",
|
||||
RunE: runNamepoolReset,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(namepoolCmd)
|
||||
namepoolCmd.AddCommand(namepoolThemesCmd)
|
||||
namepoolCmd.AddCommand(namepoolSetCmd)
|
||||
namepoolCmd.AddCommand(namepoolAddCmd)
|
||||
namepoolCmd.AddCommand(namepoolResetCmd)
|
||||
namepoolCmd.Flags().BoolVarP(&namepoolListFlag, "list", "l", false, "List available themes")
|
||||
}
|
||||
|
||||
func runNamepool(cmd *cobra.Command, args []string) error {
|
||||
// List themes mode
|
||||
if namepoolListFlag {
|
||||
return runNamepoolThemes(cmd, nil)
|
||||
}
|
||||
|
||||
// Show current pool status
|
||||
rigName, rigPath := detectCurrentRigWithPath()
|
||||
if rigName == "" {
|
||||
return fmt.Errorf("not in a rig directory")
|
||||
}
|
||||
|
||||
// Load pool
|
||||
pool := polecat.NewNamePool(rigPath, rigName)
|
||||
if err := pool.Load(); err != nil {
|
||||
// Pool doesn't exist yet, show defaults
|
||||
fmt.Printf("Rig: %s\n", rigName)
|
||||
fmt.Printf("Theme: %s (default)\n", polecat.DefaultTheme)
|
||||
fmt.Printf("Active polecats: 0\n")
|
||||
fmt.Printf("Max pool size: %d\n", polecat.DefaultPoolSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show pool status
|
||||
fmt.Printf("Rig: %s\n", rigName)
|
||||
fmt.Printf("Theme: %s\n", pool.GetTheme())
|
||||
fmt.Printf("Active polecats: %d\n", pool.ActiveCount())
|
||||
|
||||
activeNames := pool.ActiveNames()
|
||||
if len(activeNames) > 0 {
|
||||
fmt.Printf("In use: %s\n", strings.Join(activeNames, ", "))
|
||||
}
|
||||
|
||||
// Check if configured
|
||||
configPath := filepath.Join(rigPath, ".gastown", "config.json")
|
||||
if cfg, err := config.LoadRigConfig(configPath); err == nil && cfg.Namepool != nil {
|
||||
fmt.Printf("(configured in .gastown/config.json)\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runNamepoolThemes(cmd *cobra.Command, args []string) error {
|
||||
themes := polecat.ListThemes()
|
||||
|
||||
if len(args) == 0 {
|
||||
// List all themes
|
||||
fmt.Println("Available themes:")
|
||||
for _, theme := range themes {
|
||||
names, _ := polecat.GetThemeNames(theme)
|
||||
fmt.Printf("\n %s (%d names):\n", theme, len(names))
|
||||
// Show first 10 names
|
||||
preview := names
|
||||
if len(preview) > 10 {
|
||||
preview = preview[:10]
|
||||
}
|
||||
fmt.Printf(" %s...\n", strings.Join(preview, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show specific theme names
|
||||
theme := args[0]
|
||||
names, err := polecat.GetThemeNames(theme)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unknown theme: %s (available: %s)", theme, strings.Join(themes, ", "))
|
||||
}
|
||||
|
||||
fmt.Printf("Theme: %s (%d names)\n\n", theme, len(names))
|
||||
for i, name := range names {
|
||||
if i > 0 && i%5 == 0 {
|
||||
fmt.Println()
|
||||
}
|
||||
fmt.Printf(" %-12s", name)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runNamepoolSet(cmd *cobra.Command, args []string) error {
|
||||
theme := args[0]
|
||||
|
||||
// Validate theme
|
||||
themes := polecat.ListThemes()
|
||||
valid := false
|
||||
for _, t := range themes {
|
||||
if t == theme {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("unknown theme: %s (available: %s)", theme, strings.Join(themes, ", "))
|
||||
}
|
||||
|
||||
// Get rig
|
||||
rigName, rigPath := detectCurrentRigWithPath()
|
||||
if rigName == "" {
|
||||
return fmt.Errorf("not in a rig directory")
|
||||
}
|
||||
|
||||
// Update pool
|
||||
pool := polecat.NewNamePool(rigPath, rigName)
|
||||
if err := pool.Load(); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("loading pool: %w", err)
|
||||
}
|
||||
|
||||
if err := pool.SetTheme(theme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pool.Save(); err != nil {
|
||||
return fmt.Errorf("saving pool: %w", err)
|
||||
}
|
||||
|
||||
// Also save to rig config
|
||||
if err := saveRigNamepoolConfig(rigPath, theme, nil); err != nil {
|
||||
return fmt.Errorf("saving config: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Theme '%s' set for rig '%s'\n", theme, rigName)
|
||||
fmt.Printf("New polecats will use names from this theme.\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runNamepoolAdd(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
rigName, rigPath := detectCurrentRigWithPath()
|
||||
if rigName == "" {
|
||||
return fmt.Errorf("not in a rig directory")
|
||||
}
|
||||
|
||||
// Load pool
|
||||
pool := polecat.NewNamePool(rigPath, rigName)
|
||||
if err := pool.Load(); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("loading pool: %w", err)
|
||||
}
|
||||
|
||||
pool.AddCustomName(name)
|
||||
|
||||
if err := pool.Save(); err != nil {
|
||||
return fmt.Errorf("saving pool: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Added '%s' to the name pool\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runNamepoolReset(cmd *cobra.Command, args []string) error {
|
||||
rigName, rigPath := detectCurrentRigWithPath()
|
||||
if rigName == "" {
|
||||
return fmt.Errorf("not in a rig directory")
|
||||
}
|
||||
|
||||
// Load pool
|
||||
pool := polecat.NewNamePool(rigPath, rigName)
|
||||
if err := pool.Load(); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("loading pool: %w", err)
|
||||
}
|
||||
|
||||
pool.Reset()
|
||||
|
||||
if err := pool.Save(); err != nil {
|
||||
return fmt.Errorf("saving pool: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Pool reset for rig '%s'\n", rigName)
|
||||
fmt.Printf("All names released and available for reuse.\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectCurrentRigWithPath determines the rig name and path from cwd.
|
||||
func detectCurrentRigWithPath() (string, string) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
townRoot, err := workspace.FindFromCwd()
|
||||
if err != nil || townRoot == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Get path relative to town root
|
||||
rel, err := filepath.Rel(townRoot, cwd)
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Extract first path component (rig name)
|
||||
parts := strings.Split(rel, string(filepath.Separator))
|
||||
if len(parts) > 0 && parts[0] != "." && parts[0] != "mayor" && parts[0] != "deacon" {
|
||||
return parts[0], filepath.Join(townRoot, parts[0])
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// saveRigNamepoolConfig saves the namepool config to rig config.
|
||||
func saveRigNamepoolConfig(rigPath, theme string, customNames []string) error {
|
||||
configPath := filepath.Join(rigPath, ".gastown", "config.json")
|
||||
|
||||
// Load existing config or create new
|
||||
var cfg *config.RigConfig
|
||||
cfg, err := config.LoadRigConfig(configPath)
|
||||
if err != nil {
|
||||
// Create new config if not found
|
||||
if os.IsNotExist(err) || strings.Contains(err.Error(), "not found") {
|
||||
cfg = &config.RigConfig{
|
||||
Type: "rig",
|
||||
Version: config.CurrentRigConfigVersion,
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("loading config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set namepool
|
||||
cfg.Namepool = &config.NamepoolConfig{
|
||||
Style: theme,
|
||||
Names: customNames,
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save
|
||||
if err := config.SaveRigConfig(configPath, cfg); err != nil {
|
||||
return fmt.Errorf("saving config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -53,6 +53,7 @@ type RigConfig struct {
|
||||
Version int `json:"version"` // schema version
|
||||
MergeQueue *MergeQueueConfig `json:"merge_queue,omitempty"` // merge queue settings
|
||||
Theme *ThemeConfig `json:"theme,omitempty"` // tmux theme settings
|
||||
Namepool *NamepoolConfig `json:"namepool,omitempty"` // polecat name pool settings
|
||||
}
|
||||
|
||||
// ThemeConfig represents tmux theme settings for a rig.
|
||||
@@ -125,3 +126,26 @@ func DefaultMergeQueueConfig() *MergeQueueConfig {
|
||||
MaxConcurrent: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// NamepoolConfig represents namepool settings for themed polecat names.
|
||||
type NamepoolConfig struct {
|
||||
// Style picks from a built-in theme (e.g., "mad-max", "minerals", "wasteland").
|
||||
// If empty, defaults to "mad-max".
|
||||
Style string `json:"style,omitempty"`
|
||||
|
||||
// Names is a custom list of names to use instead of a built-in theme.
|
||||
// If provided, overrides the Style setting.
|
||||
Names []string `json:"names,omitempty"`
|
||||
|
||||
// MaxBeforeNumbering is when to start appending numbers.
|
||||
// Default is 50. After this many polecats, names become name-01, name-02, etc.
|
||||
MaxBeforeNumbering int `json:"max_before_numbering,omitempty"`
|
||||
}
|
||||
|
||||
// DefaultNamepoolConfig returns a NamepoolConfig with sensible defaults.
|
||||
func DefaultNamepoolConfig() *NamepoolConfig {
|
||||
return &NamepoolConfig{
|
||||
Style: "mad-max",
|
||||
MaxBeforeNumbering: 50,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/steveyegge/gastown/internal/beads"
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/git"
|
||||
"github.com/steveyegge/gastown/internal/rig"
|
||||
)
|
||||
@@ -32,8 +33,24 @@ func NewManager(r *rig.Rig, g *git.Git) *Manager {
|
||||
// Use the mayor's rig directory for beads operations (rig-level beads)
|
||||
mayorRigPath := filepath.Join(r.Path, "mayor", "rig")
|
||||
|
||||
// Initialize name pool
|
||||
pool := NewNamePool(r.Path, r.Name)
|
||||
// Try to load rig config for namepool settings
|
||||
rigConfigPath := filepath.Join(r.Path, ".gastown", "config.json")
|
||||
var pool *NamePool
|
||||
|
||||
rigConfig, err := config.LoadRigConfig(rigConfigPath)
|
||||
if err == nil && rigConfig.Namepool != nil {
|
||||
// Use configured namepool settings
|
||||
pool = NewNamePoolWithConfig(
|
||||
r.Path,
|
||||
r.Name,
|
||||
rigConfig.Namepool.Style,
|
||||
rigConfig.Namepool.Names,
|
||||
rigConfig.Namepool.MaxBeforeNumbering,
|
||||
)
|
||||
} else {
|
||||
// Use defaults
|
||||
pool = NewNamePool(r.Path, r.Name)
|
||||
}
|
||||
_ = pool.Load() // Load existing state, ignore errors for new rigs
|
||||
|
||||
return &Manager{
|
||||
|
||||
@@ -10,15 +10,55 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// PoolSize is the number of reusable names in the pool.
|
||||
PoolSize = 50
|
||||
// DefaultPoolSize is the number of reusable names in the pool.
|
||||
DefaultPoolSize = 50
|
||||
|
||||
// NamePrefix is the prefix for pooled polecat names.
|
||||
NamePrefix = "polecat-"
|
||||
// DefaultTheme is the default theme for new rigs.
|
||||
DefaultTheme = "mad-max"
|
||||
)
|
||||
|
||||
// Built-in themes with themed polecat names.
|
||||
var BuiltinThemes = map[string][]string{
|
||||
"mad-max": {
|
||||
"furiosa", "nux", "slit", "rictus", "dementus",
|
||||
"capable", "toast", "dag", "cheedo", "valkyrie",
|
||||
"keeper", "morsov", "ace", "warboy", "imperator",
|
||||
"organic", "coma", "splendid", "angharad", "max",
|
||||
"immortan", "bullet", "toecutter", "goose", "nightrider",
|
||||
"glory", "scrotus", "chumbucket", "corpus", "dinki",
|
||||
"prime", "vuvalini", "rockryder", "wretched", "buzzard",
|
||||
"gastown", "bullet-farmer", "citadel", "wasteland", "fury",
|
||||
"road-warrior", "interceptor", "blackfinger", "wraith", "witness",
|
||||
"chrome", "shiny", "mediocre", "guzzoline", "aqua-cola",
|
||||
},
|
||||
"minerals": {
|
||||
"obsidian", "quartz", "jasper", "onyx", "opal",
|
||||
"topaz", "garnet", "ruby", "amber", "jade",
|
||||
"pearl", "flint", "granite", "basalt", "marble",
|
||||
"shale", "slate", "pyrite", "mica", "agate",
|
||||
"malachite", "turquoise", "lapis", "emerald", "sapphire",
|
||||
"diamond", "amethyst", "citrine", "zircon", "peridot",
|
||||
"coral", "jet", "moonstone", "sunstone", "bloodstone",
|
||||
"rhodonite", "sodalite", "hematite", "magnetite", "calcite",
|
||||
"fluorite", "selenite", "kyanite", "labradorite", "amazonite",
|
||||
"chalcedony", "carnelian", "aventurine", "chrysoprase", "heliodor",
|
||||
},
|
||||
"wasteland": {
|
||||
"rust", "chrome", "nitro", "guzzle", "witness",
|
||||
"shiny", "fury", "thunder", "dust", "scavenger",
|
||||
"radrat", "ghoul", "mutant", "raider", "vault",
|
||||
"pipboy", "nuka", "brahmin", "deathclaw", "mirelurk",
|
||||
"synth", "institute", "enclave", "brotherhood", "minuteman",
|
||||
"railroad", "atom", "crater", "foundation", "refuge",
|
||||
"settler", "wanderer", "courier", "lone", "chosen",
|
||||
"tribal", "khan", "legion", "ncr", "ranger",
|
||||
"overseer", "sentinel", "paladin", "scribe", "initiate",
|
||||
"elder", "lancer", "knight", "squire", "proctor",
|
||||
},
|
||||
}
|
||||
|
||||
// NamePool manages a bounded pool of reusable polecat names.
|
||||
// Names in the pool are polecat-01 through polecat-50.
|
||||
// Names are drawn from a themed pool (mad-max by default).
|
||||
// When the pool is exhausted, overflow names use rigname-N format.
|
||||
type NamePool struct {
|
||||
mu sync.RWMutex
|
||||
@@ -26,14 +66,23 @@ type NamePool struct {
|
||||
// RigName is the rig this pool belongs to.
|
||||
RigName string `json:"rig_name"`
|
||||
|
||||
// InUse tracks which pool indices are currently in use.
|
||||
// Key is the pool index (1-50), value is true if in use.
|
||||
InUse map[int]bool `json:"in_use"`
|
||||
// Theme is the current theme name (e.g., "mad-max", "minerals").
|
||||
Theme string `json:"theme"`
|
||||
|
||||
// CustomNames allows overriding the built-in theme names.
|
||||
CustomNames []string `json:"custom_names,omitempty"`
|
||||
|
||||
// InUse tracks which pool names are currently in use.
|
||||
// Key is the name itself, value is true if in use.
|
||||
InUse map[string]bool `json:"in_use"`
|
||||
|
||||
// OverflowNext is the next overflow sequence number.
|
||||
// Starts at PoolSize+1 (51) and increments.
|
||||
// Starts at MaxSize+1 and increments.
|
||||
OverflowNext int `json:"overflow_next"`
|
||||
|
||||
// MaxSize is the maximum number of themed names before overflow.
|
||||
MaxSize int `json:"max_size"`
|
||||
|
||||
// stateFile is the path to persist pool state.
|
||||
stateFile string
|
||||
}
|
||||
@@ -42,12 +91,50 @@ type NamePool struct {
|
||||
func NewNamePool(rigPath, rigName string) *NamePool {
|
||||
return &NamePool{
|
||||
RigName: rigName,
|
||||
InUse: make(map[int]bool),
|
||||
OverflowNext: PoolSize + 1,
|
||||
Theme: DefaultTheme,
|
||||
InUse: make(map[string]bool),
|
||||
OverflowNext: DefaultPoolSize + 1,
|
||||
MaxSize: DefaultPoolSize,
|
||||
stateFile: filepath.Join(rigPath, ".gastown", "namepool.json"),
|
||||
}
|
||||
}
|
||||
|
||||
// NewNamePoolWithConfig creates a name pool with specific configuration.
|
||||
func NewNamePoolWithConfig(rigPath, rigName, theme string, customNames []string, maxSize int) *NamePool {
|
||||
if theme == "" {
|
||||
theme = DefaultTheme
|
||||
}
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultPoolSize
|
||||
}
|
||||
|
||||
return &NamePool{
|
||||
RigName: rigName,
|
||||
Theme: theme,
|
||||
CustomNames: customNames,
|
||||
InUse: make(map[string]bool),
|
||||
OverflowNext: maxSize + 1,
|
||||
MaxSize: maxSize,
|
||||
stateFile: filepath.Join(rigPath, ".gastown", "namepool.json"),
|
||||
}
|
||||
}
|
||||
|
||||
// getNames returns the list of names to use for the pool.
|
||||
func (p *NamePool) getNames() []string {
|
||||
// Custom names take precedence
|
||||
if len(p.CustomNames) > 0 {
|
||||
return p.CustomNames
|
||||
}
|
||||
|
||||
// Look up built-in theme
|
||||
if names, ok := BuiltinThemes[p.Theme]; ok {
|
||||
return names
|
||||
}
|
||||
|
||||
// Fall back to default theme
|
||||
return BuiltinThemes[DefaultTheme]
|
||||
}
|
||||
|
||||
// Load loads the pool state from disk.
|
||||
func (p *NamePool) Load() error {
|
||||
p.mu.Lock()
|
||||
@@ -57,8 +144,8 @@ func (p *NamePool) Load() error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Initialize with empty state
|
||||
p.InUse = make(map[int]bool)
|
||||
p.OverflowNext = PoolSize + 1
|
||||
p.InUse = make(map[string]bool)
|
||||
p.OverflowNext = p.MaxSize + 1
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@@ -69,13 +156,24 @@ func (p *NamePool) Load() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Preserve the theme and custom names if already set
|
||||
if p.Theme == "" && loaded.Theme != "" {
|
||||
p.Theme = loaded.Theme
|
||||
}
|
||||
if len(p.CustomNames) == 0 && len(loaded.CustomNames) > 0 {
|
||||
p.CustomNames = loaded.CustomNames
|
||||
}
|
||||
|
||||
p.InUse = loaded.InUse
|
||||
if p.InUse == nil {
|
||||
p.InUse = make(map[int]bool)
|
||||
p.InUse = make(map[string]bool)
|
||||
}
|
||||
p.OverflowNext = loaded.OverflowNext
|
||||
if p.OverflowNext < PoolSize+1 {
|
||||
p.OverflowNext = PoolSize + 1
|
||||
if p.OverflowNext < p.MaxSize+1 {
|
||||
p.OverflowNext = p.MaxSize + 1
|
||||
}
|
||||
if loaded.MaxSize > 0 {
|
||||
p.MaxSize = loaded.MaxSize
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -100,17 +198,20 @@ func (p *NamePool) Save() error {
|
||||
}
|
||||
|
||||
// Allocate returns a name from the pool.
|
||||
// It prefers lower-numbered pool slots, and falls back to overflow names
|
||||
// It prefers names in order from the theme list, and falls back to overflow names
|
||||
// when the pool is exhausted.
|
||||
func (p *NamePool) Allocate() (string, error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Try to find first available slot in pool (prefer low numbers)
|
||||
for i := 1; i <= PoolSize; i++ {
|
||||
if !p.InUse[i] {
|
||||
p.InUse[i] = true
|
||||
return p.formatPoolName(i), nil
|
||||
names := p.getNames()
|
||||
|
||||
// Try to find first available name from the theme
|
||||
for i := 0; i < len(names) && i < p.MaxSize; i++ {
|
||||
name := names[i]
|
||||
if !p.InUse[name] {
|
||||
p.InUse[name] = true
|
||||
return name, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,17 +227,27 @@ func (p *NamePool) Release(name string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
idx := p.parsePoolIndex(name)
|
||||
if idx > 0 && idx <= PoolSize {
|
||||
delete(p.InUse, idx)
|
||||
// Check if it's a themed name
|
||||
if p.isThemedName(name) {
|
||||
delete(p.InUse, name)
|
||||
}
|
||||
// Overflow names are not reusable, so we don't track them
|
||||
}
|
||||
|
||||
// IsPoolName returns true if the name is a pool name (polecat-NN format).
|
||||
// isThemedName checks if a name is in the theme pool.
|
||||
func (p *NamePool) isThemedName(name string) bool {
|
||||
names := p.getNames()
|
||||
for _, n := range names {
|
||||
if n == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsPoolName returns true if the name is a pool name (themed or numbered).
|
||||
func (p *NamePool) IsPoolName(name string) bool {
|
||||
idx := p.parsePoolIndex(name)
|
||||
return idx > 0 && idx <= PoolSize
|
||||
return p.isThemedName(name)
|
||||
}
|
||||
|
||||
// ActiveCount returns the number of names currently in use from the pool.
|
||||
@@ -152,8 +263,8 @@ func (p *NamePool) ActiveNames() []string {
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
var names []string
|
||||
for idx := range p.InUse {
|
||||
names = append(names, p.formatPoolName(idx))
|
||||
for name := range p.InUse {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
@@ -164,9 +275,8 @@ func (p *NamePool) MarkInUse(name string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
idx := p.parsePoolIndex(name)
|
||||
if idx > 0 && idx <= PoolSize {
|
||||
p.InUse[idx] = true
|
||||
if p.isThemedName(name) {
|
||||
p.InUse[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,41 +287,93 @@ func (p *NamePool) Reconcile(existingPolecats []string) {
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Clear current state
|
||||
p.InUse = make(map[int]bool)
|
||||
p.InUse = make(map[string]bool)
|
||||
|
||||
// Mark all existing polecats as in use
|
||||
for _, name := range existingPolecats {
|
||||
idx := p.parsePoolIndex(name)
|
||||
if idx > 0 && idx <= PoolSize {
|
||||
p.InUse[idx] = true
|
||||
if p.isThemedName(name) {
|
||||
p.InUse[name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatPoolName formats a pool index as a name.
|
||||
func (p *NamePool) formatPoolName(idx int) string {
|
||||
return fmt.Sprintf("%s%02d", NamePrefix, idx)
|
||||
}
|
||||
|
||||
// formatOverflowName formats an overflow sequence number as a name.
|
||||
func (p *NamePool) formatOverflowName(seq int) string {
|
||||
return fmt.Sprintf("%s-%d", p.RigName, seq)
|
||||
}
|
||||
|
||||
// parsePoolIndex extracts the pool index from a pool name.
|
||||
// Returns 0 if not a valid pool name.
|
||||
func (p *NamePool) parsePoolIndex(name string) int {
|
||||
if len(name) < len(NamePrefix)+2 {
|
||||
return 0
|
||||
}
|
||||
if name[:len(NamePrefix)] != NamePrefix {
|
||||
return 0
|
||||
// GetTheme returns the current theme name.
|
||||
func (p *NamePool) GetTheme() string {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.Theme
|
||||
}
|
||||
|
||||
// SetTheme sets the theme and resets the pool.
|
||||
// Existing in-use names are preserved if they exist in the new theme.
|
||||
func (p *NamePool) SetTheme(theme string) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if _, ok := BuiltinThemes[theme]; !ok {
|
||||
return fmt.Errorf("unknown theme: %s (available: mad-max, minerals, wasteland)", theme)
|
||||
}
|
||||
|
||||
var idx int
|
||||
_, err := fmt.Sscanf(name[len(NamePrefix):], "%d", &idx)
|
||||
if err != nil {
|
||||
return 0
|
||||
// Preserve names that exist in both themes
|
||||
newNames := BuiltinThemes[theme]
|
||||
newInUse := make(map[string]bool)
|
||||
for name := range p.InUse {
|
||||
for _, n := range newNames {
|
||||
if n == name {
|
||||
newInUse[name] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return idx
|
||||
|
||||
p.Theme = theme
|
||||
p.InUse = newInUse
|
||||
p.CustomNames = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListThemes returns the list of available built-in themes.
|
||||
func ListThemes() []string {
|
||||
themes := make([]string, 0, len(BuiltinThemes))
|
||||
for theme := range BuiltinThemes {
|
||||
themes = append(themes, theme)
|
||||
}
|
||||
sort.Strings(themes)
|
||||
return themes
|
||||
}
|
||||
|
||||
// GetThemeNames returns the names in a specific theme.
|
||||
func GetThemeNames(theme string) ([]string, error) {
|
||||
if names, ok := BuiltinThemes[theme]; ok {
|
||||
return names, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown theme: %s", theme)
|
||||
}
|
||||
|
||||
// AddCustomName adds a custom name to the pool.
|
||||
func (p *NamePool) AddCustomName(name string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Check if already in custom names
|
||||
for _, n := range p.CustomNames {
|
||||
if n == name {
|
||||
return
|
||||
}
|
||||
}
|
||||
p.CustomNames = append(p.CustomNames, name)
|
||||
}
|
||||
|
||||
// Reset clears the pool state, releasing all names.
|
||||
func (p *NamePool) Reset() {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
p.InUse = make(map[string]bool)
|
||||
p.OverflowNext = p.MaxSize + 1
|
||||
}
|
||||
|
||||
@@ -15,22 +15,22 @@ func TestNamePool_Allocate(t *testing.T) {
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
|
||||
// First allocation should be polecat-01
|
||||
// First allocation should be first themed name (furiosa)
|
||||
name, err := pool.Allocate()
|
||||
if err != nil {
|
||||
t.Fatalf("Allocate error: %v", err)
|
||||
}
|
||||
if name != "polecat-01" {
|
||||
t.Errorf("expected polecat-01, got %s", name)
|
||||
if name != "furiosa" {
|
||||
t.Errorf("expected furiosa, got %s", name)
|
||||
}
|
||||
|
||||
// Second allocation should be polecat-02
|
||||
// Second allocation should be nux
|
||||
name, err = pool.Allocate()
|
||||
if err != nil {
|
||||
t.Fatalf("Allocate error: %v", err)
|
||||
}
|
||||
if name != "polecat-02" {
|
||||
t.Errorf("expected polecat-02, got %s", name)
|
||||
if name != "nux" {
|
||||
t.Errorf("expected nux, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,21 +47,21 @@ func TestNamePool_Release(t *testing.T) {
|
||||
name1, _ := pool.Allocate()
|
||||
name2, _ := pool.Allocate()
|
||||
|
||||
if name1 != "polecat-01" || name2 != "polecat-02" {
|
||||
if name1 != "furiosa" || name2 != "nux" {
|
||||
t.Fatalf("unexpected allocations: %s, %s", name1, name2)
|
||||
}
|
||||
|
||||
// Release first one
|
||||
pool.Release("polecat-01")
|
||||
pool.Release("furiosa")
|
||||
|
||||
// Next allocation should reuse polecat-01
|
||||
// Next allocation should reuse furiosa
|
||||
name, _ := pool.Allocate()
|
||||
if name != "polecat-01" {
|
||||
t.Errorf("expected polecat-01 to be reused, got %s", name)
|
||||
if name != "furiosa" {
|
||||
t.Errorf("expected furiosa to be reused, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamePool_PrefersLowNumbers(t *testing.T) {
|
||||
func TestNamePool_PrefersOrder(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "namepool-test-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -75,20 +75,20 @@ func TestNamePool_PrefersLowNumbers(t *testing.T) {
|
||||
pool.Allocate()
|
||||
}
|
||||
|
||||
// Release 03 and 01
|
||||
pool.Release("polecat-03")
|
||||
pool.Release("polecat-01")
|
||||
// Release slit and furiosa
|
||||
pool.Release("slit")
|
||||
pool.Release("furiosa")
|
||||
|
||||
// Next allocation should be 01 (lowest available)
|
||||
// Next allocation should be furiosa (first in theme order)
|
||||
name, _ := pool.Allocate()
|
||||
if name != "polecat-01" {
|
||||
t.Errorf("expected polecat-01 (lowest), got %s", name)
|
||||
if name != "furiosa" {
|
||||
t.Errorf("expected furiosa (first in order), got %s", name)
|
||||
}
|
||||
|
||||
// Next should be 03
|
||||
// Next should be slit
|
||||
name, _ = pool.Allocate()
|
||||
if name != "polecat-03" {
|
||||
t.Errorf("expected polecat-03, got %s", name)
|
||||
if name != "slit" {
|
||||
t.Errorf("expected slit, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,10 +99,10 @@ func TestNamePool_Overflow(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "gastown")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "gastown", "mad-max", nil, 5)
|
||||
|
||||
// Exhaust the pool
|
||||
for i := 0; i < PoolSize; i++ {
|
||||
// Exhaust the small pool
|
||||
for i := 0; i < 5; i++ {
|
||||
pool.Allocate()
|
||||
}
|
||||
|
||||
@@ -111,15 +111,15 @@ func TestNamePool_Overflow(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Allocate error: %v", err)
|
||||
}
|
||||
expected := "gastown-51"
|
||||
expected := "gastown-6"
|
||||
if name != expected {
|
||||
t.Errorf("expected overflow name %s, got %s", expected, name)
|
||||
}
|
||||
|
||||
// Next overflow
|
||||
name, _ = pool.Allocate()
|
||||
if name != "gastown-52" {
|
||||
t.Errorf("expected gastown-52, got %s", name)
|
||||
if name != "gastown-7" {
|
||||
t.Errorf("expected gastown-7, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,26 +130,26 @@ func TestNamePool_OverflowNotReusable(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "gastown")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "gastown", "mad-max", nil, 3)
|
||||
|
||||
// Exhaust the pool
|
||||
for i := 0; i < PoolSize; i++ {
|
||||
for i := 0; i < 3; i++ {
|
||||
pool.Allocate()
|
||||
}
|
||||
|
||||
// Get overflow name
|
||||
overflow1, _ := pool.Allocate()
|
||||
if overflow1 != "gastown-51" {
|
||||
t.Fatalf("expected gastown-51, got %s", overflow1)
|
||||
if overflow1 != "gastown-4" {
|
||||
t.Fatalf("expected gastown-4, got %s", overflow1)
|
||||
}
|
||||
|
||||
// Release it - should not be reused
|
||||
pool.Release(overflow1)
|
||||
|
||||
// Next allocation should be gastown-52, not gastown-51
|
||||
// Next allocation should be gastown-5, not gastown-4
|
||||
name, _ := pool.Allocate()
|
||||
if name != "gastown-52" {
|
||||
t.Errorf("expected gastown-52 (overflow increments), got %s", name)
|
||||
if name != "gastown-5" {
|
||||
t.Errorf("expected gastown-5 (overflow increments), got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +163,10 @@ func TestNamePool_SaveLoad(t *testing.T) {
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
|
||||
// Allocate some names
|
||||
pool.Allocate() // 01
|
||||
pool.Allocate() // 02
|
||||
pool.Allocate() // 03
|
||||
pool.Release("polecat-02")
|
||||
pool.Allocate() // furiosa
|
||||
pool.Allocate() // nux
|
||||
pool.Allocate() // slit
|
||||
pool.Release("nux")
|
||||
|
||||
// Save state
|
||||
if err := pool.Save(); err != nil {
|
||||
@@ -179,15 +179,15 @@ func TestNamePool_SaveLoad(t *testing.T) {
|
||||
t.Fatalf("Load error: %v", err)
|
||||
}
|
||||
|
||||
// Should have 01 and 03 in use
|
||||
// Should have furiosa and slit in use
|
||||
if pool2.ActiveCount() != 2 {
|
||||
t.Errorf("expected 2 active, got %d", pool2.ActiveCount())
|
||||
}
|
||||
|
||||
// Next allocation should be 02 (released slot)
|
||||
// Next allocation should be nux (released slot)
|
||||
name, _ := pool2.Allocate()
|
||||
if name != "polecat-02" {
|
||||
t.Errorf("expected polecat-02, got %s", name)
|
||||
if name != "nux" {
|
||||
t.Errorf("expected nux, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ func TestNamePool_Reconcile(t *testing.T) {
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
|
||||
// Simulate existing polecats from filesystem
|
||||
existing := []string{"polecat-03", "polecat-07", "some-other-name"}
|
||||
existing := []string{"slit", "valkyrie", "some-other-name"}
|
||||
|
||||
pool.Reconcile(existing)
|
||||
|
||||
@@ -209,10 +209,10 @@ func TestNamePool_Reconcile(t *testing.T) {
|
||||
t.Errorf("expected 2 active after reconcile, got %d", pool.ActiveCount())
|
||||
}
|
||||
|
||||
// Should allocate 01 first (not 03 or 07)
|
||||
// Should allocate furiosa first (not slit or valkyrie)
|
||||
name, _ := pool.Allocate()
|
||||
if name != "polecat-01" {
|
||||
t.Errorf("expected polecat-01, got %s", name)
|
||||
if name != "furiosa" {
|
||||
t.Errorf("expected furiosa, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,13 +229,12 @@ func TestNamePool_IsPoolName(t *testing.T) {
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{"polecat-01", true},
|
||||
{"polecat-50", true},
|
||||
{"polecat-51", false}, // > PoolSize
|
||||
{"furiosa", true},
|
||||
{"nux", true},
|
||||
{"max", true},
|
||||
{"gastown-51", false}, // overflow format
|
||||
{"Nux", false}, // legacy name
|
||||
{"polecat-", false}, // invalid
|
||||
{"polecat-abc", false},
|
||||
{"random-name", false},
|
||||
{"polecat-01", false}, // old format
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -255,17 +254,18 @@ func TestNamePool_ActiveNames(t *testing.T) {
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
|
||||
pool.Allocate() // 01
|
||||
pool.Allocate() // 02
|
||||
pool.Allocate() // 03
|
||||
pool.Release("polecat-02")
|
||||
pool.Allocate() // furiosa
|
||||
pool.Allocate() // nux
|
||||
pool.Allocate() // slit
|
||||
pool.Release("nux")
|
||||
|
||||
names := pool.ActiveNames()
|
||||
if len(names) != 2 {
|
||||
t.Errorf("expected 2 active names, got %d", len(names))
|
||||
}
|
||||
if names[0] != "polecat-01" || names[1] != "polecat-03" {
|
||||
t.Errorf("expected [polecat-01, polecat-03], got %v", names)
|
||||
// Names are sorted
|
||||
if names[0] != "furiosa" || names[1] != "slit" {
|
||||
t.Errorf("expected [furiosa, slit], got %v", names)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,17 +279,17 @@ func TestNamePool_MarkInUse(t *testing.T) {
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
|
||||
// Mark some slots as in use
|
||||
pool.MarkInUse("polecat-05")
|
||||
pool.MarkInUse("polecat-10")
|
||||
pool.MarkInUse("dementus")
|
||||
pool.MarkInUse("valkyrie")
|
||||
|
||||
// Allocate should skip those
|
||||
name, _ := pool.Allocate()
|
||||
if name != "polecat-01" {
|
||||
t.Errorf("expected polecat-01, got %s", name)
|
||||
if name != "furiosa" {
|
||||
t.Errorf("expected furiosa, got %s", name)
|
||||
}
|
||||
|
||||
// Mark more and verify count
|
||||
if pool.ActiveCount() != 3 { // 01, 05, 10
|
||||
// Verify count
|
||||
if pool.ActiveCount() != 3 { // furiosa, dementus, valkyrie
|
||||
t.Errorf("expected 3 active, got %d", pool.ActiveCount())
|
||||
}
|
||||
}
|
||||
@@ -313,3 +313,120 @@ func TestNamePool_StateFilePath(t *testing.T) {
|
||||
t.Errorf("state file not found at expected path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamePool_Themes(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "namepool-test-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
// Test minerals theme
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "minerals", nil, 50)
|
||||
|
||||
name, err := pool.Allocate()
|
||||
if err != nil {
|
||||
t.Fatalf("Allocate error: %v", err)
|
||||
}
|
||||
if name != "obsidian" {
|
||||
t.Errorf("expected obsidian (first mineral), got %s", name)
|
||||
}
|
||||
|
||||
// Test theme switching
|
||||
if err := pool.SetTheme("wasteland"); err != nil {
|
||||
t.Fatalf("SetTheme error: %v", err)
|
||||
}
|
||||
|
||||
// obsidian should be released (not in wasteland theme)
|
||||
name, _ = pool.Allocate()
|
||||
if name != "rust" {
|
||||
t.Errorf("expected rust (first wasteland name), got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamePool_CustomNames(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "namepool-test-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
custom := []string{"alpha", "beta", "gamma", "delta"}
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "", custom, 4)
|
||||
|
||||
name, _ := pool.Allocate()
|
||||
if name != "alpha" {
|
||||
t.Errorf("expected alpha, got %s", name)
|
||||
}
|
||||
|
||||
name, _ = pool.Allocate()
|
||||
if name != "beta" {
|
||||
t.Errorf("expected beta, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListThemes(t *testing.T) {
|
||||
themes := ListThemes()
|
||||
if len(themes) != 3 {
|
||||
t.Errorf("expected 3 themes, got %d", len(themes))
|
||||
}
|
||||
|
||||
// Check that all expected themes are present
|
||||
expected := map[string]bool{"mad-max": true, "minerals": true, "wasteland": true}
|
||||
for _, theme := range themes {
|
||||
if !expected[theme] {
|
||||
t.Errorf("unexpected theme: %s", theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetThemeNames(t *testing.T) {
|
||||
names, err := GetThemeNames("mad-max")
|
||||
if err != nil {
|
||||
t.Fatalf("GetThemeNames error: %v", err)
|
||||
}
|
||||
if len(names) != 50 {
|
||||
t.Errorf("expected 50 mad-max names, got %d", len(names))
|
||||
}
|
||||
if names[0] != "furiosa" {
|
||||
t.Errorf("expected first name to be furiosa, got %s", names[0])
|
||||
}
|
||||
|
||||
// Test invalid theme
|
||||
_, err = GetThemeNames("invalid-theme")
|
||||
if err == nil {
|
||||
t.Error("expected error for invalid theme")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamePool_Reset(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "namepool-test-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
|
||||
// Allocate several names
|
||||
for i := 0; i < 10; i++ {
|
||||
pool.Allocate()
|
||||
}
|
||||
|
||||
if pool.ActiveCount() != 10 {
|
||||
t.Errorf("expected 10 active, got %d", pool.ActiveCount())
|
||||
}
|
||||
|
||||
// Reset
|
||||
pool.Reset()
|
||||
|
||||
if pool.ActiveCount() != 0 {
|
||||
t.Errorf("expected 0 active after reset, got %d", pool.ActiveCount())
|
||||
}
|
||||
|
||||
// Should allocate furiosa again
|
||||
name, _ := pool.Allocate()
|
||||
if name != "furiosa" {
|
||||
t.Errorf("expected furiosa after reset, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user