<commit_before>package native

/*
int StartApp(char *setupTitle, char *appName, char *imageBytes, int imageLen);
void SetLabel(char *cString);
void SetProgress(int value);
char *ValidateBundle(char *bundlePath);
int LaunchBundle(char *bundlePath);
void Quit();
*/
import "C"

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"
	"unsafe"

	"github.com/itchio/itch-setup/bindata"
	"github.com/itchio/itch-setup/cl"
	"github.com/itchio/itch-setup/setup"
	"github.com/itchio/ox/macox"
	"github.com/pkg/errors"
)

type nativeCore struct {
	cli                  cl.CLI
	selfPath             string
	roamingSetupPath     string
	homeApplicationsPath string
}

var globalNc *nativeCore

func NewNativeCore(cli cl.CLI) (NativeCore, error) {
	nc := &nativeCore{
		cli: cli,
	}

	appSupportPath, err := macox.GetApplicationSupportPath()
	if err != nil {
		return nil, err
	}
	nc.roamingSetupPath = filepath.Join(appSupportPath, fmt.Sprintf("%s-setup", cli.AppName))

	log.Printf("Base dir: %s", nc.roamingSetupPath)

	selfPath, err := os.Executable()
	if err != nil {
		return nil, err
	}
	nc.selfPath = selfPath
	log.Printf("Self path: %s", nc.selfPath)

	homePath, err := macox.GetHomeDirectory()
	if err != nil {
		return nil, err
	}
	nc.homeApplicationsPath = filepath.Join(homePath, "Applications")
	log.Printf("Home Applications path: %s", nc.homeApplicationsPath)

	globalNc = nc

	return nc, nil
}

func (nc *nativeCore) Install() error {
	cli := nc.cli
	setupTitle := cli.Localizer.T("setup.window.title", map[string]string{"app_name": cli.AppName})

	// thanks, go-bindata!
	imageData, err := bindata.Asset(fmt.Sprintf("data/installer-%s.png", cli.AppName))
	if err != nil {
		log.Printf("Installer image not found :()")
		return nil
	}

	imageBytes := unsafe.Pointer(&imageData[0])
	imageLen := C.int(len(imageData))
	C.StartApp(C.CString(setupTitle), C.CString(cli.AppName), (*C.char)(imageBytes), imageLen)
	return nil
}

func (nc *nativeCore) Uninstall() error {
	return errors.Errorf("uninstall: stub!")
}

func (nc *nativeCore) Upgrade() error {
	return errors.Errorf("upgrade: stub!")
}

func (nc *nativeCore) Relaunch() error {
	pid := nc.cli.RelaunchPID

	ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
	setup.WaitForProcessToExit(ctx, pid)

	mv, err := nc.newMultiverse()
	if err != nil {
		return err
	}

	if mv.HasReadyPending() {
		err = mv.MakeReadyCurrent()
		if err != nil {
			return err
		}
	}

	nc.tryLaunchCurrent()
	return nil
}

func (nc *nativeCore) ErrorDialog(err error) {
	// TODO: use cocoa for this?
	log.Fatalf("Fatal error: %+v", err)
}

//export StartItchSetup
func StartItchSetup() {
	var installer *setup.Installer
	nc := globalNc
	cli := nc.cli

	if cli.Silent {
		C.SetLabel(C.CString("Silent install mode is not supported on macOS"))
		return
	}

	if cli.PreferLaunch {
		log.Printf("--prefer-launch passed, looking for valid install")
		err := nc.tryLaunchCurrent()
		if err != nil {
			log.Printf("Could not launch current: %v", err)
			log.Printf("Carrying on with install")
		}
	}

	mv, err := nc.newMultiverse()
	if err != nil {
		nc.ErrorDialog(err)
	}

	// TODO: add hook to validate bundle
	installer = setup.NewInstaller(setup.InstallerSettings{
		Localizer: cli.Localizer,
		AppName:   cli.AppName,
		OnError: func(err error) {
			log.Printf("Error: %+v", err)
			C.SetLabel(C.CString(fmt.Sprintf("%+v", err)))
		},
		OnFinish: func(source setup.InstallSource) {
			err := nc.tryLaunchCurrent()
			if err != nil {
				nc.ErrorDialog(err)
			}

			C.Quit()
		},
		OnProgress: func(progress float64) {
			C.SetProgress(C.int(progress * 1000.0))
		},
		OnProgressLabel: func(label string) {
			C.SetLabel(C.CString(label))
		},
	})
	installer.WarmUp()

	installer.Install(mv)
}

func (nc *nativeCore) tryLaunchCurrent() error {
	mv, err := nc.newMultiverse()
	if err != nil {
		return err
	}

	b := mv.GetCurrentVersion()
	if b == nil {
		return errors.Errorf("No valid version of %s found installed", nc.cli.AppName)
	}

	log.Printf("Launching (%s) from (%s)", b.Version, b.Path)
	if C.LaunchBundle(C.CString(b.Path)) == 0 {
		return errors.Errorf("Could not launch (%s)", b.Path)
	}

	log.Printf("Bundle launched successfully, getting out of the way")
	C.Quit()

	// unreachable, but the go compiler doesn't know it
	return nil
}

func (nc *nativeCore) validateBundle(bundlePath string) error {
	log.Printf("Making sure (%s) is signed and valid", bundlePath)

	result := C.ValidateBundle(C.CString(bundlePath))
	if result != nil {
		return errors.Errorf("Bundle (%s) invalid: %s", bundlePath, C.GoString(result))
	}
	return nil
}

func (nc *nativeCore) newMultiverse() (setup.Multiverse, error) {
	return setup.NewMultiverse(&setup.MultiverseParams{
		AppName:         nc.cli.AppName,
		BaseDir:         nc.roamingSetupPath,
		ApplicationsDir: nc.homeApplicationsPath,

		OnValidate: nc.validateBundle,
	})
}
<commit_msg>macOS: Re-use multiverses<commit_after>package native

/*
int StartApp(char *setupTitle, char *appName, char *imageBytes, int imageLen);
void SetLabel(char *cString);
void SetProgress(int value);
char *ValidateBundle(char *bundlePath);
int LaunchBundle(char *bundlePath);
void Quit();
*/
import "C"

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"
	"unsafe"

	"github.com/itchio/itch-setup/bindata"
	"github.com/itchio/itch-setup/cl"
	"github.com/itchio/itch-setup/setup"
	"github.com/itchio/ox/macox"
	"github.com/pkg/errors"
)

type nativeCore struct {
	cli                  cl.CLI
	selfPath             string
	roamingSetupPath     string
	homeApplicationsPath string
}

var globalNc *nativeCore

func NewNativeCore(cli cl.CLI) (NativeCore, error) {
	nc := &nativeCore{
		cli: cli,
	}

	appSupportPath, err := macox.GetApplicationSupportPath()
	if err != nil {
		return nil, err
	}
	nc.roamingSetupPath = filepath.Join(appSupportPath, fmt.Sprintf("%s-setup", cli.AppName))

	log.Printf("Base dir: %s", nc.roamingSetupPath)

	selfPath, err := os.Executable()
	if err != nil {
		return nil, err
	}
	nc.selfPath = selfPath
	log.Printf("Self path: %s", nc.selfPath)

	homePath, err := macox.GetHomeDirectory()
	if err != nil {
		return nil, err
	}
	nc.homeApplicationsPath = filepath.Join(homePath, "Applications")
	log.Printf("Home Applications path: %s", nc.homeApplicationsPath)

	globalNc = nc

	return nc, nil
}

func (nc *nativeCore) Install() error {
	cli := nc.cli
	setupTitle := cli.Localizer.T("setup.window.title", map[string]string{"app_name": cli.AppName})

	// thanks, go-bindata!
	imageData, err := bindata.Asset(fmt.Sprintf("data/installer-%s.png", cli.AppName))
	if err != nil {
		log.Printf("Installer image not found :()")
		return nil
	}

	imageBytes := unsafe.Pointer(&imageData[0])
	imageLen := C.int(len(imageData))
	C.StartApp(C.CString(setupTitle), C.CString(cli.AppName), (*C.char)(imageBytes), imageLen)
	return nil
}

func (nc *nativeCore) Uninstall() error {
	return errors.Errorf("uninstall: stub!")
}

func (nc *nativeCore) Upgrade() error {
	return errors.Errorf("upgrade: stub!")
}

func (nc *nativeCore) Relaunch() error {
	pid := nc.cli.RelaunchPID

	ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
	setup.WaitForProcessToExit(ctx, pid)

	mv, err := nc.newMultiverse()
	if err != nil {
		return err
	}

	if mv.HasReadyPending() {
		err = mv.MakeReadyCurrent()
		if err != nil {
			return err
		}
	}

	nc.tryLaunchCurrent(mv)
	return nil
}

func (nc *nativeCore) ErrorDialog(err error) {
	// TODO: use cocoa for this?
	log.Fatalf("Fatal error: %+v", err)
}

//export StartItchSetup
func StartItchSetup() {
	var installer *setup.Installer
	nc := globalNc
	cli := nc.cli

	mv, err := nc.newMultiverse()
	if err != nil {
		nc.ErrorDialog(err)
	}

	if cli.Silent {
		C.SetLabel(C.CString("Silent install mode is not supported on macOS"))
		return
	}

	if cli.PreferLaunch {
		log.Printf("--prefer-launch passed, looking for valid install")
		err := nc.tryLaunchCurrent(mv)
		if err != nil {
			log.Printf("Could not launch current: %v", err)
			log.Printf("Carrying on with install")
		}
	}

	installer = setup.NewInstaller(setup.InstallerSettings{
		Localizer: cli.Localizer,
		AppName:   cli.AppName,
		OnError: func(err error) {
			log.Printf("Error: %+v", err)
			C.SetLabel(C.CString(fmt.Sprintf("%+v", err)))
		},
		OnFinish: func(source setup.InstallSource) {
			err := nc.tryLaunchCurrent(mv)
			if err != nil {
				nc.ErrorDialog(err)
			}

			C.Quit()
		},
		OnProgress: func(progress float64) {
			C.SetProgress(C.int(progress * 1000.0))
		},
		OnProgressLabel: func(label string) {
			C.SetLabel(C.CString(label))
		},
	})
	installer.WarmUp()

	installer.Install(mv)
}

func (nc *nativeCore) tryLaunchCurrent(mv setup.Multiverse) error {
	b := mv.GetCurrentVersion()
	if b == nil {
		return errors.Errorf("No valid version of %s found installed", nc.cli.AppName)
	}

	log.Printf("Launching (%s) from (%s)", b.Version, b.Path)
	if C.LaunchBundle(C.CString(b.Path)) == 0 {
		return errors.Errorf("Could not launch (%s)", b.Path)
	}

	log.Printf("Bundle launched successfully, getting out of the way")
	C.Quit()

	// unreachable, but the go compiler doesn't know it
	return nil
}

func (nc *nativeCore) validateBundle(bundlePath string) error {
	log.Printf("Making sure (%s) is signed and valid", bundlePath)

	result := C.ValidateBundle(C.CString(bundlePath))
	if result != nil {
		return errors.Errorf("Bundle (%s) invalid: %s", bundlePath, C.GoString(result))
	}
	return nil
}

func (nc *nativeCore) newMultiverse() (setup.Multiverse, error) {
	return setup.NewMultiverse(&setup.MultiverseParams{
		AppName:         nc.cli.AppName,
		BaseDir:         nc.roamingSetupPath,
		ApplicationsDir: nc.homeApplicationsPath,

		OnValidate: nc.validateBundle,
	})
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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 configs_test

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"testing"

	"google.golang.org/protobuf/encoding/prototext"
	"bitbucket.org/creachadair/stringset"
	spb "github.com/google/localtoast/scannerlib/proto/severity_go_proto"
	apb "github.com/google/localtoast/scannerlib/proto/api_go_proto"
	ipb "github.com/google/localtoast/scannerlib/proto/scan_instructions_go_proto"
	sipb "github.com/google/localtoast/scannerlib/proto/scan_instructions_go_proto"
)

const (
	configDefPath     = "defs/"
	reducedConfigPath = "reduced/"
)

var configFileNames = stringset.New(
	"vm_image_scanning.textproto", "container_image_scanning.textproto", "instance_scanning.textproto")

var scanConfigDefs, reducedScanConfigs = readConfigFiles()

func readConfigFiles() (map[string][]byte, map[string][]byte) {
	return readFilesInDir(configDefPath), readFilesInDir(reducedConfigPath)
}

