<commit_before>/*
Copyright 2016 Mirantis

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 network

import (
	"bytes"
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/containernetworking/cni/pkg/ns"
	cnicurrent "github.com/containernetworking/cni/pkg/types/current"

	"github.com/Mirantis/virtlet/pkg/dhcp"
)

const (
	maxNetTestMembers = 32
	dhcpcdTimeout     = 2
)

type NetTester interface {
	Name() string
	Fg() bool
	Run(readyCh, stopCh chan struct{}) error
}

type NetTestGroup struct {
	wg, fg  sync.WaitGroup
	t       *testing.T
	timeout time.Duration
	stopCh  chan struct{}
	errCh   chan error
	nVacant int
}

func NewNetTestGroup(t *testing.T, timeout time.Duration) *NetTestGroup {
	return &NetTestGroup{
		t:       t,
		timeout: timeout,
		stopCh:  make(chan struct{}),
		errCh:   make(chan error, maxNetTestMembers),
		nVacant: maxNetTestMembers,
	}
}

func (g *NetTestGroup) Add(netNS ns.NetNS, tester NetTester) chan struct{} {
	if g.nVacant == 0 {
		// sending to errCh can possibly block if we add more members
		// than maxNetTestMembers
		g.t.Fatal("can't add more members to the test group")
	}
	doneCh := make(chan struct{})
	g.nVacant--
	readyCh := make(chan struct{})
	g.wg.Add(1)
	if tester.Fg() {
		g.fg.Add(1)
	}
	go func() {
		err := netNS.Do(func(ns.NetNS) (err error) {
			return tester.Run(readyCh, g.stopCh)
		})
		if err != nil {
			g.errCh <- fmt.Errorf("%s: %v", tester.Name(), err)
		}
		if tester.Fg() {
			g.fg.Done()
		}
		g.wg.Done()
		close(doneCh)
	}()
	select {
	case err := <-g.errCh:
		g.t.Fatal(err)
	case <-readyCh:
	}
	return doneCh
}

func (g *NetTestGroup) Stop() {
	if g.stopCh != nil {
		close(g.stopCh)
		g.stopCh = nil
		g.wg.Wait()
	}
}
<commit_msg>Fix network test failure handling<commit_after>/*
Copyright 2016 Mirantis

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 network

import (
	"bytes"
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/containernetworking/cni/pkg/ns"
	cnicurrent "github.com/containernetworking/cni/pkg/types/current"

	"github.com/Mirantis/virtlet/pkg/dhcp"
)

const (
	maxNetTestMembers = 32
	dhcpcdTimeout     = 2
)

type NetTester interface {
	Name() string
	Fg() bool
	Run(readyCh, stopCh chan struct{}) error
}

type NetTestGroup struct {
	wg, fg  sync.WaitGroup
	t       *testing.T
	timeout time.Duration
	stopCh  chan struct{}
	errCh   chan error
	nVacant int
}

func NewNetTestGroup(t *testing.T, timeout time.Duration) *NetTestGroup {
	return &NetTestGroup{
		t:       t,
		timeout: timeout,
		stopCh:  make(chan struct{}),
		errCh:   make(chan error, maxNetTestMembers),
		nVacant: maxNetTestMembers,
	}
}

func (g *NetTestGroup) Add(netNS ns.NetNS, tester NetTester) chan struct{} {
	if g.nVacant == 0 {
		// sending to errCh can possibly block if we add more members
		// than maxNetTestMembers
		g.t.Fatal("can't add more members to the test group")
	}
	doneCh := make(chan struct{})
	g.nVacant--
	readyCh := make(chan struct{})
	g.wg.Add(1)
	if tester.Fg() {
		g.fg.Add(1)
	}
	go func() {
		err := netNS.Do(func(ns.NetNS) (err error) {
			return tester.Run(readyCh, g.stopCh)
		})
		if err != nil {
			g.errCh <- fmt.Errorf("%s: %v", tester.Name(), err)
		}
		if tester.Fg() {
			g.fg.Done()
		}
		g.wg.Done()
		close(doneCh)
	}()
	select {
	case err := <-g.errCh:
		close(g.stopCh)
		g.stopCh = nil
		g.t.Fatal(err)
	case <-readyCh:
	}
	return doneCh
}

func (g *NetTestGroup) Stop() {
	if g.stopCh != nil {
		close(g.stopCh)
		g.stopCh = nil
		g.wg.Wait()
	}
}

{'fraction_non_alphanumeric': 0.12286158631415241, 'fraction_numerical': 0.006220839813374806, 'mean_word_length': 3.5155466399197595, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4710714', 'n_tokens_mistral': 1782, 'n_tokens_neox': 1649, 'n_words': 485}