ronantakizawa/codereview-bench huggingface dataset:
Repository: etcd-io/etcd
File: scripts/test.sh (Shell)

Code before:
  log_callout "Downloading $file"

  set +e
  curl --fail -L "https://github.com/etcd-io/etcd/releases/download/$UPGRADE_VER/$file" -o "/tmp/$file"
  local result=$?
  set -e
  case $result in
    0)  ;;
    *)  log_error "--- FAIL:" ${result}
      return $result
      ;;
  esac

  tar xzvf "/tmp/$file" -C /tmp/ --strip-components=1
  mkdir -p ./bin
  mv /tmp/etcd ./bin/etcd-last-release
}

function release_tests_pass {
  VERSION=3.4.99

  if [ "${1-}" ]; then
    VERSION=$1
  fi

  if [ "${CI}" ]; then
    git config user.email "prow@etcd.io"
    git config user.name "Prow"

    gpg --batch --gen-key <<EOF
%no-protection
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: Prow
Name-Email: prow@etcd.io
Expire-Date: 0
EOF

    git remote add origin https://github.com/etcd-io/etcd.git
  fi

  DRY_RUN=true run "${ETCD_ROOT_DIR}/scripts/release.sh" --no-upload --no-docker-push --no-gh-release --in-place "${VERSION}"
  VERSION="${VERSION}" run "${ETCD_ROOT_DIR}/scripts/test_images.sh"
}

function mod_tidy_for_module {
  run go mod tidy -diff
}


Review comment (suggestion):
Suggestion to use something like:

```suggestion
  if [ -n "${CI:-}" ]; then
```

Code after:
  log_callout "Downloading $file"

  set +e
  curl --fail -L "https://github.com/etcd-io/etcd/releases/download/$UPGRADE_VER/$file" -o "/tmp/$file"
  local result=$?
  set -e
  case $result in
    0)  ;;
    *)  log_error "--- FAIL:" ${result}
      return $result
      ;;
  esac

  tar xzvf "/tmp/$file" -C /tmp/ --strip-components=1
  mkdir -p ./bin
  mv /tmp/etcd ./bin/etcd-last-release
}

function release_tests_pass {
  VERSION=$(go list -m go.etcd.io/etcd/api/v3 2>/dev/null | awk '{split(substr($2,2), a, "."); print a[1]"."a[2]".99"}')

  if [ -n "${CI:-}" ]; then
    git config user.email "prow@etcd.io"
    git config user.name "Prow"

    gpg --batch --gen-key <<EOF
%no-protection
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: Prow
Name-Email: prow@etcd.io
Expire-Date: 0
EOF

    git remote add origin https://github.com/etcd-io/etcd.git
  fi

  DRY_RUN=true run "${ETCD_ROOT_DIR}/scripts/release.sh" --no-upload --no-docker-push --no-gh-release --in-place "${VERSION}"
  VERSION="${VERSION}" run "${ETCD_ROOT_DIR}/scripts/test_images.sh"
}

function mod_tidy_for_module {
  run go mod tidy -diff
}

function mod_tidy_pass {
  run_for_modules generic_checker mod_tidy_for_module
}
Repository: etcd-io/etcd
File: scripts/test.sh (Shell)

Code before:
  if [[ "$(uname -s)" == 'Darwin' ]]; then
    file="etcd-$UPGRADE_VER-darwin-$GOARCH.zip"
  else
    file="etcd-$UPGRADE_VER-linux-$GOARCH.tar.gz"
  fi

  log_callout "Downloading $file"

  set +e
  curl --fail -L "https://github.com/etcd-io/etcd/releases/download/$UPGRADE_VER/$file" -o "/tmp/$file"
  local result=$?
  set -e
  case $result in
    0)  ;;
    *)  log_error "--- FAIL:" ${result}
      return $result
      ;;
  esac

  tar xzvf "/tmp/$file" -C /tmp/ --strip-components=1
  mkdir -p ./bin
  mv /tmp/etcd ./bin/etcd-last-release
}

function mod_tidy_for_module {
  run go mod tidy -diff
}

function mod_tidy_pass {
  run_for_modules generic_checker mod_tidy_for_module
}

function proto_annotations_pass {
  "${ETCD_ROOT_DIR}/scripts/verify_proto_annotations.sh"
}

function genproto_pass {
  "${ETCD_ROOT_DIR}/scripts/verify_genproto.sh"
}