func readFilesInDir(dirPath string) map[string][]byte {
	result := make(map[string][]byte)
	err := filepath.Walk(dirPath,
		func(filePath string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if path.Ext(filePath) != ".textproto" {
				return nil
			}
			content, err := ioutil.ReadFile(filePath)
			if err != nil {
				return err
			}
			result[filePath] = content
			return nil
		})
	if err != nil {
		log.Fatalf("Error reading scan config defs %v\n", err)
	}
	return result
}

// Check if the serialized config uses the new reduced per-OS format.
// The test will skip these so that the migration to the reduced format can
// happen in several steps.
// TODO(b/200859368): Remove the usage of this function and update the tests
// once all configs have been migrated.
func isReducedConfigFile(configBytes []byte) bool {
	config := &apb.PerOsBenchmarkConfig{}
	err := prototext.Unmarshal(configBytes, config)
	return err == nil
}

// Validate behavior across all the configs.
func TestRequiredAttributes(t *testing.T) {
	for filePath, configBytes := range scanConfigDefs {
		config := &apb.ScanConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		for _, b := range config.GetBenchmarkConfigs() {
			noteID := b.GetId()
			note := b.GetComplianceNote()
			if note.GetTitle() == "" {
				t.Errorf(`%s GetTitle(): got "", want non-empty string`, noteID)
			}
			if note.GetDescription() == "" {
				t.Errorf(`%s GetDescription(): got "", want non-empty string`, noteID)
			}
			if note.GetRationale() == "" {
				t.Errorf(`%s GetRationale(): got "", want non-empty string`, noteID)
			}
			if note.GetRemediation() == "" {
				t.Errorf(`%s GetRemediation(): got "", want non-empty string`, noteID)
			}
			if len(note.GetVersion()) == 0 {
				t.Errorf(`%s len(GetVersion()): got 0, want at least 1`, noteID)
			}
			for _, version := range note.GetVersion() {
				if version.GetVersion() == "" {
					t.Errorf(`%s version.GetVersion(): got "", want non-empty string`, noteID)
				}
				if version.GetCpeUri() != "fallback" && version.GetCpeUri() != "cpe:/distribution_independent_linux" && len(strings.Split(version.GetCpeUri(), ":")) != 5 {
					t.Errorf("%s version.getCpeUri(): got %s, want a valid CPE", noteID, version.GetCpeUri())
				}
			}
			if note.GetCisBenchmark().GetProfileLevel() < 1 || note.GetCisBenchmark().GetProfileLevel() > 3 {
				t.Errorf("%s GetCisBenchmark().GetProfilelevel(): got %d, want 1 <= level <= 3", noteID, note.GetCisBenchmark().GetProfileLevel())
			}
			if note.GetCisBenchmark().GetSeverity() == spb.Severity_SEVERITY_UNSPECIFIED {
				t.Errorf("%s GetCisBenchmark().GetSeverity(): got %s, want any specified severity", noteID, note.GetCisBenchmark().GetSeverity())
			}
			scanInstructions := &sipb.BenchmarkScanInstructionDef{}
			if err := prototext.Unmarshal(note.GetScanInstructions(), scanInstructions); err != nil {
				t.Errorf("%s could not parse scan instructions: %v", noteID, err)
			}
		}
	}
}

func TestFilesHaveSupportedName(t *testing.T) {
	for filePath := range reducedScanConfigs {
		fileName := filepath.Base(filePath)
		if !configFileNames.Contains(fileName) {
			t.Errorf("checking file names: got %q, want image_scanning.textproto or instance_scanning.textproto", fileName)
		}
	}
}

func TestScanInstructionsHaveDisplayCommandAndNonComplianceReason(t *testing.T) {
	for filePath, configBytes := range scanConfigDefs {
		config := &apb.ScanConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Fatalf("error reading %s: %v", filePath, err)
		}
		for _, b := range config.GetBenchmarkConfigs() {
			noteID := b.GetId()
			instructionDef := &ipb.BenchmarkScanInstructionDef{}
			if err := prototext.Unmarshal(b.GetComplianceNote().GetScanInstructions(), instructionDef); err != nil {
				t.Errorf("%s could not parse scan instructions: %v", noteID, err)
			}
			var scanInstructions []*ipb.BenchmarkScanInstruction
			if instructionDef.GetGeneric() != nil {
				scanInstructions = []*ipb.BenchmarkScanInstruction{instructionDef.GetGeneric()}
			} else if instructionDef.GetScanTypeSpecific() != nil {
				scanInstructions = []*ipb.BenchmarkScanInstruction{
					instructionDef.GetScanTypeSpecific().InstanceScanning,
					instructionDef.GetScanTypeSpecific().ImageScanning,
				}
			} else {
				t.Fatalf("benchmark %s has invalid instruction def %v", noteID, instructionDef)
			}
			for _, i := range scanInstructions {
				for _, a := range i.GetCheckAlternatives() {
					for _, f := range a.GetFileChecks() {
						if len(f.GetFileDisplayCommand()) > 0 && len(f.GetNonComplianceMsg()) == 0 {
							t.Errorf("check for benchmark %s has a file display command set but no non-compliance message: %v", noteID, f)
						}
					}
				}
			}
		}
	}
}

func TestFallbackBenchmarkDefsHaveExpectedIdFormat(t *testing.T) {
	for filePath, configBytes := range scanConfigDefs {
		if filePath != "fallback.textproto" {
			continue
		}
		if isReducedConfigFile(configBytes) {
			continue
		}
		config := &apb.ScanConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		for _, b := range config.GetBenchmarkConfigs() {
			noteID := b.GetId()
			if !strings.HasSuffix(noteID, "-fallback") {
				t.Errorf("Fallback benchmark ID %q should end with -fallback", noteID)
			}
		}
	}
}

func TestFallbackPerOsBenchmarksHaveExpectedIdFormat(t *testing.T) {
	for filePath, configBytes := range reducedScanConfigs {
		if !strings.Contains(filePath, "/fallback/") {
			continue
		}
		config := &apb.PerOsBenchmarkConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		for _, id := range config.BenchmarkId {
			if !strings.HasSuffix(id, "-fallback") {
				t.Errorf("Fallback benchmark ID %q should end with -fallback", id)
			}
		}
	}
}

func TestProfileLevelOverridesUseExistingIDs(t *testing.T) {
	for filePath, configBytes := range reducedScanConfigs {
		config := &apb.PerOsBenchmarkConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		ids := make(map[string]bool)
		for _, id := range config.BenchmarkId {
			ids[id] = true
		}
		for _, o := range config.ProfileLevelOverride {
			for _, id := range o.BenchmarkId {
				if _, ok := ids[id]; !ok {
					t.Errorf("%s: overridden benchmark ID %q isn't used by the config", filePath, id)
				}
			}
		}
	}
}
<commit_msg>Add a test to ensure that within a single config defs file, a note ID does not have the same definition duplicated to help consolidate definitions.<commit_after>// Copyright 2021 Google LLC
//
// 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 configs_test

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"google.golang.org/protobuf/encoding/prototext"
	"google.golang.org/protobuf/testing/protocmp"
	"bitbucket.org/creachadair/stringset"
	cpb "github.com/google/localtoast/scannerlib/proto/compliance_go_proto"
	spb "github.com/google/localtoast/scannerlib/proto/severity_go_proto"
	apb "github.com/google/localtoast/scannerlib/proto/api_go_proto"
	ipb "github.com/google/localtoast/scannerlib/proto/scan_instructions_go_proto"
	sipb "github.com/google/localtoast/scannerlib/proto/scan_instructions_go_proto"
)

const (
	configDefPath     = "defs/"
	reducedConfigPath = "reduced/"
)

var configFileNames = stringset.New(
	"vm_image_scanning.textproto", "container_image_scanning.textproto", "instance_scanning.textproto")

var scanConfigDefs, reducedScanConfigs = readConfigFiles()

func readConfigFiles() (map[string][]byte, map[string][]byte) {
	return readFilesInDir(configDefPath), readFilesInDir(reducedConfigPath)
}

func readFilesInDir(dirPath string) map[string][]byte {
	result := make(map[string][]byte)
	err := filepath.Walk(dirPath,
		func(filePath string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if path.Ext(filePath) != ".textproto" {
				return nil
			}
			content, err := ioutil.ReadFile(filePath)
			if err != nil {
				return err
			}
			result[filePath] = content
			return nil
		})
	if err != nil {
		log.Fatalf("Error reading scan config defs %v\n", err)
	}
	return result
}

// Check if the serialized config uses the new reduced per-OS format.
// The test will skip these so that the migration to the reduced format can
// happen in several steps.
// TODO(b/200859368): Remove the usage of this function and update the tests
// once all configs have been migrated.
func isReducedConfigFile(configBytes []byte) bool {
	config := &apb.PerOsBenchmarkConfig{}
	err := prototext.Unmarshal(configBytes, config)
	return err == nil
}

// Validate behavior across all the configs.
func TestRequiredAttributes(t *testing.T) {
	for filePath, configBytes := range scanConfigDefs {
		config := &apb.ScanConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		for _, b := range config.GetBenchmarkConfigs() {
			noteID := b.GetId()
			note := b.GetComplianceNote()
			if note.GetTitle() == "" {
				t.Errorf(`%s GetTitle(): got "", want non-empty string`, noteID)
			}
			if note.GetDescription() == "" {
				t.Errorf(`%s GetDescription(): got "", want non-empty string`, noteID)
			}
			if note.GetRationale() == "" {
				t.Errorf(`%s GetRationale(): got "", want non-empty string`, noteID)
			}
			if note.GetRemediation() == "" {
				t.Errorf(`%s GetRemediation(): got "", want non-empty string`, noteID)
			}
			if len(note.GetVersion()) == 0 {
				t.Errorf(`%s len(GetVersion()): got 0, want at least 1`, noteID)
			}
			for _, version := range note.GetVersion() {
				if version.GetVersion() == "" {
					t.Errorf(`%s version.GetVersion(): got "", want non-empty string`, noteID)
				}
				if version.GetCpeUri() != "fallback" && version.GetCpeUri() != "cpe:/distribution_independent_linux" && len(strings.Split(version.GetCpeUri(), ":")) != 5 {
					t.Errorf("%s version.getCpeUri(): got %s, want a valid CPE", noteID, version.GetCpeUri())
				}
			}
			if note.GetCisBenchmark().GetProfileLevel() < 1 || note.GetCisBenchmark().GetProfileLevel() > 3 {
				t.Errorf("%s GetCisBenchmark().GetProfilelevel(): got %d, want 1 <= level <= 3", noteID, note.GetCisBenchmark().GetProfileLevel())
			}
			if note.GetCisBenchmark().GetSeverity() == spb.Severity_SEVERITY_UNSPECIFIED {
				t.Errorf("%s GetCisBenchmark().GetSeverity(): got %s, want any specified severity", noteID, note.GetCisBenchmark().GetSeverity())
			}
			scanInstructions := &sipb.BenchmarkScanInstructionDef{}
			if err := prototext.Unmarshal(note.GetScanInstructions(), scanInstructions); err != nil {
				t.Errorf("%s could not parse scan instructions: %v", noteID, err)
			}
		}
	}
}

func TestFilesHaveSupportedName(t *testing.T) {
	for filePath := range reducedScanConfigs {
		fileName := filepath.Base(filePath)
		if !configFileNames.Contains(fileName) {
			t.Errorf("checking file names: got %q, want image_scanning.textproto or instance_scanning.textproto", fileName)
		}
	}
}

func TestScanInstructionsHaveDisplayCommandAndNonComplianceReason(t *testing.T) {
	for filePath, configBytes := range scanConfigDefs {
		config := &apb.ScanConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Fatalf("error reading %s: %v", filePath, err)
		}
		for _, b := range config.GetBenchmarkConfigs() {
			noteID := b.GetId()
			instructionDef := &ipb.BenchmarkScanInstructionDef{}
			if err := prototext.Unmarshal(b.GetComplianceNote().GetScanInstructions(), instructionDef); err != nil {
				t.Errorf("%s could not parse scan instructions: %v", noteID, err)
			}
			var scanInstructions []*ipb.BenchmarkScanInstruction
			if instructionDef.GetGeneric() != nil {
				scanInstructions = []*ipb.BenchmarkScanInstruction{instructionDef.GetGeneric()}
			} else if instructionDef.GetScanTypeSpecific() != nil {
				scanInstructions = []*ipb.BenchmarkScanInstruction{
					instructionDef.GetScanTypeSpecific().InstanceScanning,
					instructionDef.GetScanTypeSpecific().ImageScanning,
				}
			} else {
				t.Fatalf("benchmark %s has invalid instruction def %v", noteID, instructionDef)
			}
			for _, i := range scanInstructions {
				for _, a := range i.GetCheckAlternatives() {
					for _, f := range a.GetFileChecks() {
						if len(f.GetFileDisplayCommand()) > 0 && len(f.GetNonComplianceMsg()) == 0 {
							t.Errorf("check for benchmark %s has a file display command set but no non-compliance message: %v", noteID, f)
						}
					}
				}
			}
		}
	}
}