########### MAIN ###############################################################

function run_pass {
  local pass="${1}"
  shift 1
  log_callout -e "\\n'${pass}' started at $(date)"
  if "${pass}_pass" "$@" ; then
    log_success "'${pass}' PASSED and completed at $(date)"
    return 0
  else
    log_error "FAIL: '${pass}' FAILED at $(date)"

Review comment (suggestion):
I would either create a local variable for this or support overriding the `VERSION` variable (I would go with the latter for cases such as 3.4).

```sh
  local version
  version=$(go list -m go.etcd.io/etcd/api/v3 2>/dev/null | awk '{split(substr($2,2), a, "."); print a[1]"."a[2]".99"}')
```

```sh
  if [ -z "${VERSION:-}" ]; then
    VERSION=$(go list -m go.etcd.io/etcd/api/v3 2>/dev/null | awk '{split(substr($2,2), a, "."); print a[1]"."a[2]".99"}')
  fi
```

Code after:
  if [[ "$(uname -s)" == 'Darwin' ]]; then
    file="etcd-$UPGRADE_VER-darwin-$GOARCH.zip"
  else
    file="etcd-$UPGRADE_VER-linux-$GOARCH.tar.gz"
  fi

  log_callout "Downloading $file"

  set +e
  curl --fail -L "https://github.com/etcd-io/etcd/releases/download/$UPGRADE_VER/$file" -o "/tmp/$file"
  local result=$?
  set -e
  case $result in
    0)  ;;
    *)  log_error "--- FAIL:" ${result}
      return $result
      ;;
  esac

  tar xzvf "/tmp/$file" -C /tmp/ --strip-components=1
  mkdir -p ./bin
  mv /tmp/etcd ./bin/etcd-last-release
}

function release_tests_pass {
  VERSION=$(go list -m go.etcd.io/etcd/api/v3 2>/dev/null | awk '{split(substr($2,2), a, "."); print a[1]"."a[2]".99"}')

  if [ -n "${CI:-}" ]; then
    git config user.email "prow@etcd.io"
    git config user.name "Prow"

    gpg --batch --gen-key <<EOF
%no-protection
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: Prow
Name-Email: prow@etcd.io
Expire-Date: 0
EOF

    git remote add origin https://github.com/etcd-io/etcd.git
  fi

  DRY_RUN=true run "${ETCD_ROOT_DIR}/scripts/release.sh" --no-upload --no-docker-push --no-gh-release --in-place "${VERSION}"
  VERSION="${VERSION}" run "${ETCD_ROOT_DIR}/scripts/test_images.sh"
}

function mod_tidy_for_module {
  run go mod tidy -diff
Repository: etcd-io/etcd
File: client/pkg/types/set.go (Go)

Code before:
// 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 types

import (
	"reflect"
	"sort"
	"sync"
)

type Set interface {
	Add(string)
	Remove(string)
	Contains(string) bool
	Equals(Set) bool
	Length() int
	Values() []string
	Copy() Set
	Sub(Set) Set
}

type UnsafeSet interface {
	Set
	ContainsAll(values []string) bool
}

func NewUnsafeSet(values ...string) UnsafeSet {
	set := &unsafeSet{make(map[string]struct{})}
	for _, v := range values {
		set.Add(v)
	}
	return set
}

func NewThreadsafeSet(values ...string) Set {
	us := NewUnsafeSet(values...)
	return &tsafeSet{us, sync.RWMutex{}}
}

var _ UnsafeSet = (*unsafeSet)(nil)

type unsafeSet struct {
	d map[string]struct{}
}

// Add adds a new value to the set (no-op if the value is already present)
func (us *unsafeSet) Add(value string) {
	us.d[value] = struct{}{}
}

Review comment (suggestion):
`ContainsAll` should be part of `Set`. But a better choice is to change `Contains(string) bool` to `Contains(val ...string) bool`,
- It's backward compatible.
- It also covers the functionality of `ContainsAll`

But in order to be backward compatible, we need to keep `ContainsAll` (add it into `Set`), but mark it as deprecated, and recommend users to use `Contains(val ...string) bool` instead.

Code after:
// 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 types

import (
	"reflect"
	"sort"
	"sync"
)

type Set interface {
	Add(string)
	Remove(string)
	Contains(val ...string) bool
	Equals(Set) bool
	Length() int
	Values() []string
	Copy() Set
	Sub(Set) Set

	// ContainsAll returns whether the set contains all given values
	// Deprecated: Use Contains instead.
	ContainsAll(values []string) bool
}

type ThreadsafeSet interface {
	Set
}

func NewUnsafeSet(values ...string) Set {
	set := &unsafeSet{make(map[string]struct{})}
	for _, v := range values {
		set.Add(v)
	}
	return set
}

func NewThreadsafeSet(values ...string) ThreadsafeSet {
	us := NewUnsafeSet(values...)
	return &tsafeSet{us, sync.RWMutex{}}
}

var _ Set = (*unsafeSet)(nil)

type unsafeSet struct {
	d map[string]struct{}
}

// Add adds a new value to the set (no-op if the value is already present)
Repository: etcd-io/etcd
File: client/pkg/types/set.go (Go)

Code before:
func (ts *tsafeSet) Add(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Add(value)
}

func (ts *tsafeSet) Remove(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Remove(value)
}

func (ts *tsafeSet) Contains(values ...string) (exists bool) {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Contains(values...)
}

// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (ts *tsafeSet) ContainsAll(values []string) bool {
	for _, s := range values {
		if !ts.Contains(s) {
			return false
		}
	}
	return true
}