func TestFallbackBenchmarkDefsHaveExpectedIdFormat(t *testing.T) {
	for filePath, configBytes := range scanConfigDefs {
		if filePath != "fallback.textproto" {
			continue
		}
		if isReducedConfigFile(configBytes) {
			continue
		}
		config := &apb.ScanConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		for _, b := range config.GetBenchmarkConfigs() {
			noteID := b.GetId()
			if !strings.HasSuffix(noteID, "-fallback") {
				t.Errorf("Fallback benchmark ID %q should end with -fallback", noteID)
			}
		}
	}
}

func TestFallbackPerOsBenchmarksHaveExpectedIdFormat(t *testing.T) {
	for filePath, configBytes := range reducedScanConfigs {
		if !strings.Contains(filePath, "/fallback/") {
			continue
		}
		config := &apb.PerOsBenchmarkConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		for _, id := range config.BenchmarkId {
			if !strings.HasSuffix(id, "-fallback") {
				t.Errorf("Fallback benchmark ID %q should end with -fallback", id)
			}
		}
	}
}

func TestProfileLevelOverridesUseExistingIDs(t *testing.T) {
	for filePath, configBytes := range reducedScanConfigs {
		config := &apb.PerOsBenchmarkConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		ids := make(map[string]bool)
		for _, id := range config.BenchmarkId {
			ids[id] = true
		}
		for _, o := range config.ProfileLevelOverride {
			for _, id := range o.BenchmarkId {
				if _, ok := ids[id]; !ok {
					t.Errorf("%s: overridden benchmark ID %q isn't used by the config", filePath, id)
				}
			}
		}
	}
}

func TestConfigDefContainsUniqueNoteDefinitions(t *testing.T) {
	for filePath, configBytes := range scanConfigDefs {
		noteIdsToConfigs := make(map[string][]*apb.BenchmarkConfig)
		config := &apb.ScanConfig{}
		if err := prototext.Unmarshal(configBytes, config); err != nil {
			t.Errorf("error reading %s: %v", filePath, err)
		}
		for _, benchmarkConfig := range config.GetBenchmarkConfigs() {
			noteID := benchmarkConfig.GetId()

			if configs, present := noteIdsToConfigs[noteID]; present {
				for _, priorBenchmarkConfig := range configs {
					if diff := cmp.Diff(priorBenchmarkConfig, benchmarkConfig, protocmp.Transform(), protocmp.IgnoreFields(&cpb.ComplianceNote{}, "version")); diff == "" {
						t.Errorf("%s: Benchmark ID %q has the exact definition specified multiple times", filePath, noteID)
					}
				}
				noteIdsToConfigs[noteID] = append(configs, benchmarkConfig)
			} else {
				noteIdsToConfigs[noteID] = []*apb.BenchmarkConfig{benchmarkConfig}
			}
		}
	}
}
<|endoftext|>


<commit_before>package swarm

import (
	"testing"
	"time"

	"context"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	ma "github.com/multiformats/go-multiaddr"
)

func streamsSame(a, b inet.Stream) bool {
	sa := a.(*Stream)
	sb := b.(*Stream)
	return sa.Stream() == sb.Stream()
}

func TestNotifications(t *testing.T) {
	ctx := context.Background()
	swarms := makeSwarms(ctx, t, 5)
	defer func() {
		for _, s := range swarms {
			s.Close()
		}
	}()

	timeout := 5 * time.Second

	// signup notifs
	notifiees := make([]*netNotifiee, len(swarms))
	for i, swarm := range swarms {
		n := newNetNotifiee()
		swarm.Notify(n)
		notifiees[i] = n
	}

	connectSwarms(t, ctx, swarms)

	<-time.After(time.Millisecond)
	// should've gotten 5 by now.

	// test everyone got the correct connection opened calls
	for i, s := range swarms {
		n := notifiees[i]
		notifs := make(map[peer.ID][]inet.Conn)
		for j, s2 := range swarms {
			if i == j {
				continue
			}

			// this feels a little sketchy, but its probably okay
			for len(s.ConnectionsToPeer(s2.LocalPeer())) != len(notifs[s2.LocalPeer()]) {
				select {
				case c := <-n.connected:
					nfp := notifs[c.RemotePeer()]
					notifs[c.RemotePeer()] = append(nfp, c)
				case <-time.After(timeout):
					t.Fatal("timeout")
				}
			}
		}

		for p, cons := range notifs {
			expect := s.ConnectionsToPeer(p)
			if len(expect) != len(cons) {
				t.Fatal("got different number of connections")
			}

			for _, c := range cons {
				var found bool
				for _, c2 := range expect {
					if c == c2 {
						found = true
						break
					}
				}

				if !found {
					t.Fatal("connection not found!")
				}
			}
		}
	}

	complement := func(c inet.Conn) (*Swarm, *netNotifiee, *Conn) {
		for i, s := range swarms {
			for _, c2 := range s.Connections() {
				if c.LocalMultiaddr().Equal(c2.RemoteMultiaddr()) &&
					c2.LocalMultiaddr().Equal(c.RemoteMultiaddr()) {
					return s, notifiees[i], c2
				}
			}
		}
		t.Fatal("complementary conn not found", c)
		return nil, nil, nil
	}

	testOCStream := func(n *netNotifiee, s inet.Stream) {
		var s2 inet.Stream
		select {
		case s2 = <-n.openedStream:
			t.Log("got notif for opened stream")
		case <-time.After(timeout):
			t.Fatal("timeout")
		}
		if !streamsSame(s, s2) {
			t.Fatal("got incorrect stream", s.Conn(), s2.Conn())
		}

		select {
		case s2 = <-n.closedStream:
			t.Log("got notif for closed stream")
		case <-time.After(timeout):
			t.Fatal("timeout")
		}
		if !streamsSame(s, s2) {
			t.Fatal("got incorrect stream", s.Conn(), s2.Conn())
		}
	}

	streams := make(chan inet.Stream)
	for _, s := range swarms {
		s.SetStreamHandler(func(s inet.Stream) {
			streams <- s
			s.Close()
		})
	}

	// open a streams in each conn
	for i, s := range swarms {
		for _, c := range s.Connections() {
			_, n2, _ := complement(c)

			st1, err := c.NewStream()
			if err != nil {
				t.Error(err)
			} else {
				st1.Write([]byte("hello"))
				st1.Close()
				testOCStream(notifiees[i], st1)
				st2 := <-streams
				testOCStream(n2, st2)
			}
		}
	}

	// close conns
	for i, s := range swarms {
		n := notifiees[i]
		for _, c := range s.Connections() {
			_, n2, c2 := complement(c)
			c.Close()
			c2.Close()

			var c3, c4 inet.Conn
			select {
			case c3 = <-n.disconnected:
			case <-time.After(timeout):
				t.Fatal("timeout")
			}
			if c != c3 {
				t.Fatal("got incorrect conn", c, c3)
			}

			select {
			case c4 = <-n2.disconnected:
			case <-time.After(timeout):
				t.Fatal("timeout")
			}
			if c2 != c4 {
				t.Fatal("got incorrect conn", c, c2)
			}
		}
	}
}

type netNotifiee struct {
	listen       chan ma.Multiaddr
	listenClose  chan ma.Multiaddr
	connected    chan inet.Conn
	disconnected chan inet.Conn
	openedStream chan inet.Stream
	closedStream chan inet.Stream
}

func newNetNotifiee() *netNotifiee {
	return &netNotifiee{
		listen:       make(chan ma.Multiaddr),
		listenClose:  make(chan ma.Multiaddr),
		connected:    make(chan inet.Conn),
		disconnected: make(chan inet.Conn),
		openedStream: make(chan inet.Stream),
		closedStream: make(chan inet.Stream),
	}
}

func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr) {
	nn.listen <- a
}
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {
	nn.listenClose <- a
}
func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
	nn.connected <- v
}
func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
	nn.disconnected <- v
}
func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {
	nn.openedStream <- v
}
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {
	nn.closedStream <- v
}
<commit_msg>update deps<commit_after>package swarm

import (
	"testing"
	"time"

	"context"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	ma "github.com/multiformats/go-multiaddr"
)

func streamsSame(a, b inet.Stream) bool {
	sa := a.(*Stream)
	sb := b.(*Stream)
	return sa.Stream() == sb.Stream()
}

func TestNotifications(t *testing.T) {
	const swarmSize = 5

	ctx := context.Background()
	swarms := makeSwarms(ctx, t, swarmSize)
	defer func() {
		for _, s := range swarms {
			s.Close()
		}
	}()

	timeout := 5 * time.Second

	// signup notifs
	notifiees := make([]*netNotifiee, len(swarms))
	for i, swarm := range swarms {
		n := newNetNotifiee(swarmSize)
		swarm.Notify(n)
		notifiees[i] = n
	}

	connectSwarms(t, ctx, swarms)

	<-time.After(time.Millisecond)
	// should've gotten 5 by now.

	// test everyone got the correct connection opened calls
	for i, s := range swarms {
		n := notifiees[i]
		notifs := make(map[peer.ID][]inet.Conn)
		for j, s2 := range swarms {
			if i == j {
				continue
			}

			// this feels a little sketchy, but its probably okay
			for len(s.ConnectionsToPeer(s2.LocalPeer())) != len(notifs[s2.LocalPeer()]) {
				select {
				case c := <-n.connected:
					nfp := notifs[c.RemotePeer()]
					notifs[c.RemotePeer()] = append(nfp, c)
				case <-time.After(timeout):
					t.Fatal("timeout")
				}
			}
		}

		for p, cons := range notifs {
			expect := s.ConnectionsToPeer(p)
			if len(expect) != len(cons) {
				t.Fatal("got different number of connections")
			}

			for _, c := range cons {
				var found bool
				for _, c2 := range expect {
					if c == c2 {
						found = true
						break
					}
				}

				if !found {
					t.Fatal("connection not found!")
				}
			}
		}
	}

	complement := func(c inet.Conn) (*Swarm, *netNotifiee, *Conn) {
		for i, s := range swarms {
			for _, c2 := range s.Connections() {
				if c.LocalMultiaddr().Equal(c2.RemoteMultiaddr()) &&
					c2.LocalMultiaddr().Equal(c.RemoteMultiaddr()) {
					return s, notifiees[i], c2
				}
			}
		}
		t.Fatal("complementary conn not found", c)
		return nil, nil, nil
	}

	testOCStream := func(n *netNotifiee, s inet.Stream) {
		var s2 inet.Stream
		select {
		case s2 = <-n.openedStream:
			t.Log("got notif for opened stream")
		case <-time.After(timeout):
			t.Fatal("timeout")
		}
		if !streamsSame(s, s2) {
			t.Fatal("got incorrect stream", s.Conn(), s2.Conn())
		}

		select {
		case s2 = <-n.closedStream:
			t.Log("got notif for closed stream")
		case <-time.After(timeout):
			t.Fatal("timeout")
		}
		if !streamsSame(s, s2) {
			t.Fatal("got incorrect stream", s.Conn(), s2.Conn())
		}
	}

	streams := make(chan inet.Stream)
	for _, s := range swarms {
		s.SetStreamHandler(func(s inet.Stream) {
			streams <- s
			s.Close()
		})
	}

	// open a streams in each conn
	for i, s := range swarms {
		for _, c := range s.Connections() {
			_, n2, _ := complement(c)

			st1, err := c.NewStream()
			if err != nil {
				t.Error(err)
			} else {
				st1.Write([]byte("hello"))
				st1.Close()
				testOCStream(notifiees[i], st1)
				st2 := <-streams
				testOCStream(n2, st2)
			}
		}
	}

	// close conns
	for i, s := range swarms {
		n := notifiees[i]
		for _, c := range s.Connections() {
			_, n2, c2 := complement(c)
			c.Close()
			c2.Close()

			var c3, c4 inet.Conn
			select {
			case c3 = <-n.disconnected:
			case <-time.After(timeout):
				t.Fatal("timeout")
			}
			if c != c3 {
				t.Fatal("got incorrect conn", c, c3)
			}

			select {
			case c4 = <-n2.disconnected:
			case <-time.After(timeout):
				t.Fatal("timeout")
			}
			if c2 != c4 {
				t.Fatal("got incorrect conn", c, c2)
			}
		}
	}
}

type netNotifiee struct {
	listen       chan ma.Multiaddr
	listenClose  chan ma.Multiaddr
	connected    chan inet.Conn
	disconnected chan inet.Conn
	openedStream chan inet.Stream
	closedStream chan inet.Stream
}