func (ts *tsafeSet) Equals(other Set) bool {
	ts.m.RLock()
	defer ts.m.RUnlock()

	// If ts and other represent the same variable, avoid calling
	// ts.us.Equals(other), to avoid double RLock bug
	if _other, ok := other.(*tsafeSet); ok {
		if _other == ts {
			return true
		}
	}
	return ts.us.Equals(other)
}

func (ts *tsafeSet) Length() int {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Length()
}

func (ts *tsafeSet) Values() (values []string) {
	ts.m.RLock()

Review comment (suggestion):
```suggestion
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.ContainsAll()
```

Code after:
	us Set
	m  sync.RWMutex
}

func (ts *tsafeSet) Add(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Add(value)
}

func (ts *tsafeSet) Remove(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Remove(value)
}

func (ts *tsafeSet) Contains(values ...string) (exists bool) {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Contains(values...)
}

// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (ts *tsafeSet) ContainsAll(values []string) bool {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.ContainsAll(values)
}

func (ts *tsafeSet) Equals(other Set) bool {
	ts.m.RLock()
	defer ts.m.RUnlock()

	// If ts and other represent the same variable, avoid calling
	// ts.us.Equals(other), to avoid double RLock bug
	if _other, ok := other.(*tsafeSet); ok {
		if _other == ts {
			return true
		}
	}
	return ts.us.Equals(other)
}

func (ts *tsafeSet) Length() int {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Length()
}

func (ts *tsafeSet) Values() (values []string) {
Repository: etcd-io/etcd
File: client/pkg/types/set.go (Go)

Code before:
	return cp
}

// Sub removes all elements in other from the set
func (us *unsafeSet) Sub(other Set) Set {
	oValues := other.Values()
	result := us.Copy().(*unsafeSet)

	for _, val := range oValues {
		if _, ok := result.d[val]; !ok {
			continue
		}
		delete(result.d, val)
	}

	return result
}

var _ Set = (*tsafeSet)(nil)

type tsafeSet struct {
	us Set
	m  sync.RWMutex
}

func (ts *tsafeSet) Add(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Add(value)
}

func (ts *tsafeSet) Remove(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Remove(value)
}

func (ts *tsafeSet) Contains(values ...string) (exists bool) {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Contains(values...)
}

// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (ts *tsafeSet) ContainsAll(values []string) bool {
	for _, s := range values {
		if !ts.Contains(s) {
			return false
		}
	}

Review comment (suggestion):
pls revert this change. It should be explicitly `unsafeSet` instead of Set, otherwise you might run into indefinitely loop if it's a tsafeSet instance.

Code after:
	for val := range us.d {
		cp.Add(val)
	}

	return cp
}

// Sub removes all elements in other from the set
func (us *unsafeSet) Sub(other Set) Set {
	oValues := other.Values()
	result := us.Copy().(*unsafeSet)

	for _, val := range oValues {
		if _, ok := result.d[val]; !ok {
			continue
		}
		delete(result.d, val)
	}

	return result
}

var _ ThreadsafeSet = (*tsafeSet)(nil)

type tsafeSet struct {
	us Set
	m  sync.RWMutex
}

func (ts *tsafeSet) Add(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Add(value)
}

func (ts *tsafeSet) Remove(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Remove(value)
}

func (ts *tsafeSet) Contains(values ...string) (exists bool) {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Contains(values...)
}

// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (ts *tsafeSet) ContainsAll(values []string) bool {
	ts.m.RLock()
Repository: etcd-io/etcd
File: client/pkg/types/set.go (Go)

Code before:
	for val := range us.d {
		cp.Add(val)
	}

	return cp
}

// Sub removes all elements in other from the set
func (us *unsafeSet) Sub(other Set) Set {
	oValues := other.Values()
	result := us.Copy().(*unsafeSet)

	for _, val := range oValues {
		if _, ok := result.d[val]; !ok {
			continue
		}
		delete(result.d, val)
	}

	return result
}

var _ ThreadsafeSet = (*tsafeSet)(nil)

type tsafeSet struct {
	us ThreadsafeSet
	m  sync.RWMutex
}

func (ts *tsafeSet) Add(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Add(value)
}

func (ts *tsafeSet) Remove(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Remove(value)
}

func (ts *tsafeSet) Contains(values ...string) (exists bool) {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Contains(values...)
}

// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (ts *tsafeSet) ContainsAll(values []string) bool {
	for _, s := range values {

Review comment (suggestion):
The comment applies here as well. 

Please use `*unsafeSet` or `Set`

Code after:
	for val := range us.d {
		cp.Add(val)
	}

	return cp
}

// Sub removes all elements in other from the set
func (us *unsafeSet) Sub(other Set) Set {
	oValues := other.Values()
	result := us.Copy().(*unsafeSet)

	for _, val := range oValues {
		if _, ok := result.d[val]; !ok {
			continue
		}
		delete(result.d, val)
	}

	return result
}

var _ ThreadsafeSet = (*tsafeSet)(nil)

type tsafeSet struct {
	us Set
	m  sync.RWMutex
}

func (ts *tsafeSet) Add(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Add(value)
}

func (ts *tsafeSet) Remove(value string) {
	ts.m.Lock()
	defer ts.m.Unlock()
	ts.us.Remove(value)
}

func (ts *tsafeSet) Contains(values ...string) (exists bool) {
	ts.m.RLock()
	defer ts.m.RUnlock()
	return ts.us.Contains(values...)
}

// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (ts *tsafeSet) ContainsAll(values []string) bool {
	ts.m.RLock()
Repository: etcd-io/etcd
File: tests/robustness/main_test.go (Go)

Code before:
}

func testResultsDirectory(t *testing.T) string {
	resultsDirectory, ok := os.LookupEnv("RESULTS_DIR")
	if !ok {
		resultsDirectory = "/tmp/"
	}
	resultsDirectory, err := filepath.Abs(resultsDirectory)
	if err != nil {
		panic(err)
	}
	path, err := filepath.Abs(filepath.Join(
		resultsDirectory, strings.ReplaceAll(t.Name(), "/", "_"), fmt.Sprintf("%v", time.Now().UnixNano())))
	require.NoError(t, err)
	err = os.RemoveAll(path)
	require.NoError(t, err)
	err = os.MkdirAll(path, 0o700)
	require.NoError(t, err)
	return path
}

func processEndpoints(clus *e2e.EtcdProcessCluster) [][]string {
	endpoints := make([][]string, len(clus.Procs))
	for _, proc := range clus.Procs {
		for i, grpcEndpoint := range proc.EndpointsGRPC() {
			endpoints[i] = append(endpoints[i], grpcEndpoint)
		}
	}
	return endpoints
}


Review comment (suggestion):
`EndpointsGRPC()` returns all endpoints for process, however in this case one endpoint per process should be enough. We don't want to have too many clients.

Code after:
func testResultsDirectory(t *testing.T) string {
	resultsDirectory, ok := os.LookupEnv("RESULTS_DIR")
	if !ok {
		resultsDirectory = "/tmp/"
	}
	resultsDirectory, err := filepath.Abs(resultsDirectory)
	if err != nil {
		panic(err)
	}
	path, err := filepath.Abs(filepath.Join(
		resultsDirectory, strings.ReplaceAll(t.Name(), "/", "_"), fmt.Sprintf("%v", time.Now().UnixNano())))
	require.NoError(t, err)
	err = os.RemoveAll(path)
	require.NoError(t, err)
	err = os.MkdirAll(path, 0o700)
	require.NoError(t, err)
	return path
}