func newNetNotifiee(buffer int) *netNotifiee {
	return &netNotifiee{
		listen:       make(chan ma.Multiaddr, buffer),
		listenClose:  make(chan ma.Multiaddr, buffer),
		connected:    make(chan inet.Conn, buffer),
		disconnected: make(chan inet.Conn, buffer),
		openedStream: make(chan inet.Stream, buffer),
		closedStream: make(chan inet.Stream, buffer),
	}
}

func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr) {
	nn.listen <- a
}
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {
	nn.listenClose <- a
}
func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
	nn.connected <- v
}
func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
	nn.disconnected <- v
}
func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {
	nn.openedStream <- v
}
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {
	nn.closedStream <- v
}
<|endoftext|>


<commit_before>
package api
/*
import (
	"testing"

	"gopkg.in/check.v1"
)

func Test(t *testing.T) {
	check.TestingT(t)
}

type S struct{}

var _ = check.Suite(&S{})

func (s *S) TestCreateNewRPCClient(c *check.C) {
	_, error := NewRPCClient("http://localhost:2633/RPC2", "oneadmin", "RaifZuewjoc4")
	c.Assert(error, check.IsNil)
}

func (s *S) TestRPCCall(c *check.C) {
	client, clientErr := NewRPCClient("http://localhost:2633/RPC2", "oneadmin", "RaifZuewjoc4")
	args := []interface{}{client.Key, -2, 3, 3}
	_, callErr := client.Call(client.RPCClient, "one.templatepool.info", args)
	c.Assert(clientErr, check.IsNil)
	c.Assert(callErr, check.IsNil)

}
*/
<commit_msg>remove commandline<commit_after>
package api

import (
	"testing"

	"gopkg.in/check.v1"
)

func Test(t *testing.T) {
	check.TestingT(t)
}

type S struct{}

var _ = check.Suite(&S{})

func (s *S) TestCreateNewRPCClient(c *check.C) {
	_, error := NewRPCClient("http://localhost:2633/RPC2", "oneadmin", "RaifZuewjoc4")
	c.Assert(error, check.IsNil)
}

func (s *S) TestRPCCall(c *check.C) {
	client, clientErr := NewRPCClient("http://localhost:2633/RPC2", "oneadmin", "RaifZuewjoc4")
	args := []interface{}{client.Key, -2, 3, 3}
	_, callErr := client.Call(client.RPCClient, "one.templatepool.info", args)
	c.Assert(clientErr, check.IsNil)
	c.Assert(callErr, check.IsNil)

}
<|endoftext|>


<commit_before>/*
word2vec-client is a tool which uses word2vec.Client to look up similarities (using an external server).
*/
package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/sajari/word2vec"
)

var addr string
var addListA, subListA string
var addListB, subListB string

func init() {
	flag.StringVar(&addr, "addr", "", "path to binary model data")
	flag.StringVar(&addListA, "addA", "", "comma separated list of model words to add to the target vector A")
	flag.StringVar(&subListA, "subA", "", "comma separated list of model words to subtract from the target vector A")
	flag.StringVar(&addListB, "addB", "", "comma separated list of model words to add to the target vector A")
	flag.StringVar(&subListB, "subB", "", "comma separated list of model words to subtract from the target vector A")
}

func makeExpr(addList, subList string) (word2vec.Expr, error) {
	if addList == "" && subList == "" {
		return word2vec.Expr{}, fmt.Errorf("must specify 'add' and/or 'sub' component for each target vector; see -h for more details")
	}

	result := word2vec.Expr{}
	if addList != "" {
		result.Add = strings.Split(addList, ",")
	}
	if subList != "" {
		result.Sub = strings.Split(subList, ",")
	}

	return result, nil
}

func main() {
	flag.Parse()

	if addr == "" {
		fmt.Println("must specify -addr; see -h for more details")
		os.Exit(1)
	}

	c := word2vec.Client{Addr: addr}

	exprA, err := makeExpr(addListA, subListA)
	if err != nil {
		fmt.Printf("error creating target vector for 'A': %v\n", err)
		os.Exit(1)
	}

	exprB, err := makeExpr(addListB, subListB)
	if err != nil {
		fmt.Printf("error creating target vector for 'A': %v\n", err)
		os.Exit(1)
	}

	start := time.Now()
	v, err := c.Sim(exprA, exprB)
	totalTime := time.Since(start)
	if err != nil {
		fmt.Printf("error looking up similarity: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Similarity: %v (took: %v)\n", v, totalTime)
}
<commit_msg>Tidied up error messages and code ordering.<commit_after>/*
word2vec-client is a tool which uses word2vec.Client to look up similarities (using an external server).
*/
package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/sajari/word2vec"
)

var addr string
var addListA, subListA string
var addListB, subListB string

func init() {
	flag.StringVar(&addr, "addr", "", "path to binary model data")
	flag.StringVar(&addListA, "addA", "", "comma separated list of model words to add to the target vector A")
	flag.StringVar(&subListA, "subA", "", "comma separated list of model words to subtract from the target vector A")
	flag.StringVar(&addListB, "addB", "", "comma separated list of model words to add to the target vector B")
	flag.StringVar(&subListB, "subB", "", "comma separated list of model words to subtract from the target vector B")
}

func makeExpr(addList, subList string) (word2vec.Expr, error) {
	if addList == "" && subList == "" {
		return word2vec.Expr{}, fmt.Errorf("must specify 'add' and/or 'sub' component for each target vector; see -h for more details")
	}

	result := word2vec.Expr{}
	if addList != "" {
		result.Add = strings.Split(addList, ",")
	}
	if subList != "" {
		result.Sub = strings.Split(subList, ",")
	}

	return result, nil
}

func main() {
	flag.Parse()

	if addr == "" {
		fmt.Println("must specify -addr; see -h for more details")
		os.Exit(1)
	}

	exprA, err := makeExpr(addListA, subListA)
	if err != nil {
		fmt.Printf("error creating target vector for 'A': %v\n", err)
		os.Exit(1)
	}

	exprB, err := makeExpr(addListB, subListB)
	if err != nil {
		fmt.Printf("error creating target vector for 'B': %v\n", err)
		os.Exit(1)
	}

	c := word2vec.Client{Addr: addr}

	start := time.Now()
	v, err := c.Sim(exprA, exprB)
	totalTime := time.Since(start)
	if err != nil {
		fmt.Printf("error looking up similarity: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Similarity: %v (took: %v)\n", v, totalTime)
}
<|endoftext|>


<commit_before>package apidiff

import (
	"bufio"
	"fmt"
	"go/types"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"golang.org/x/tools/go/packages"
)

func TestChanges(t *testing.T) {
	dir, err := os.MkdirTemp("", "apidiff_test")
	if err != nil {
		t.Fatal(err)
	}
	dir = filepath.Join(dir, "go")
	wanti, wantc := splitIntoPackages(t, dir)
	defer os.RemoveAll(dir)
	sort.Strings(wanti)
	sort.Strings(wantc)

	oldpkg, err := load(t, "apidiff/old", dir)
	if err != nil {
		t.Fatal(err)
	}
	newpkg, err := load(t, "apidiff/new", dir)
	if err != nil {
		t.Fatal(err)
	}

	report := Changes(oldpkg.Types, newpkg.Types)

	got := report.messages(false)
	if diff := cmp.Diff(wanti, got); diff != "" {
		t.Errorf("incompatibles: mismatch (-want, +got)\n%s", diff)
	}
	got = report.messages(true)
	if diff := cmp.Diff(wantc, got); diff != "" {
		t.Errorf("compatibles: mismatch (-want, +got)\n%s", diff)
	}
}

func splitIntoPackages(t *testing.T, dir string) (incompatibles, compatibles []string) {
	// Read the input file line by line.
	// Write a line into the old or new package,
	// dependent on comments.
	// Also collect expected messages.
	f, err := os.Open("testdata/tests.go")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	if err := os.MkdirAll(filepath.Join(dir, "src", "apidiff"), 0700); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(dir, "src", "apidiff", "go.mod"), []byte("module apidiff\n"), 0666); err != nil {
		t.Fatal(err)
	}

	oldd := filepath.Join(dir, "src/apidiff/old")
	newd := filepath.Join(dir, "src/apidiff/new")
	if err := os.MkdirAll(oldd, 0700); err != nil {
		t.Fatal(err)
	}
	if err := os.Mkdir(newd, 0700); err != nil && !os.IsExist(err) {
		t.Fatal(err)
	}

	oldf, err := os.Create(filepath.Join(oldd, "old.go"))
	if err != nil {
		t.Fatal(err)
	}
	newf, err := os.Create(filepath.Join(newd, "new.go"))
	if err != nil {
		t.Fatal(err)
	}

	wl := func(f *os.File, line string) {
		if _, err := fmt.Fprintln(f, line); err != nil {
			t.Fatal(err)
		}
	}
	writeBoth := func(line string) { wl(oldf, line); wl(newf, line) }
	writeln := writeBoth
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		tl := strings.TrimSpace(line)
		switch {
		case tl == "// old":
			writeln = func(line string) { wl(oldf, line) }
		case tl == "// new":
			writeln = func(line string) { wl(newf, line) }
		case tl == "// both":
			writeln = writeBoth
		case strings.HasPrefix(tl, "// i "):
			incompatibles = append(incompatibles, strings.TrimSpace(tl[4:]))
		case strings.HasPrefix(tl, "// c "):
			compatibles = append(compatibles, strings.TrimSpace(tl[4:]))
		default:
			writeln(line)
		}
	}
	if s.Err() != nil {
		t.Fatal(s.Err())
	}
	return
}

func load(t *testing.T, importPath, goPath string) (*packages.Package, error) {
	needsGoPackages(t)

	cfg := &packages.Config{
		Mode: packages.LoadTypes,
	}
	if goPath != "" {
		cfg.Env = append(os.Environ(), "GOPATH="+goPath)
		cfg.Dir = filepath.Join(goPath, "src", filepath.FromSlash(importPath))
	}
	pkgs, err := packages.Load(cfg, importPath)
	if err != nil {
		return nil, err
	}
	if len(pkgs[0].Errors) > 0 {
		return nil, pkgs[0].Errors[0]
	}
	return pkgs[0], nil
}

func TestExportedFields(t *testing.T) {
	pkg, err := load(t, "golang.org/x/exp/apidiff/testdata/exported_fields", "")
	if err != nil {
		t.Fatal(err)
	}
	typeof := func(name string) types.Type {
		return pkg.Types.Scope().Lookup(name).Type()
	}

	s := typeof("S")
	su := s.(*types.Named).Underlying().(*types.Struct)

	ef := exportedSelectableFields(su)
	wants := []struct {
		name string
		typ  types.Type
	}{
		{"A1", typeof("A1")},
		{"D", types.Typ[types.Bool]},
		{"E", types.Typ[types.Int]},
		{"F", typeof("F")},
		{"S", types.NewPointer(s)},
	}

	if got, want := len(ef), len(wants); got != want {
		t.Errorf("got %d fields, want %d\n%+v", got, want, ef)
	}
	for _, w := range wants {
		if got := ef[w.name]; got != nil && !types.Identical(got.Type(), w.typ) {
			t.Errorf("%s: got %v, want %v", w.name, got.Type(), w.typ)
		}
	}
}

// needsGoPackages skips t if the go/packages driver (or 'go' tool) implied by
// the current process environment is not present in the path.
//
// Copied and adapted from golang.org/x/tools/internal/testenv.
func needsGoPackages(t *testing.T) {
	t.Helper()

	tool := os.Getenv("GOPACKAGESDRIVER")
	switch tool {
	case "off":
		// "off" forces go/packages to use the go command.
		tool = "go"
	case "":
		if _, err := exec.LookPath("gopackagesdriver"); err == nil {
			tool = "gopackagesdriver"
		} else {
			tool = "go"
		}
	}

	needsTool(t, tool)
}

// needsTool skips t if the named tool is not present in the path.
//
// Copied and adapted from golang.org/x/tools/internal/testenv.
func needsTool(t *testing.T, tool string) {
	_, err := exec.LookPath(tool)
	if err == nil {
		return
	}

	t.Helper()
	if allowMissingTool(tool) {
		t.Skipf("skipping because %s tool not available: %v", tool, err)
	} else {
		t.Fatalf("%s tool not available: %v", tool, err)
	}
}

func allowMissingTool(tool string) bool {
	if runtime.GOOS == "android" {
		// Android builds generally run tests on a separate machine from the build,
		// so don't expect any external tools to be available.
		return true
	}

	if tool == "go" && os.Getenv("GO_BUILDER_NAME") == "illumos-amd64-joyent" {
		// Work around a misconfigured builder (see https://golang.org/issue/33950).
		return true
	}

	// If a developer is actively working on this test, we expect them to have all
	// of its dependencies installed. However, if it's just a dependency of some
	// other module (for example, being run via 'go test all'), we should be more
	// tolerant of unusual environments.
	return !packageMainIsDevel()
}

// packageMainIsDevel reports whether the module containing package main
// is a development version (if module information is available).
//
// Builds in GOPATH mode and builds that lack module information are assumed to
// be development versions.
var packageMainIsDevel = func() bool { return true }
<commit_msg>apidiff: skip TestChanges<commit_after>package apidiff

import (
	"bufio"
	"fmt"
	"go/types"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"golang.org/x/tools/go/packages"
)

func TestChanges(t *testing.T) {
	t.Skip("broken by go1.20-pre3 compiler: see https://go.dev/issue/56162")
	dir, err := os.MkdirTemp("", "apidiff_test")
	if err != nil {
		t.Fatal(err)
	}
	dir = filepath.Join(dir, "go")
	wanti, wantc := splitIntoPackages(t, dir)
	defer os.RemoveAll(dir)
	sort.Strings(wanti)
	sort.Strings(wantc)

	oldpkg, err := load(t, "apidiff/old", dir)
	if err != nil {
		t.Fatal(err)
	}
	newpkg, err := load(t, "apidiff/new", dir)
	if err != nil {
		t.Fatal(err)
	}

	report := Changes(oldpkg.Types, newpkg.Types)

	got := report.messages(false)
	if diff := cmp.Diff(wanti, got); diff != "" {
		t.Errorf("incompatibles: mismatch (-want, +got)\n%s", diff)
	}
	got = report.messages(true)
	if diff := cmp.Diff(wantc, got); diff != "" {
		t.Errorf("compatibles: mismatch (-want, +got)\n%s", diff)
	}
}

func splitIntoPackages(t *testing.T, dir string) (incompatibles, compatibles []string) {
	// Read the input file line by line.
	// Write a line into the old or new package,
	// dependent on comments.
	// Also collect expected messages.
	f, err := os.Open("testdata/tests.go")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	if err := os.MkdirAll(filepath.Join(dir, "src", "apidiff"), 0700); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(dir, "src", "apidiff", "go.mod"), []byte("module apidiff\n"), 0666); err != nil {
		t.Fatal(err)
	}

	oldd := filepath.Join(dir, "src/apidiff/old")
	newd := filepath.Join(dir, "src/apidiff/new")
	if err := os.MkdirAll(oldd, 0700); err != nil {
		t.Fatal(err)
	}
	if err := os.Mkdir(newd, 0700); err != nil && !os.IsExist(err) {
		t.Fatal(err)
	}

	oldf, err := os.Create(filepath.Join(oldd, "old.go"))
	if err != nil {
		t.Fatal(err)
	}
	newf, err := os.Create(filepath.Join(newd, "new.go"))
	if err != nil {
		t.Fatal(err)
	}

	wl := func(f *os.File, line string) {
		if _, err := fmt.Fprintln(f, line); err != nil {
			t.Fatal(err)
		}
	}
	writeBoth := func(line string) { wl(oldf, line); wl(newf, line) }
	writeln := writeBoth
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		tl := strings.TrimSpace(line)
		switch {
		case tl == "// old":
			writeln = func(line string) { wl(oldf, line) }
		case tl == "// new":
			writeln = func(line string) { wl(newf, line) }
		case tl == "// both":
			writeln = writeBoth
		case strings.HasPrefix(tl, "// i "):
			incompatibles = append(incompatibles, strings.TrimSpace(tl[4:]))
		case strings.HasPrefix(tl, "// c "):
			compatibles = append(compatibles, strings.TrimSpace(tl[4:]))
		default:
			writeln(line)
		}
	}
	if s.Err() != nil {
		t.Fatal(s.Err())
	}
	return
}

func load(t *testing.T, importPath, goPath string) (*packages.Package, error) {
	needsGoPackages(t)

	cfg := &packages.Config{
		Mode: packages.LoadTypes,
	}
	if goPath != "" {
		cfg.Env = append(os.Environ(), "GOPATH="+goPath)
		cfg.Dir = filepath.Join(goPath, "src", filepath.FromSlash(importPath))
	}
	pkgs, err := packages.Load(cfg, importPath)
	if err != nil {
		return nil, err
	}
	if len(pkgs[0].Errors) > 0 {
		return nil, pkgs[0].Errors[0]
	}
	return pkgs[0], nil
}

func TestExportedFields(t *testing.T) {
	pkg, err := load(t, "golang.org/x/exp/apidiff/testdata/exported_fields", "")
	if err != nil {
		t.Fatal(err)
	}
	typeof := func(name string) types.Type {
		return pkg.Types.Scope().Lookup(name).Type()
	}

	s := typeof("S")
	su := s.(*types.Named).Underlying().(*types.Struct)

	ef := exportedSelectableFields(su)
	wants := []struct {
		name string
		typ  types.Type
	}{
		{"A1", typeof("A1")},
		{"D", types.Typ[types.Bool]},
		{"E", types.Typ[types.Int]},
		{"F", typeof("F")},
		{"S", types.NewPointer(s)},
	}

	if got, want := len(ef), len(wants); got != want {
		t.Errorf("got %d fields, want %d\n%+v", got, want, ef)
	}
	for _, w := range wants {
		if got := ef[w.name]; got != nil && !types.Identical(got.Type(), w.typ) {
			t.Errorf("%s: got %v, want %v", w.name, got.Type(), w.typ)
		}
	}
}

// needsGoPackages skips t if the go/packages driver (or 'go' tool) implied by
// the current process environment is not present in the path.
//
// Copied and adapted from golang.org/x/tools/internal/testenv.
func needsGoPackages(t *testing.T) {
	t.Helper()

	tool := os.Getenv("GOPACKAGESDRIVER")
	switch tool {
	case "off":
		// "off" forces go/packages to use the go command.
		tool = "go"
	case "":
		if _, err := exec.LookPath("gopackagesdriver"); err == nil {
			tool = "gopackagesdriver"
		} else {
			tool = "go"
		}
	}

	needsTool(t, tool)
}

// needsTool skips t if the named tool is not present in the path.
//
// Copied and adapted from golang.org/x/tools/internal/testenv.
func needsTool(t *testing.T, tool string) {
	_, err := exec.LookPath(tool)
	if err == nil {
		return
	}

	t.Helper()
	if allowMissingTool(tool) {
		t.Skipf("skipping because %s tool not available: %v", tool, err)
	} else {
		t.Fatalf("%s tool not available: %v", tool, err)
	}
}

func allowMissingTool(tool string) bool {
	if runtime.GOOS == "android" {
		// Android builds generally run tests on a separate machine from the build,
		// so don't expect any external tools to be available.
		return true
	}

	if tool == "go" && os.Getenv("GO_BUILDER_NAME") == "illumos-amd64-joyent" {
		// Work around a misconfigured builder (see https://golang.org/issue/33950).
		return true
	}

	// If a developer is actively working on this test, we expect them to have all
	// of its dependencies installed. However, if it's just a dependency of some
	// other module (for example, being run via 'go test all'), we should be more
	// tolerant of unusual environments.
	return !packageMainIsDevel()
}

// packageMainIsDevel reports whether the module containing package main
// is a development version (if module information is available).
//
// Builds in GOPATH mode and builds that lack module information are assumed to
// be development versions.
var packageMainIsDevel = func() bool { return true }
<|endoftext|>


<commit_before>package transmit

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"hash/adler32"
	"io"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"sync/atomic"

	"github.com/midbel/uuid"
)

var ErrClosed = errors.New("closed")

var (
	ErrCorrupted = errors.New("packet corrupted")
	ErrUnknownId = errors.New("unknown packet id")
)

const (
	Size    = 26
	Padding = 512
)

const (
	Bind uint16 = iota
	Accept
	Reject
	Copy
	Abort
	Done
)

type Route struct {
	Id   string `json:"id"`
	Addr string `json:"addr"`
	Eth  string `json:"ifi"`
}

func Subscribe(a, n string) (net.Conn, error) {
	addr, err := net.ResolveUDPAddr("udp", a)
	if err != nil {
		return nil, err
	}
	i, err := net.InterfaceByName(n)
	if err != nil && len(n) > 0 {
		return nil, err
	}
	c, err := net.ListenMulticastUDP("udp", i, addr)
	if err != nil {
		return nil, err
	}
	return &subscriber{c}, nil
}

func Dispatch(a string) (net.Conn, error) {
	c, err := net.Dial("udp", a)
	if err != nil {
		return nil, err
	}
	return &subscriber{c}, nil
}

func Forward(a, s string) (net.Conn, error) {
	c, err := net.Dial("tcp", a)
	if err != nil {
		return nil, err
	}
	id, _ := uuid.UUID5(uuid.URL, []byte(s))

	f := &forwarder{
		Conn:    c,
		id:      id.Bytes(),
		padding: Padding,
		reader:  bufio.NewReaderSize(rand.Reader, 4096),
	}
	if _, err := f.Write(f.id); err != nil {
		f.Close()
		return nil, err
	}
	return f, nil
}

type Router struct {
	net.Listener

	routes map[string]*pool
}

func (r *Router) Accept() (net.Conn, net.Conn, error) {
	c, err := r.Listener.Accept()
	if err != nil {
		return nil, nil, err
	}
	id := make([]byte, Size+uuid.Size)
	if _, err := io.ReadFull(c, id); err != nil {
		c.Close()
		return nil, nil, err
	} else {
		id = id[:uuid.Size]
	}
	p, ok := r.routes[string(id)]
	if !ok {
		c.Close()
		return nil, nil, ErrUnknownId
	}
	io.CopyN(ioutil.Discard, c, Padding)
	w, err := p.Acquire()
	if err != nil {
		return nil, nil, err
	}
	return &forwarder{Conn: c, id: id}, w, nil
}

func (r *Router) Close() error {
	err := r.Listener.Close()
	for _, p := range r.routes {
		if e := p.Close(); err == nil && e != nil {
			err = e
		}
	}
	return err
}

func NewRouter(a string, rs []Route) (*Router, error) {
	l, err := net.Listen("tcp", a)
	if err != nil {
		return nil, err
	}
	gs := make(map[string]*pool)
	for _, r := range rs {
		id, _ := uuid.UUID5(uuid.URL, []byte(r.Id))
		p := &pool{
			c: make(chan net.Conn, 5),
			a: r.Addr,
		}
		gs[string(id.Bytes())] = p
	}
	return &Router{Listener: l, routes: gs}, nil
}

type subscriber struct {
	net.Conn
}

func (s *subscriber) Read(b []byte) (int, error) {
	d := make([]byte, len(b))
	r, err := s.Conn.Read(d)
	if err != nil && r == 0 {
		return r, err
	} else {
		d = d[:r]
		go log.Printf("%d bytes read from %s (%x)", r, s.LocalAddr(), md5.Sum(d))
	}
	sum := make([]byte, 4)
	binary.BigEndian.PutUint32(sum, adler32.Checksum(d))
	d = append(d, sum...)

	return copy(b, d), err
}

func (s *subscriber) Write(b []byte) (int, error) {
	d, sum := b[:len(b)-adler32.Size], b[len(b)-adler32.Size:]
	if a := adler32.Checksum(d); a != binary.BigEndian.Uint32(sum) {
		return len(b), ErrCorrupted
	}
	w, err := s.Conn.Write(d)
	if err == nil {
		go log.Printf("%d (%d) bytes written to %s (%x)", w, len(d), s.RemoteAddr(), md5.Sum(d))
	}
	return len(b), err
}

type forwarder struct {
	net.Conn

	id []byte

	sequence uint32
	padding  uint16

	reader io.Reader
}

func (f *forwarder) Read(b []byte) (int, error) {
	d, err := readFrom(f.Conn, f.id)
	go log.Printf("%d bytes received from %s", len(d), f.RemoteAddr())
	return copy(b, d), err
}

func (f *forwarder) Write(b []byte) (int, error) {
	s := atomic.AddUint32(&f.sequence, 1)
	err := writeTo(f.Conn, s, f.id, b)
	go log.Printf("%d bytes sent to %s", len(b), f.RemoteAddr())
	return len(b), err
}

type block struct {
	Id       [uuid.Size]byte
	Length   uint16
	Sequence uint32
	N        uint8
	C        uint8
	R        uint16
}