func processEndpoints(clus *e2e.EtcdProcessCluster) []string {
	endpoints := make([]string, len(clus.Procs))
	for _, proc := range clus.Procs {
		endpoints = append(endpoints, proc.EndpointsGRPC()[0])
	}
	return endpoints
}
Repository: etcd-io/etcd
File: tests/robustness/traffic/key_store.go (Go)

Code before:
		k.keys[k.counter] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
	}

	return k
}

func (k *keyStore) GetKey() string {
	k.mu.Lock()
	defer k.mu.Unlock()

	useKey := k.keys[rand.Intn(len(k.keys))]

	return useKey
}

func (k *keyStore) GetKeyForDelete() string {
	k.mu.Lock()
	defer k.mu.Unlock()

	useKeyIndex := rand.Intn(len(k.keys))
	useKey := k.keys[useKeyIndex]

	if rand.Intn(100) < 90 {
		k.keys[useKeyIndex] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
		k.counter++
	}

	return useKey
}

func (k *keyStore) GetKeysForMultiTxnOps(ops []model.OperationType) []string {
	k.mu.Lock()
	defer k.mu.Unlock()

	numOps := len(ops)

	if numOps > len(k.keys) {
		panic("GetKeysForMultiTxnOps: number of operations is more than the key pool size")
	}

	keys := make([]string, numOps)

	permutedKeyIndexes := rand.Perm(len(k.keys))
	for i, op := range ops {
		keys[i] = k.keys[permutedKeyIndexes[i]]

		if op == model.DeleteOperation && rand.Intn(100) < 90 {
			if rand.Intn(100) < 90 {
				k.keys[permutedKeyIndexes[i]] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
				k.counter++
			}

Review comment (refactor):
```suggestion
	if rand.Intn(100) < 90 {
		k.keys[useKeyIndex] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
		k.counter++
	}
```
Move this to a separate function`replaceKey`

Code after:
		k.keys[k.counter] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
	}

	return k
}

func (k *keyStore) GetKey() string {
	k.mu.Lock()
	defer k.mu.Unlock()

	useKey := k.keys[rand.Intn(len(k.keys))]

	return useKey
}

func (k *keyStore) GetKeyForDelete() string {
	k.mu.Lock()
	defer k.mu.Unlock()

	useKeyIndex := rand.Intn(len(k.keys))
	useKey := k.keys[useKeyIndex]

	k.replaceKey(useKeyIndex)

	return useKey
}

func (k *keyStore) GetKeysForMultiTxnOps(ops []model.OperationType) []string {
	k.mu.Lock()
	defer k.mu.Unlock()

	numOps := len(ops)

	if numOps > len(k.keys) {
		panic("GetKeysForMultiTxnOps: number of operations is more than the key pool size")
	}

	keys := make([]string, numOps)

	permutedKeyIndexes := rand.Perm(len(k.keys))
	for i, op := range ops {
		keys[i] = k.keys[permutedKeyIndexes[i]]

		if op == model.DeleteOperation {
			k.replaceKey(permutedKeyIndexes[i])
		}
	}

	return keys
}
Repository: etcd-io/etcd
File: tests/e2e/force_new_cluster_test.go (Go)

Code before:
// 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 e2e

import (
	"testing"

	"github.com/stretchr/testify/require"

	"go.etcd.io/etcd/tests/v3/framework/e2e"
)

func TestForceNewCluster(t *testing.T) {
	e2e.BeforeTest(t)

	epc, promotedMembers := mustCreateNewClusterByPromotingMembers(t, e2e.CurrentVersion, 5, e2e.WithKeepDataDir(true))
	require.Len(t, promotedMembers, 4)
	require.NoError(t, epc.Close())

	m := epc.Procs[0]
	t.Logf("Forcibly create a one-member cluster with member: %s", m.Config().Name)
	m.Config().Args = append(m.Config().Args, "--force-new-cluster")
	require.NoError(t, m.Start(t.Context()))

	t.Log("Restarting the member")
	require.NoError(t, m.Restart(t.Context()))

	t.Log("Closing the member")
	require.NoError(t, m.Close())
}


Review comment (suggestion):
While the panic happens in v3.6, the v2 store is no longer source of truth. Could we check (even manually) that resulting v2 store will work with etcd version that treats v2 store as source of truth. 

Meaning we should test if the resulting snapshot can be loaded by etcd v3.5.

Code after:
// 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 e2e

import (
	"testing"

	"github.com/stretchr/testify/require"

	"go.etcd.io/etcd/tests/v3/framework/config"
	"go.etcd.io/etcd/tests/v3/framework/e2e"
)

// TestForceNewCluster verified that etcd works as expected when --force-new-cluster.
// Refer to discussion in https://github.com/etcd-io/etcd/issues/20009.
func TestForceNewCluster(t *testing.T) {
	e2e.BeforeTest(t)

	testCases := []struct {
		name      string
		snapcount int
	}{
		{
			name:      "create a snapshot after promotion",
			snapcount: 10,
		},
		{
			name:      "no snapshot after promotion",
			snapcount: 0,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			epc, promotedMembers := mustCreateNewClusterByPromotingMembers(t, e2e.CurrentVersion, 5,
				e2e.WithKeepDataDir(true), e2e.WithSnapshotCount(uint64(tc.snapcount)))
			require.Len(t, promotedMembers, 4)

			for i := 0; i < tc.snapcount; i++ {
				err := epc.Etcdctl().Put(t.Context(), "foo", "bar", config.PutOptions{})
				require.NoError(t, err)
			}

			require.NoError(t, epc.Close())

			m := epc.Procs[0]
			t.Logf("Forcibly create a one-member cluster with member: %s", m.Config().Name)
Repository: etcd-io/etcd
File: tests/robustness/traffic/key_store.go (Go)

Code before:
	}

	k.latestRevision = resp.Header.GetRevision()

	kvs := resp.Kvs

	listKeys := make([]string, len(kvs))
	for i, kv := range kvs {
		listKeys[i] = string(kv.Key)
	}

	for _, key := range k.keys {
		if !slices.Contains(listKeys, key) {
			listKeys = append(listKeys, key)
		}
	}

	sort.Slice(listKeys, func(i, j int) bool {
		keyNumI, _ := strconv.Atoi(listKeys[i][len(k.keyPrefix):])
		keyNumJ, _ := strconv.Atoi(listKeys[j][len(k.keyPrefix):])
		return keyNumI < keyNumJ
	})

	for i := 0; i < len(k.keys); i++ {
		k.keys[i] = listKeys[i]
	}

	lastKeyNum, _ := strconv.Atoi(k.keys[len(k.keys)-1][len(k.keyPrefix):])
	k.counter = lastKeyNum + 1
}

func (k *keyStore) replaceKey(index int) {
	if rand.Intn(100) < 90 {
		k.keys[index] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
		k.counter++
	}
}


Review comment (suggestion):
```suggestion
	k.keys = listKeys[:len(k.keys)]
```

Code after:
//
// Notice that higher numbered keys will eventually be added
// again into the keystore, so it is safe to temporarily remove them from the.
// key store.
func (k *keyStore) SyncKeys(resp *clientv3.GetResponse) {
	k.mu.Lock()
	defer k.mu.Unlock()

	if resp.Header.GetRevision() < k.latestRevision {
		return
	}

	k.latestRevision = resp.Header.GetRevision()

	listKeys := make([]string, len(resp.Kvs))
	for i, kv := range resp.Kvs {
		listKeys[i] = string(kv.Key)
	}

	for _, key := range k.keys {
		if !slices.Contains(listKeys, key) {
			listKeys = append(listKeys, key)
		}
	}

	sort.Slice(listKeys, func(i, j int) bool {
		keyNumI := k.getKeyNum(listKeys[i])
		keyNumJ := k.getKeyNum(listKeys[j])
		return keyNumI < keyNumJ
	})

	k.keys = listKeys[:len(k.keys)]

	lastKeyNum := k.getKeyNum(k.keys[len(k.keys)-1])
	k.counter = lastKeyNum + 1
}

func (k *keyStore) getKeyNum(key string) int {
	if len(key) < len(k.keyPrefix) {
		return 0
	}

	numStr := key[len(k.keyPrefix):]
	num, _ := strconv.Atoi(numStr)
	return num
}

func (k *keyStore) replaceKey(index int) {
	if rand.Intn(100) < 90 {
		k.keys[index] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
		k.counter++