func readFrom(r io.Reader, i []byte) ([]byte, error) {
	w := new(bytes.Buffer)

	for {
		v := new(block)
		if err := binary.Read(r, binary.BigEndian, v); err != nil {
			return nil, err
		}
		if _, err := io.CopyN(w, r, int64(v.R)); err != nil {
			return nil, err
		}
		if v.R < Padding {
			io.CopyN(ioutil.Discard, r, Padding)
		}
		if !bytes.Equal(v.Id[:], i) {
			return nil, ErrUnknownId
		}
		if v.N == v.C {
			break
		}
	}
	return w.Bytes(), nil
}

func writeTo(w io.Writer, s uint32, id, b []byte) error {
	const size = 1024
	r := bytes.NewReader(b)

	n := r.Size() / size
	if m := r.Size() % size; n > 0 && m != 0 {
		n += 1
	}
	var j uint8
	for r.Len() > 0 {
		buf := new(bytes.Buffer)

		buf.Write(id)
		binary.Write(buf, binary.BigEndian, s)
		binary.Write(buf, binary.BigEndian, uint16(len(b)))
		binary.Write(buf, binary.BigEndian, uint8(n))
		binary.Write(buf, binary.BigEndian, uint8(j))

		count := size
		if r.Len() < size {
			count = r.Len()
		}
		binary.Write(buf, binary.BigEndian, uint16(count))

		io.CopyN(buf, r, int64(count))
		if count < Padding {
			io.CopyN(buf, rand.Reader, Padding)
		}
		if _, err := io.Copy(w, buf); err != nil {
			return err
		}
		j++
	}
	return nil
}

type pool struct {
	a string
	c chan net.Conn

	closed bool
	mu     sync.Mutex
}

func (p *pool) Acquire() (net.Conn, error) {
	select {
	case c, ok := <-p.c:
		if !ok {
			return nil, ErrClosed
		}
		return c, nil
	default:
		return Dispatch(p.a)
	}
}

func (p *pool) Release(c net.Conn) error {
	if p.closed {
		return ErrClosed
	}
	select {
	case p.c <- c:
		return nil
	default:
		return c.Close()
	}
}

func (p *pool) Close() error {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.closed {
		return ErrClosed
	}
	var err error
	for c := range p.c {
		if e := c.Close(); err == nil && e != nil {
			err = e
		}
	}
	close(p.c)
	return err
}
<commit_msg>don't add 1 under certain conditions<commit_after>package transmit

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"hash/adler32"
	"io"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"sync/atomic"

	"github.com/midbel/uuid"
)

var ErrClosed = errors.New("closed")

var (
	ErrCorrupted = errors.New("packet corrupted")
	ErrUnknownId = errors.New("unknown packet id")
)

const (
	Size    = 26
	Padding = 512
)

const (
	Bind uint16 = iota
	Accept
	Reject
	Copy
	Abort
	Done
)

type Route struct {
	Id   string `json:"id"`
	Addr string `json:"addr"`
	Eth  string `json:"ifi"`
}

func Subscribe(a, n string) (net.Conn, error) {
	addr, err := net.ResolveUDPAddr("udp", a)
	if err != nil {
		return nil, err
	}
	i, err := net.InterfaceByName(n)
	if err != nil && len(n) > 0 {
		return nil, err
	}
	c, err := net.ListenMulticastUDP("udp", i, addr)
	if err != nil {
		return nil, err
	}
	return &subscriber{c}, nil
}

func Dispatch(a string) (net.Conn, error) {
	c, err := net.Dial("udp", a)
	if err != nil {
		return nil, err
	}
	return &subscriber{c}, nil
}

func Forward(a, s string) (net.Conn, error) {
	c, err := net.Dial("tcp", a)
	if err != nil {
		return nil, err
	}
	id, _ := uuid.UUID5(uuid.URL, []byte(s))

	f := &forwarder{
		Conn:    c,
		id:      id.Bytes(),
		padding: Padding,
		reader:  bufio.NewReaderSize(rand.Reader, 4096),
	}
	if _, err := f.Write(f.id); err != nil {
		f.Close()
		return nil, err
	}
	return f, nil
}

type Router struct {
	net.Listener

	routes map[string]*pool
}

func (r *Router) Accept() (net.Conn, net.Conn, error) {
	c, err := r.Listener.Accept()
	if err != nil {
		return nil, nil, err
	}
	id := make([]byte, Size+uuid.Size)
	if _, err := io.ReadFull(c, id); err != nil {
		c.Close()
		return nil, nil, err
	} else {
		id = id[:uuid.Size]
	}
	p, ok := r.routes[string(id)]
	if !ok {
		c.Close()
		return nil, nil, ErrUnknownId
	}
	io.CopyN(ioutil.Discard, c, Padding)
	w, err := p.Acquire()
	if err != nil {
		return nil, nil, err
	}
	return &forwarder{Conn: c, id: id}, w, nil
}

func (r *Router) Close() error {
	err := r.Listener.Close()
	for _, p := range r.routes {
		if e := p.Close(); err == nil && e != nil {
			err = e
		}
	}
	return err
}

func NewRouter(a string, rs []Route) (*Router, error) {
	l, err := net.Listen("tcp", a)
	if err != nil {
		return nil, err
	}
	gs := make(map[string]*pool)
	for _, r := range rs {
		id, _ := uuid.UUID5(uuid.URL, []byte(r.Id))
		p := &pool{
			c: make(chan net.Conn, 5),
			a: r.Addr,
		}
		gs[string(id.Bytes())] = p
	}
	return &Router{Listener: l, routes: gs}, nil
}

type subscriber struct {
	net.Conn
}

func (s *subscriber) Read(b []byte) (int, error) {
	d := make([]byte, len(b))
	r, err := s.Conn.Read(d)
	if err != nil && r == 0 {
		return r, err
	} else {
		d = d[:r]
		go log.Printf("%d bytes read from %s (%x)", r, s.LocalAddr(), md5.Sum(d))
	}
	sum := make([]byte, 4)
	binary.BigEndian.PutUint32(sum, adler32.Checksum(d))
	d = append(d, sum...)

	return copy(b, d), err
}

func (s *subscriber) Write(b []byte) (int, error) {
	d, sum := b[:len(b)-adler32.Size], b[len(b)-adler32.Size:]
	if a := adler32.Checksum(d); a != binary.BigEndian.Uint32(sum) {
		return len(b), ErrCorrupted
	}
	w, err := s.Conn.Write(d)
	if err == nil {
		go log.Printf("%d (%d) bytes written to %s (%x)", w, len(d), s.RemoteAddr(), md5.Sum(d))
	}
	return len(b), err
}

type forwarder struct {
	net.Conn

	id []byte

	sequence uint32
	padding  uint16

	reader io.Reader
}

func (f *forwarder) Read(b []byte) (int, error) {
	d, err := readFrom(f.Conn, f.id)
	go log.Printf("%d bytes received from %s", len(d), f.RemoteAddr())
	return copy(b, d), err
}

func (f *forwarder) Write(b []byte) (int, error) {
	s := atomic.AddUint32(&f.sequence, 1)
	err := writeTo(f.Conn, s, f.id, b)
	go log.Printf("%d bytes sent to %s", len(b), f.RemoteAddr())
	return len(b), err
}

type block struct {
	Id       [uuid.Size]byte
	Length   uint16
	Sequence uint32
	N        uint8
	C        uint8
	R        uint16
}

func readFrom(r io.Reader, i []byte) ([]byte, error) {
	w := new(bytes.Buffer)

	for {
		v := new(block)
		if err := binary.Read(r, binary.BigEndian, v); err != nil {
			return nil, err
		}
		if _, err := io.CopyN(w, r, int64(v.R)); err != nil {
			return nil, err
		}
		if v.R < Padding {
			io.CopyN(ioutil.Discard, r, Padding)
		}
		if !bytes.Equal(v.Id[:], i) {
			return nil, ErrUnknownId
		}
		if v.N == v.C {
			break
		}
	}
	return w.Bytes(), nil
}

func writeTo(w io.Writer, s uint32, id, b []byte) error {
	const size = 1024
	r := bytes.NewReader(b)

	n := r.Size() / size
	/*if m := r.Size() % size; n > 0 && m != 0 {
		n += 1
	}*/
	var j uint8
	for r.Len() > 0 {
		buf := new(bytes.Buffer)

		buf.Write(id)
		binary.Write(buf, binary.BigEndian, s)
		binary.Write(buf, binary.BigEndian, uint16(len(b)))
		binary.Write(buf, binary.BigEndian, uint8(n))
		binary.Write(buf, binary.BigEndian, uint8(j))

		count := size
		if r.Len() < size {
			count = r.Len()
		}
		binary.Write(buf, binary.BigEndian, uint16(count))

		io.CopyN(buf, r, int64(count))
		if count < Padding {
			io.CopyN(buf, rand.Reader, Padding)
		}
		if _, err := io.Copy(w, buf); err != nil {
			return err
		}
		j++
	}
	return nil
}

type pool struct {
	a string
	c chan net.Conn

	closed bool
	mu     sync.Mutex
}

func (p *pool) Acquire() (net.Conn, error) {
	select {
	case c, ok := <-p.c:
		if !ok {
			return nil, ErrClosed
		}
		return c, nil
	default:
		return Dispatch(p.a)
	}
}

func (p *pool) Release(c net.Conn) error {
	if p.closed {
		return ErrClosed
	}
	select {
	case p.c <- c:
		return nil
	default:
		return c.Close()
	}
}

func (p *pool) Close() error {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.closed {
		return ErrClosed
	}
	var err error
	for c := range p.c {
		if e := c.Close(); err == nil && e != nil {
			err = e
		}
	}
	close(p.c)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// 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 blueprint

import (
	"fmt"

	"github.com/google/blueprint/pathtools"
)

type Singleton interface {
	GenerateBuildActions(SingletonContext)
}

type SingletonContext interface {
	// Config returns the config object that was passed to Context.PrepareBuildActions.
	Config() interface{}

	// Name returns the name of the current singleton passed to Context.RegisterSingletonType
	Name() string

	// ModuleName returns the name of the given Module.  See BaseModuleContext.ModuleName for more information.
	ModuleName(module Module) string

	// ModuleDir returns the directory of the given Module.  See BaseModuleContext.ModuleDir for more information.
	ModuleDir(module Module) string

	// ModuleSubDir returns the unique subdirectory name of the given Module.  See ModuleContext.ModuleSubDir for
	// more information.
	ModuleSubDir(module Module) string

	// ModuleType returns the type of the given Module.  See BaseModuleContext.ModuleType for more information.
	ModuleType(module Module) string

	// BlueprintFile returns the path of the Blueprint file that defined the given module.
	BlueprintFile(module Module) string

	// ModuleErrorf reports an error at the line number of the module type in the module definition.
	ModuleErrorf(module Module, format string, args ...interface{})

	// Errorf reports an error at the specified position of the module definition file.
	Errorf(format string, args ...interface{})

	// Failed returns true if any errors have been reported.  In most cases the singleton can continue with generating
	// build rules after an error, allowing it to report additional errors in a single run, but in cases where the error
	// has prevented the singleton from creating necessary data it can return early when Failed returns true.
	Failed() bool

	// Variable creates a new ninja variable scoped to the singleton.  It can be referenced by calls to Rule and Build
	// in the same singleton.
	Variable(pctx PackageContext, name, value string)

	// Rule creates a new ninja rule scoped to the singleton.  It can be referenced by calls to Build in the same
	// singleton.
	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule

	// Build creates a new ninja build statement.
	Build(pctx PackageContext, params BuildParams)

	// RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja.
	RequireNinjaVersion(major, minor, micro int)

	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
	// that controls where Ninja stores its build log files.  This value can be
	// set at most one time for a single build, later calls are ignored.
	SetNinjaBuildDir(pctx PackageContext, value string)

	// AddSubninja adds a ninja file to include with subninja. This should likely
	// only ever be used inside bootstrap to handle glob rules.
	AddSubninja(file string)

	// Eval takes a string with embedded ninja variables, and returns a string
	// with all of the variables recursively expanded. Any variables references
	// are expanded in the scope of the PackageContext.
	Eval(pctx PackageContext, ninjaStr string) (string, error)

	// VisitAllModules calls visit for each defined variant of each module in an unspecified order.
	VisitAllModules(visit func(Module))

	// VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns
	// true calls visit.
	VisitAllModulesIf(pred func(Module) bool, visit func(Module))

	// VisitDirectDeps calls visit for each direct dependency of the Module.  If there are
	// multiple direct dependencies on the same module visit will be called multiple times on
	// that module and OtherModuleDependencyTag will return a different tag for each.
	//
	// The Module passed to the visit function should not be retained outside of the visit
	// function, it may be invalidated by future mutators.
	VisitDirectDeps(module Module, visit func(Module))

	// VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred
	// returns true calls visit.  If there are multiple direct dependencies on the same module
	// pred and visit will be called multiple times on that module and OtherModuleDependencyTag
	// will return a different tag for each.
	//
	// The Module passed to the visit function should not be retained outside of the visit
	// function, it may be invalidated by future mutators.
	VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))

	// VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first
	// order. visit will only be called once for any given module, even if there are multiple paths through the
	// dependency tree to the module or multiple direct dependencies with different tags.
	VisitDepsDepthFirst(module Module, visit func(Module))

	// VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing
	// the dependency tree in depth first order.  visit will only be called once for any given module, even if there are
	// multiple paths through the dependency tree to the module or multiple direct dependencies with different tags.
	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
		visit func(Module))

	// VisitAllModuleVariants calls visit for each variant of the given module.
	VisitAllModuleVariants(module Module, visit func(Module))

	// PrimaryModule returns the first variant of the given module.  This can be used to perform
	//	// singleton actions that are only done once for all variants of a module.
	PrimaryModule(module Module) Module

	// FinalModule returns the last variant of the given module.  This can be used to perform
	// singleton actions that are only done once for all variants of a module.
	FinalModule(module Module) Module

	// AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest.  The
	// primary builder will be rerun whenever the specified files are modified.
	AddNinjaFileDeps(deps ...string)

	// GlobWithDeps returns a list of files and directories that match the
	// specified pattern but do not match any of the patterns in excludes.
	// Any directories will have a '/' suffix. It also adds efficient
	// dependencies to rerun the primary builder whenever a file matching
	// the pattern as added or removed, without rerunning if a file that
	// does not match the pattern is added to a searched directory.
	GlobWithDeps(pattern string, excludes []string) ([]string, error)

	// Fs returns a pathtools.Filesystem that can be used to interact with files.  Using the Filesystem interface allows
	// the singleton to be used in build system tests that run against a mock filesystem.
	Fs() pathtools.FileSystem

	// ModuleListFile returns a string representation of the path to the file
	// which lists blueprint files to parse.
	ModuleListFile() string
}

var _ SingletonContext = (*singletonContext)(nil)

type singletonContext struct {
	name    string
	context *Context
	config  interface{}
	scope   *localScope
	globals *liveTracker

	ninjaFileDeps []string
	errs          []error

	actionDefs localBuildActions
}

func (s *singletonContext) Config() interface{} {
	return s.config
}

func (s *singletonContext) Name() string {
	return s.name
}

func (s *singletonContext) ModuleName(logicModule Module) string {
	return s.context.ModuleName(logicModule)
}

func (s *singletonContext) ModuleDir(logicModule Module) string {
	return s.context.ModuleDir(logicModule)
}

func (s *singletonContext) ModuleSubDir(logicModule Module) string {
	return s.context.ModuleSubDir(logicModule)
}

func (s *singletonContext) ModuleType(logicModule Module) string {
	return s.context.ModuleType(logicModule)
}

func (s *singletonContext) BlueprintFile(logicModule Module) string {
	return s.context.BlueprintFile(logicModule)
}

func (s *singletonContext) error(err error) {
	if err != nil {
		s.errs = append(s.errs, err)
	}
}

func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
	args ...interface{}) {

	s.error(s.context.ModuleErrorf(logicModule, format, args...))
}

func (s *singletonContext) Errorf(format string, args ...interface{}) {
	// TODO: Make this not result in the error being printed as "internal error"
	s.error(fmt.Errorf(format, args...))
}

func (s *singletonContext) Failed() bool {
	return len(s.errs) > 0
}

func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
	s.scope.ReparentTo(pctx)

	v, err := s.scope.AddLocalVariable(name, value)
	if err != nil {
		panic(err)
	}

	s.actionDefs.variables = append(s.actionDefs.variables, v)
}

func (s *singletonContext) Rule(pctx PackageContext, name string,
	params RuleParams, argNames ...string) Rule {

	s.scope.ReparentTo(pctx)

	r, err := s.scope.AddLocalRule(name, &params, argNames...)
	if err != nil {
		panic(err)
	}

	s.actionDefs.rules = append(s.actionDefs.rules, r)

	return r
}

func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
	s.scope.ReparentTo(pctx)

	def, err := parseBuildParams(s.scope, &params)
	if err != nil {
		panic(err)
	}

	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
}

func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
	s.scope.ReparentTo(pctx)

	ninjaStr, err := parseNinjaString(s.scope, str)
	if err != nil {
		return "", err
	}

	err = s.globals.addNinjaStringDeps(ninjaStr)
	if err != nil {
		return "", err
	}

	return ninjaStr.Eval(s.globals.variables)
}

func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
	s.context.requireNinjaVersion(major, minor, micro)
}

func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) {
	s.scope.ReparentTo(pctx)

	ninjaValue, err := parseNinjaString(s.scope, value)
	if err != nil {
		panic(err)
	}

	s.context.setNinjaBuildDir(ninjaValue)
}

func (s *singletonContext) AddSubninja(file string) {
	s.context.subninjas = append(s.context.subninjas, file)
}

func (s *singletonContext) VisitAllModules(visit func(Module)) {
	var visitingModule Module
	defer func() {
		if r := recover(); r != nil {
			panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
				funcName(visit), s.context.moduleInfo[visitingModule]))
		}
	}()

	s.context.VisitAllModules(func(m Module) {
		visitingModule = m
		visit(m)
	})
}

func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
	visit func(Module)) {

	s.context.VisitAllModulesIf(pred, visit)
}

func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) {
	s.context.VisitDirectDeps(module, visit)
}

func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
	s.context.VisitDirectDepsIf(module, pred, visit)
}

func (s *singletonContext) VisitDepsDepthFirst(module Module,
	visit func(Module)) {

	s.context.VisitDepsDepthFirst(module, visit)
}

func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
	pred func(Module) bool, visit func(Module)) {

	s.context.VisitDepsDepthFirstIf(module, pred, visit)
}

func (s *singletonContext) PrimaryModule(module Module) Module {
	return s.context.PrimaryModule(module)
}

func (s *singletonContext) FinalModule(module Module) Module {
	return s.context.FinalModule(module)
}

func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
	s.context.VisitAllModuleVariants(module, visit)
}

func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
}

func (s *singletonContext) GlobWithDeps(pattern string,
	excludes []string) ([]string, error) {
	return s.context.glob(pattern, excludes)
}

func (s *singletonContext) Fs() pathtools.FileSystem {
	return s.context.fs
}

func (s *singletonContext) ModuleListFile() string {
	return s.context.moduleListFile
}
<commit_msg>Revert changes to singleton_ctx.go<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// 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 blueprint

import (
	"fmt"

	"github.com/google/blueprint/pathtools"
)

type Singleton interface {
	GenerateBuildActions(SingletonContext)
}

type SingletonContext interface {
	// Config returns the config object that was passed to Context.PrepareBuildActions.
	Config() interface{}

	// Name returns the name of the current singleton passed to Context.RegisterSingletonType
	Name() string

	// ModuleName returns the name of the given Module.  See BaseModuleContext.ModuleName for more information.
	ModuleName(module Module) string

	// ModuleDir returns the directory of the given Module.  See BaseModuleContext.ModuleDir for more information.
	ModuleDir(module Module) string

	// ModuleSubDir returns the unique subdirectory name of the given Module.  See ModuleContext.ModuleSubDir for
	// more information.
	ModuleSubDir(module Module) string

	// ModuleType returns the type of the given Module.  See BaseModuleContext.ModuleType for more information.
	ModuleType(module Module) string

	// BlueprintFile returns the path of the Blueprint file that defined the given module.
	BlueprintFile(module Module) string

	// ModuleErrorf reports an error at the line number of the module type in the module definition.
	ModuleErrorf(module Module, format string, args ...interface{})

	// Errorf reports an error at the specified position of the module definition file.
	Errorf(format string, args ...interface{})

	// Failed returns true if any errors have been reported.  In most cases the singleton can continue with generating
	// build rules after an error, allowing it to report additional errors in a single run, but in cases where the error
	// has prevented the singleton from creating necessary data it can return early when Failed returns true.
	Failed() bool

	// Variable creates a new ninja variable scoped to the singleton.  It can be referenced by calls to Rule and Build
	// in the same singleton.
	Variable(pctx PackageContext, name, value string)

	// Rule creates a new ninja rule scoped to the singleton.  It can be referenced by calls to Build in the same
	// singleton.
	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule

	// Build creates a new ninja build statement.
	Build(pctx PackageContext, params BuildParams)

	// RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja.
	RequireNinjaVersion(major, minor, micro int)

	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
	// that controls where Ninja stores its build log files.  This value can be
	// set at most one time for a single build, later calls are ignored.
	SetNinjaBuildDir(pctx PackageContext, value string)

	// AddSubninja adds a ninja file to include with subninja. This should likely
	// only ever be used inside bootstrap to handle glob rules.
	AddSubninja(file string)

	// Eval takes a string with embedded ninja variables, and returns a string
	// with all of the variables recursively expanded. Any variables references
	// are expanded in the scope of the PackageContext.
	Eval(pctx PackageContext, ninjaStr string) (string, error)

	// VisitAllModules calls visit for each defined variant of each module in an unspecified order.
	VisitAllModules(visit func(Module))

	// VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns
	// true calls visit.
	VisitAllModulesIf(pred func(Module) bool, visit func(Module))

	// VisitDirectDeps calls visit for each direct dependency of the Module.  If there are
	// multiple direct dependencies on the same module visit will be called multiple times on
	// that module and OtherModuleDependencyTag will return a different tag for each.
	//
	// The Module passed to the visit function should not be retained outside of the visit
	// function, it may be invalidated by future mutators.
	VisitDirectDeps(module Module, visit func(Module))

	// VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred
	// returns true calls visit.  If there are multiple direct dependencies on the same module
	// pred and visit will be called multiple times on that module and OtherModuleDependencyTag
	// will return a different tag for each.
	//
	// The Module passed to the visit function should not be retained outside of the visit
	// function, it may be invalidated by future mutators.
	VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))

	// VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first
	// order. visit will only be called once for any given module, even if there are multiple paths through the
	// dependency tree to the module or multiple direct dependencies with different tags.
	VisitDepsDepthFirst(module Module, visit func(Module))

	// VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing
	// the dependency tree in depth first order.  visit will only be called once for any given module, even if there are
	// multiple paths through the dependency tree to the module or multiple direct dependencies with different tags.
	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
		visit func(Module))

	// VisitAllModuleVariants calls visit for each variant of the given module.
	VisitAllModuleVariants(module Module, visit func(Module))

	// PrimaryModule returns the first variant of the given module.  This can be used to perform
	//	// singleton actions that are only done once for all variants of a module.
	PrimaryModule(module Module) Module

	// FinalModule returns the last variant of the given module.  This can be used to perform
	// singleton actions that are only done once for all variants of a module.
	FinalModule(module Module) Module

	// AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest.  The
	// primary builder will be rerun whenever the specified files are modified.
	AddNinjaFileDeps(deps ...string)

	// GlobWithDeps returns a list of files and directories that match the
	// specified pattern but do not match any of the patterns in excludes.
	// Any directories will have a '/' suffix. It also adds efficient
	// dependencies to rerun the primary builder whenever a file matching
	// the pattern as added or removed, without rerunning if a file that
	// does not match the pattern is added to a searched directory.
	GlobWithDeps(pattern string, excludes []string) ([]string, error)

	// Fs returns a pathtools.Filesystem that can be used to interact with files.  Using the Filesystem interface allows
	// the singleton to be used in build system tests that run against a mock filesystem.
	Fs() pathtools.FileSystem
}

var _ SingletonContext = (*singletonContext)(nil)

type singletonContext struct {
	name    string
	context *Context
	config  interface{}
	scope   *localScope
	globals *liveTracker

	ninjaFileDeps []string
	errs          []error

	actionDefs localBuildActions
}

func (s *singletonContext) Config() interface{} {
	return s.config
}

func (s *singletonContext) Name() string {
	return s.name
}

func (s *singletonContext) ModuleName(logicModule Module) string {
	return s.context.ModuleName(logicModule)
}

func (s *singletonContext) ModuleDir(logicModule Module) string {
	return s.context.ModuleDir(logicModule)
}

func (s *singletonContext) ModuleSubDir(logicModule Module) string {
	return s.context.ModuleSubDir(logicModule)
}

func (s *singletonContext) ModuleType(logicModule Module) string {
	return s.context.ModuleType(logicModule)
}

func (s *singletonContext) BlueprintFile(logicModule Module) string {
	return s.context.BlueprintFile(logicModule)
}

func (s *singletonContext) error(err error) {
	if err != nil {
		s.errs = append(s.errs, err)
	}
}

func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
	args ...interface{}) {

	s.error(s.context.ModuleErrorf(logicModule, format, args...))
}

func (s *singletonContext) Errorf(format string, args ...interface{}) {
	// TODO: Make this not result in the error being printed as "internal error"
	s.error(fmt.Errorf(format, args...))
}

func (s *singletonContext) Failed() bool {
	return len(s.errs) > 0
}

func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
	s.scope.ReparentTo(pctx)

	v, err := s.scope.AddLocalVariable(name, value)
	if err != nil {
		panic(err)
	}

	s.actionDefs.variables = append(s.actionDefs.variables, v)
}

func (s *singletonContext) Rule(pctx PackageContext, name string,
	params RuleParams, argNames ...string) Rule {

	s.scope.ReparentTo(pctx)

	r, err := s.scope.AddLocalRule(name, &params, argNames...)
	if err != nil {
		panic(err)
	}

	s.actionDefs.rules = append(s.actionDefs.rules, r)

	return r
}

func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
	s.scope.ReparentTo(pctx)

	def, err := parseBuildParams(s.scope, &params)
	if err != nil {
		panic(err)
	}

	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
}

func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
	s.scope.ReparentTo(pctx)

	ninjaStr, err := parseNinjaString(s.scope, str)
	if err != nil {
		return "", err
	}

	err = s.globals.addNinjaStringDeps(ninjaStr)
	if err != nil {
		return "", err
	}

	return ninjaStr.Eval(s.globals.variables)
}

func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
	s.context.requireNinjaVersion(major, minor, micro)
}

func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) {
	s.scope.ReparentTo(pctx)

	ninjaValue, err := parseNinjaString(s.scope, value)
	if err != nil {
		panic(err)
	}

	s.context.setNinjaBuildDir(ninjaValue)
}

func (s *singletonContext) AddSubninja(file string) {
	s.context.subninjas = append(s.context.subninjas, file)
}

func (s *singletonContext) VisitAllModules(visit func(Module)) {
	var visitingModule Module
	defer func() {
		if r := recover(); r != nil {
			panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
				funcName(visit), s.context.moduleInfo[visitingModule]))
		}
	}()

	s.context.VisitAllModules(func(m Module) {
		visitingModule = m
		visit(m)
	})
}

func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
	visit func(Module)) {

	s.context.VisitAllModulesIf(pred, visit)
}

func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) {
	s.context.VisitDirectDeps(module, visit)
}

func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
	s.context.VisitDirectDepsIf(module, pred, visit)
}

func (s *singletonContext) VisitDepsDepthFirst(module Module,
	visit func(Module)) {

	s.context.VisitDepsDepthFirst(module, visit)
}

func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
	pred func(Module) bool, visit func(Module)) {

	s.context.VisitDepsDepthFirstIf(module, pred, visit)
}

func (s *singletonContext) PrimaryModule(module Module) Module {
	return s.context.PrimaryModule(module)
}

func (s *singletonContext) FinalModule(module Module) Module {
	return s.context.FinalModule(module)
}

func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
	s.context.VisitAllModuleVariants(module, visit)
}

func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
}

func (s *singletonContext) GlobWithDeps(pattern string,
	excludes []string) ([]string, error) {
	return s.context.glob(pattern, excludes)
}

func (s *singletonContext) Fs() pathtools.FileSystem {
	return s.context.fs
}
<|endoftext|>


<commit_before>package spold2

import (
	"encoding/xml"
)

// EcoSpold is the container for activity data sets.
type EcoSpold struct {
	XMLName      xml.Name         `xml:"http://www.EcoInvent.org/EcoSpold02 ecoSpold"`
	DataSet      *ActivityDataSet `xml:"activityDataset,omitempty"`
	ChildDataSet *ActivityDataSet `xml:"childActivityDataset,omitempty"`
}

// ActivityDataSet contains the information of an activity.
type ActivityDataSet struct {
	Description     *ActivityDescription `xml:"activityDescription>activity"`
	Classifications []Classification     `xml:"activityDescription>classification"`
	Geography       *Geography           `xml:"activityDescription>geography"`
	Technology      *Technology          `xml:"activityDescription>technology"`
	TimePeriod      *TimePeriod          `xml:"activityDescription>timePeriod"`
	FlowData        *FlowData            `xml:"flowData,omitempty"`
}

// ActivityDescription contains the identifying information of an activity
// dataset including name and classification.
type ActivityDescription struct {
	ID               string `xml:"id,attr"`
	NameID           string `xml:"activityNameId,attr,omitempty"`
	ParentID         string `xml:"parentActivityId,attr,omitempty"`
	InheritanceDepth int    `xml:"inheritanceDepth,attr"`
	Type             int    `xml:"type,attr"`
	SpecialType      int    `xml:"specialActivityType,attr"`

	Name    string        `xml:"activityName"`
	Comment *TextAndImage `xml:"generalComment,omitempty"`
}

// Classification contains classification pairs to specify the activity.
type Classification struct {
	ID string `xml:"classificationId,attr"`

	System string `xml:"classificationSystem,omitempty"`
	Value  string `xml:"classificationValue,omitempty"`
}

// Geography contains information about the geographic validity of the process.
type Geography struct {
	ID string `xml:"geographyId,attr"`

	ShortName string        `xml:"shortname,omitempty"`
	Comment   *TextAndImage `xml:"comment,omitempty"`
}

// Technology contains information about the process technology.
type Technology struct {
	Level   int           `xml:"technologyLevel,attr"`
	Comment *TextAndImage `xml:"comment,omitempty"`
}

// TimePeriod contains information about the process time.
type TimePeriod struct {
	StartDate                  string        `xml:"startDate,attr"`
	EndDate                    string        `xml:"endDate,attr"`
	IsDataValidForEntirePeriod bool          `xml:"isDataValidForEntirePeriod,attr"`
	Comment                    *TextAndImage `xml:"comment,omitempty"`
}

// TODO: time etc.

// FlowData contains the inputs and outputs of an activity data set.
type FlowData struct {
	IntermediateExchanges []IntermediateExchange `xml:"intermediateExchange"`
	ElementaryExchanges   []ElementaryExchange   `xml:"elementaryExchange"`
}

// Exchange contains the common information of intermediate and elementary
// exchanges.
type Exchange struct {
	ID          string     `xml:"id,attr"`
	UnitID      string     `xml:"unitId,attr"`
	Amount      float64    `xml:"amount,attr"`
	Name        string     `xml:"name"`
	UnitName    string     `xml:"unitName"`
	Comment     string     `xml:"comment,omitempty"`
	Properties  []Property `xml:"property"`
	OutputGroup *int       `xml:"outputGroup,omitempty"`
	InputGroup  *int       `xml:"inputGroup,omitempty"`
}

// IntermediateExchange comprises intermediate product and waste inputs and
// outputs for the activity.
type IntermediateExchange struct {
	Exchange
	ExchangeID     string `xml:"intermediateExchangeId,attr"`
	ActivityLinkID string `xml:"activityLinkId,attr"`
}

// Compartment contains the compartment information of an elementary flow
type Compartment struct {
	ID             string `xml:"subcompartmentId,attr"`
	Compartment    string `xml:"compartment"`
	SubCompartment string `xml:"subcompartment"`
}

// ElementaryExchange are environmental inputs and outputs of a process
type ElementaryExchange struct {
	Exchange
	CAS         string       `xml:"casNumber,attr,omitempty"`
	ExchangeID  string       `xml:"elementaryExchangeId,attr"`
	Compartment *Compartment `xml:"compartment,omitempty"`
}

// Property contains additional information about an exchange.
type Property struct {
	ID       string  `xml:"propertyId,attr"`
	Amount   float64 `xml:"amount,attr"`
	UnitID   string  `xml:"unitId,attr"`
	Name     string  `xml:"name"`
	UnitName string  `xml:"unitName"`
	Comment  string  `xml:"comment,omitempty"`
}
<commit_msg>renamed ExchangeID to FlowID<commit_after>package spold2

import (
	"encoding/xml"
)

// EcoSpold is the container for activity data sets.
type EcoSpold struct {
	XMLName      xml.Name         `xml:"http://www.EcoInvent.org/EcoSpold02 ecoSpold"`
	DataSet      *ActivityDataSet `xml:"activityDataset,omitempty"`
	ChildDataSet *ActivityDataSet `xml:"childActivityDataset,omitempty"`
}

// ActivityDataSet contains the information of an activity.
type ActivityDataSet struct {
	Description     *ActivityDescription `xml:"activityDescription>activity"`
	Classifications []Classification     `xml:"activityDescription>classification"`
	Geography       *Geography           `xml:"activityDescription>geography"`
	Technology      *Technology          `xml:"activityDescription>technology"`
	TimePeriod      *TimePeriod          `xml:"activityDescription>timePeriod"`
	FlowData        *FlowData            `xml:"flowData,omitempty"`
}

// ActivityDescription contains the identifying information of an activity
// dataset including name and classification.
type ActivityDescription struct {
	ID               string `xml:"id,attr"`
	NameID           string `xml:"activityNameId,attr,omitempty"`
	ParentID         string `xml:"parentActivityId,attr,omitempty"`
	InheritanceDepth int    `xml:"inheritanceDepth,attr"`
	Type             int    `xml:"type,attr"`
	SpecialType      int    `xml:"specialActivityType,attr"`

	Name    string        `xml:"activityName"`
	Comment *TextAndImage `xml:"generalComment,omitempty"`
}

// Classification contains classification pairs to specify the activity.
type Classification struct {
	ID string `xml:"classificationId,attr"`

	System string `xml:"classificationSystem,omitempty"`
	Value  string `xml:"classificationValue,omitempty"`
}

// Geography contains information about the geographic validity of the process.
type Geography struct {
	ID string `xml:"geographyId,attr"`

	ShortName string        `xml:"shortname,omitempty"`
	Comment   *TextAndImage `xml:"comment,omitempty"`
}

// Technology contains information about the process technology.
type Technology struct {
	Level   int           `xml:"technologyLevel,attr"`
	Comment *TextAndImage `xml:"comment,omitempty"`
}

// TimePeriod contains information about the process time.
type TimePeriod struct {
	StartDate                  string        `xml:"startDate,attr"`
	EndDate                    string        `xml:"endDate,attr"`
	IsDataValidForEntirePeriod bool          `xml:"isDataValidForEntirePeriod,attr"`
	Comment                    *TextAndImage `xml:"comment,omitempty"`
}

// TODO: time etc.

// FlowData contains the inputs and outputs of an activity data set.
type FlowData struct {
	IntermediateExchanges []IntermediateExchange `xml:"intermediateExchange"`
	ElementaryExchanges   []ElementaryExchange   `xml:"elementaryExchange"`
}

// Exchange contains the common information of intermediate and elementary
// exchanges.
type Exchange struct {
	ID          string     `xml:"id,attr"`
	UnitID      string     `xml:"unitId,attr"`
	Amount      float64    `xml:"amount,attr"`
	Name        string     `xml:"name"`
	UnitName    string     `xml:"unitName"`
	Comment     string     `xml:"comment,omitempty"`
	Properties  []Property `xml:"property"`
	OutputGroup *int       `xml:"outputGroup,omitempty"`
	InputGroup  *int       `xml:"inputGroup,omitempty"`
}

// IntermediateExchange comprises intermediate product and waste inputs and
// outputs for the activity.
type IntermediateExchange struct {
	Exchange

	// FlowID is the value of the intermediateExchangeId attribute
	FlowID         string `xml:"intermediateExchangeId,attr"`
	ActivityLinkID string `xml:"activityLinkId,attr"`
}

// Compartment contains the compartment information of an elementary flow
type Compartment struct {
	ID             string `xml:"subcompartmentId,attr"`
	Compartment    string `xml:"compartment"`
	SubCompartment string `xml:"subcompartment"`
}

// ElementaryExchange are environmental inputs and outputs of a process
type ElementaryExchange struct {
	Exchange

	// FlowID is the value of the elementaryExchangeId attribute
	FlowID      string       `xml:"elementaryExchangeId,attr"`
	CAS         string       `xml:"casNumber,attr,omitempty"`
	Compartment *Compartment `xml:"compartment,omitempty"`
}

// Property contains additional information about an exchange.
type Property struct {
	ID       string  `xml:"propertyId,attr"`
	Amount   float64 `xml:"amount,attr"`
	UnitID   string  `xml:"unitId,attr"`
	Name     string  `xml:"name"`
	UnitName string  `xml:"unitName"`
	Comment  string  `xml:"comment,omitempty"`
}
<|endoftext|>