<commit_before>package prometheus

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"time"

	"github.com/ghodss/yaml"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

type podMetadata struct {
	ResourceVersion string `json:"resourceVersion"`
	SelfLink        string `json:"selfLink"`
}

type podResponse struct {
	Kind       string        `json:"kind"`
	APIVersion string        `json:"apiVersion"`
	Metadata   podMetadata   `json:"metadata"`
	Items      []*corev1.Pod `json:"items,string,omitempty"`
}

const cAdvisorPodListDefaultInterval = 60

// loadClient parses a kubeconfig from a file and returns a Kubernetes
// client. It does not support extensions or client auth providers.
func loadClient(kubeconfigPath string) (*kubernetes.Clientset, error) {
	data, err := os.ReadFile(kubeconfigPath)
	if err != nil {
		return nil, fmt.Errorf("failed reading '%s': %v", kubeconfigPath, err)
	}

	// Unmarshal YAML into a Kubernetes config object.
	var config rest.Config
	if err := yaml.Unmarshal(data, &config); err != nil {
		return nil, err
	}
	return kubernetes.NewForConfig(&config)
}

func (p *Prometheus) startK8s(ctx context.Context) error {
	config, err := rest.InClusterConfig()
	if err != nil {
		return fmt.Errorf("failed to get InClusterConfig - %v", err)
	}
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		u, err := user.Current()
		if err != nil {
			return fmt.Errorf("failed to get current user - %v", err)
		}

		configLocation := filepath.Join(u.HomeDir, ".kube/config")
		if p.KubeConfig != "" {
			configLocation = p.KubeConfig
		}
		client, err = loadClient(configLocation)
		if err != nil {
			return err
		}
	}

	p.wg.Add(1)
	go func() {
		defer p.wg.Done()
		for {
			select {
			case <-ctx.Done():
				return
			case <-time.After(time.Second):
				if p.isNodeScrapeScope {
					err = p.cAdvisor(ctx, config.BearerToken)
					if err != nil {
						p.Log.Errorf("Unable to monitor pods with node scrape scope: %s", err.Error())
					}
				} else {
					err = p.watchPod(ctx, client)
					if err != nil {
						p.Log.Errorf("Unable to watch resources: %s", err.Error())
					}
				}
			}
		}
	}()

	return nil
}

// An edge case exists if a pod goes offline at the same time a new pod is created
// (without the scrape annotations). K8s may re-assign the old pod ip to the non-scrape
// pod, causing errors in the logs. This is only true if the pod going offline is not
// directed to do so by K8s.
func (p *Prometheus) watchPod(ctx context.Context, client *kubernetes.Clientset) error {
	watcher, err := client.CoreV1().Pods(p.PodNamespace).Watch(ctx, metav1.ListOptions{
		LabelSelector: p.KubernetesLabelSelector,
		FieldSelector: p.KubernetesFieldSelector,
	})
	defer watcher.Stop()
	if err != nil {
		return err
	}

	for {
		select {
		case <-ctx.Done():
			return nil
		default:
			for event := range watcher.ResultChan() {
				pod, ok := event.Object.(*corev1.Pod)
				if !ok {
					return fmt.Errorf("Unexpected object when getting pods")
				}

				// If the pod is not "ready", there will be no ip associated with it.
				if pod.Annotations["prometheus.io/scrape"] != "true" ||
					!podReady(pod.Status.ContainerStatuses) {
					continue
				}

				switch event.Type {
				case watch.Added:
					registerPod(pod, p)
				case watch.Modified:
					// To avoid multiple actions for each event, unregister on the first event
					// in the delete sequence, when the containers are still "ready".
					if pod.GetDeletionTimestamp() != nil {
						unregisterPod(pod, p)
					} else {
						registerPod(pod, p)
					}
				}
			}
		}
	}
}

func (p *Prometheus) cAdvisor(ctx context.Context, bearerToken string) error {
	// The request will be the same each time
	podsURL := fmt.Sprintf("https://%s:10250/pods", p.NodeIP)
	req, err := http.NewRequest("GET", podsURL, nil)
	if err != nil {
		return fmt.Errorf("error when creating request to %s to get pod list: %w", podsURL, err)
	}
	req.Header.Set("Authorization", "Bearer "+bearerToken)
	req.Header.Add("Accept", "application/json")

	// Update right away so code is not waiting the length of the specified scrape interval initially
	err = updateCadvisorPodList(p, req)
	if err != nil {
		return fmt.Errorf("error initially updating pod list: %w", err)
	}

	scrapeInterval := cAdvisorPodListDefaultInterval
	if p.PodScrapeInterval != 0 {
		scrapeInterval = p.PodScrapeInterval
	}

	for {
		select {
		case <-ctx.Done():
			return nil
		case <-time.After(time.Duration(scrapeInterval) * time.Second):
			err := updateCadvisorPodList(p, req)
			if err != nil {
				return fmt.Errorf("error updating pod list: %w", err)
			}
		}
	}
}

func updateCadvisorPodList(p *Prometheus, req *http.Request) error {
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	httpClient := http.Client{}

	resp, err := httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("error when making request for pod list: %w", err)
	}

	// If err is nil, still check response code
	if resp.StatusCode != 200 {
		return fmt.Errorf("error when making request for pod list with status %s", resp.Status)
	}

	defer resp.Body.Close()

	cadvisorPodsResponse := podResponse{}

	// Will have expected type errors for some parts of corev1.Pod struct for some unused fields
	// Instead have nil checks for every used field in case of incorrect decoding
	if err := json.NewDecoder(resp.Body).Decode(&cadvisorPodsResponse); err != nil {
		return fmt.Errorf("decoding response failed: %v", err)
	}
	pods := cadvisorPodsResponse.Items

	// Updating pod list to be latest cadvisor response
	p.lock.Lock()
	p.kubernetesPods = make(map[string]URLAndAddress)

	// Register pod only if it has an annotation to scrape, if it is ready,
	// and if namespace and selectors are specified and match
	for _, pod := range pods {
		if necessaryPodFieldsArePresent(pod) &&
			pod.Annotations["prometheus.io/scrape"] == "true" &&
			podReady(pod.Status.ContainerStatuses) &&
			podHasMatchingNamespace(pod, p) &&
			podHasMatchingLabelSelector(pod, p.podLabelSelector) &&
			podHasMatchingFieldSelector(pod, p.podFieldSelector) {
			registerPod(pod, p)
		}
	}
	p.lock.Unlock()

	// No errors
	return nil
}

func necessaryPodFieldsArePresent(pod *corev1.Pod) bool {
	return pod.Annotations != nil &&
		pod.Labels != nil &&
		pod.Status.ContainerStatuses != nil
}

/* See the docs on kubernetes label selectors:
 * https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
 */
func podHasMatchingLabelSelector(pod *corev1.Pod, labelSelector labels.Selector) bool {
	if labelSelector == nil {
		return true
	}

	var labelsSet labels.Set = pod.Labels
	return labelSelector.Matches(labelsSet)
}

/* See ToSelectableFields() for list of fields that are selectable:
 * https://github.com/kubernetes/kubernetes/release-1.20/pkg/registry/core/pod/strategy.go
 * See docs on kubernetes field selectors:
 * https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/
 */
func podHasMatchingFieldSelector(pod *corev1.Pod, fieldSelector fields.Selector) bool {
	if fieldSelector == nil {
		return true
	}

	fieldsSet := make(fields.Set)
	fieldsSet["spec.nodeName"] = pod.Spec.NodeName
	fieldsSet["spec.restartPolicy"] = string(pod.Spec.RestartPolicy)
	fieldsSet["spec.schedulerName"] = pod.Spec.SchedulerName
	fieldsSet["spec.serviceAccountName"] = pod.Spec.ServiceAccountName
	fieldsSet["status.phase"] = string(pod.Status.Phase)
	fieldsSet["status.podIP"] = pod.Status.PodIP
	fieldsSet["status.nominatedNodeName"] = pod.Status.NominatedNodeName

	return fieldSelector.Matches(fieldsSet)
}

/*
 * If a namespace is specified and the pod doesn't have that namespace, return false
 * Else return true
 */
func podHasMatchingNamespace(pod *corev1.Pod, p *Prometheus) bool {
	return !(p.PodNamespace != "" && pod.Namespace != p.PodNamespace)
}

func podReady(statuss []corev1.ContainerStatus) bool {
	if len(statuss) == 0 {
		return false
	}
	for _, cs := range statuss {
		if !cs.Ready {
			return false
		}
	}
	return true
}

func registerPod(pod *corev1.Pod, p *Prometheus) {
	if p.kubernetesPods == nil {
		p.kubernetesPods = map[string]URLAndAddress{}
	}
	targetURL, err := getScrapeURL(pod)
	if err != nil {
		p.Log.Errorf("could not parse URL: %s", err)
		return
	} else if targetURL == nil {
		return
	}

	p.Log.Debugf("will scrape metrics from %q", targetURL.String())
	// add annotation as metrics tags
	tags := pod.Annotations
	if tags == nil {
		tags = map[string]string{}
	}
	tags["pod_name"] = pod.Name
	tags["namespace"] = pod.Namespace
	// add labels as metrics tags
	for k, v := range pod.Labels {
		tags[k] = v
	}
	podURL := p.AddressToURL(targetURL, targetURL.Hostname())

	// Locks earlier if using cAdvisor calls - makes a new list each time
	// rather than updating and removing from the same list
	if !p.isNodeScrapeScope {
		p.lock.Lock()
		defer p.lock.Unlock()
	}
	p.kubernetesPods[podURL.String()] = URLAndAddress{
		URL:         podURL,
		Address:     targetURL.Hostname(),
		OriginalURL: targetURL,
		Tags:        tags,
	}
}

func getScrapeURL(pod *corev1.Pod) (*url.URL, error) {
	ip := pod.Status.PodIP
	if ip == "" {
		// return as if scrape was disabled, we will be notified again once the pod
		// has an IP
		return nil, nil
	}

	scheme := pod.Annotations["prometheus.io/scheme"]
	pathAndQuery := pod.Annotations["prometheus.io/path"]
	port := pod.Annotations["prometheus.io/port"]

	if scheme == "" {
		scheme = "http"
	}
	if port == "" {
		port = "9102"
	}
	if pathAndQuery == "" {
		pathAndQuery = "/metrics"
	}

	base, err := url.Parse(pathAndQuery)
	if err != nil {
		return nil, err
	}

	base.Scheme = scheme
	base.Host = net.JoinHostPort(ip, port)

	return base, nil
}

func unregisterPod(pod *corev1.Pod, p *Prometheus) {
	targetURL, err := getScrapeURL(pod)
	if err != nil {
		p.Log.Errorf("failed to parse url: %s", err)
		return
	} else if targetURL == nil {
		return
	}

	p.Log.Debugf("registered a delete request for %q in namespace %q", pod.Name, pod.Namespace)

	p.lock.Lock()
	defer p.lock.Unlock()
	if _, ok := p.kubernetesPods[targetURL.String()]; ok {
		delete(p.kubernetesPods, targetURL.String())
		p.Log.Debugf("will stop scraping for %q", targetURL.String())
	}
}
<commit_msg>fix: check error before defer in prometheus k8s (#10091)<commit_after>package prometheus

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"time"

	"github.com/ghodss/yaml"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

type podMetadata struct {
	ResourceVersion string `json:"resourceVersion"`
	SelfLink        string `json:"selfLink"`
}

type podResponse struct {
	Kind       string        `json:"kind"`
	APIVersion string        `json:"apiVersion"`
	Metadata   podMetadata   `json:"metadata"`
	Items      []*corev1.Pod `json:"items,string,omitempty"`
}

const cAdvisorPodListDefaultInterval = 60

// loadClient parses a kubeconfig from a file and returns a Kubernetes
// client. It does not support extensions or client auth providers.
func loadClient(kubeconfigPath string) (*kubernetes.Clientset, error) {
	data, err := os.ReadFile(kubeconfigPath)
	if err != nil {
		return nil, fmt.Errorf("failed reading '%s': %v", kubeconfigPath, err)
	}

	// Unmarshal YAML into a Kubernetes config object.
	var config rest.Config
	if err := yaml.Unmarshal(data, &config); err != nil {
		return nil, err
	}
	return kubernetes.NewForConfig(&config)
}

func (p *Prometheus) startK8s(ctx context.Context) error {
	config, err := rest.InClusterConfig()
	if err != nil {
		return fmt.Errorf("failed to get InClusterConfig - %v", err)
	}
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		u, err := user.Current()
		if err != nil {
			return fmt.Errorf("failed to get current user - %v", err)
		}

		configLocation := filepath.Join(u.HomeDir, ".kube/config")
		if p.KubeConfig != "" {
			configLocation = p.KubeConfig
		}
		client, err = loadClient(configLocation)
		if err != nil {
			return err
		}
	}

	p.wg.Add(1)
	go func() {
		defer p.wg.Done()
		for {
			select {
			case <-ctx.Done():
				return
			case <-time.After(time.Second):
				if p.isNodeScrapeScope {
					err = p.cAdvisor(ctx, config.BearerToken)
					if err != nil {
						p.Log.Errorf("Unable to monitor pods with node scrape scope: %s", err.Error())
					}
				} else {
					err = p.watchPod(ctx, client)
					if err != nil {
						p.Log.Errorf("Unable to watch resources: %s", err.Error())
					}
				}
			}
		}
	}()

	return nil
}

// An edge case exists if a pod goes offline at the same time a new pod is created
// (without the scrape annotations). K8s may re-assign the old pod ip to the non-scrape
// pod, causing errors in the logs. This is only true if the pod going offline is not
// directed to do so by K8s.
func (p *Prometheus) watchPod(ctx context.Context, client *kubernetes.Clientset) error {
	watcher, err := client.CoreV1().Pods(p.PodNamespace).Watch(ctx, metav1.ListOptions{
		LabelSelector: p.KubernetesLabelSelector,
		FieldSelector: p.KubernetesFieldSelector,
	})
	if err != nil {
		return err
	}
	defer watcher.Stop()

	for {
		select {
		case <-ctx.Done():
			return nil
		default:
			for event := range watcher.ResultChan() {
				pod, ok := event.Object.(*corev1.Pod)
				if !ok {
					return fmt.Errorf("Unexpected object when getting pods")
				}

				// If the pod is not "ready", there will be no ip associated with it.
				if pod.Annotations["prometheus.io/scrape"] != "true" ||
					!podReady(pod.Status.ContainerStatuses) {
					continue
				}

				switch event.Type {
				case watch.Added:
					registerPod(pod, p)
				case watch.Modified:
					// To avoid multiple actions for each event, unregister on the first event
					// in the delete sequence, when the containers are still "ready".
					if pod.GetDeletionTimestamp() != nil {
						unregisterPod(pod, p)
					} else {
						registerPod(pod, p)
					}
				}
			}
		}
	}
}

func (p *Prometheus) cAdvisor(ctx context.Context, bearerToken string) error {
	// The request will be the same each time
	podsURL := fmt.Sprintf("https://%s:10250/pods", p.NodeIP)
	req, err := http.NewRequest("GET", podsURL, nil)
	if err != nil {
		return fmt.Errorf("error when creating request to %s to get pod list: %w", podsURL, err)
	}
	req.Header.Set("Authorization", "Bearer "+bearerToken)
	req.Header.Add("Accept", "application/json")

	// Update right away so code is not waiting the length of the specified scrape interval initially
	err = updateCadvisorPodList(p, req)
	if err != nil {
		return fmt.Errorf("error initially updating pod list: %w", err)
	}

	scrapeInterval := cAdvisorPodListDefaultInterval
	if p.PodScrapeInterval != 0 {
		scrapeInterval = p.PodScrapeInterval
	}

	for {
		select {
		case <-ctx.Done():
			return nil
		case <-time.After(time.Duration(scrapeInterval) * time.Second):
			err := updateCadvisorPodList(p, req)
			if err != nil {
				return fmt.Errorf("error updating pod list: %w", err)
			}
		}
	}
}

func updateCadvisorPodList(p *Prometheus, req *http.Request) error {
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	httpClient := http.Client{}

	resp, err := httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("error when making request for pod list: %w", err)
	}

	// If err is nil, still check response code
	if resp.StatusCode != 200 {
		return fmt.Errorf("error when making request for pod list with status %s", resp.Status)
	}

	defer resp.Body.Close()

	cadvisorPodsResponse := podResponse{}

	// Will have expected type errors for some parts of corev1.Pod struct for some unused fields
	// Instead have nil checks for every used field in case of incorrect decoding
	if err := json.NewDecoder(resp.Body).Decode(&cadvisorPodsResponse); err != nil {
		return fmt.Errorf("decoding response failed: %v", err)
	}
	pods := cadvisorPodsResponse.Items

	// Updating pod list to be latest cadvisor response
	p.lock.Lock()
	p.kubernetesPods = make(map[string]URLAndAddress)

	// Register pod only if it has an annotation to scrape, if it is ready,
	// and if namespace and selectors are specified and match
	for _, pod := range pods {
		if necessaryPodFieldsArePresent(pod) &&
			pod.Annotations["prometheus.io/scrape"] == "true" &&
			podReady(pod.Status.ContainerStatuses) &&
			podHasMatchingNamespace(pod, p) &&
			podHasMatchingLabelSelector(pod, p.podLabelSelector) &&
			podHasMatchingFieldSelector(pod, p.podFieldSelector) {
			registerPod(pod, p)
		}
	}
	p.lock.Unlock()

	// No errors
	return nil
}

func necessaryPodFieldsArePresent(pod *corev1.Pod) bool {
	return pod.Annotations != nil &&
		pod.Labels != nil &&
		pod.Status.ContainerStatuses != nil
}

/* See the docs on kubernetes label selectors:
 * https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
 */
func podHasMatchingLabelSelector(pod *corev1.Pod, labelSelector labels.Selector) bool {
	if labelSelector == nil {
		return true
	}

	var labelsSet labels.Set = pod.Labels
	return labelSelector.Matches(labelsSet)
}

/* See ToSelectableFields() for list of fields that are selectable:
 * https://github.com/kubernetes/kubernetes/release-1.20/pkg/registry/core/pod/strategy.go
 * See docs on kubernetes field selectors:
 * https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/
 */
func podHasMatchingFieldSelector(pod *corev1.Pod, fieldSelector fields.Selector) bool {
	if fieldSelector == nil {
		return true
	}

	fieldsSet := make(fields.Set)
	fieldsSet["spec.nodeName"] = pod.Spec.NodeName
	fieldsSet["spec.restartPolicy"] = string(pod.Spec.RestartPolicy)
	fieldsSet["spec.schedulerName"] = pod.Spec.SchedulerName
	fieldsSet["spec.serviceAccountName"] = pod.Spec.ServiceAccountName
	fieldsSet["status.phase"] = string(pod.Status.Phase)
	fieldsSet["status.podIP"] = pod.Status.PodIP
	fieldsSet["status.nominatedNodeName"] = pod.Status.NominatedNodeName

	return fieldSelector.Matches(fieldsSet)
}

/*
 * If a namespace is specified and the pod doesn't have that namespace, return false
 * Else return true
 */
func podHasMatchingNamespace(pod *corev1.Pod, p *Prometheus) bool {
	return !(p.PodNamespace != "" && pod.Namespace != p.PodNamespace)
}

func podReady(statuss []corev1.ContainerStatus) bool {
	if len(statuss) == 0 {
		return false
	}
	for _, cs := range statuss {
		if !cs.Ready {
			return false
		}
	}
	return true
}

func registerPod(pod *corev1.Pod, p *Prometheus) {
	if p.kubernetesPods == nil {
		p.kubernetesPods = map[string]URLAndAddress{}
	}
	targetURL, err := getScrapeURL(pod)
	if err != nil {
		p.Log.Errorf("could not parse URL: %s", err)
		return
	} else if targetURL == nil {
		return
	}

	p.Log.Debugf("will scrape metrics from %q", targetURL.String())
	// add annotation as metrics tags
	tags := pod.Annotations
	if tags == nil {
		tags = map[string]string{}
	}
	tags["pod_name"] = pod.Name
	tags["namespace"] = pod.Namespace
	// add labels as metrics tags
	for k, v := range pod.Labels {
		tags[k] = v
	}
	podURL := p.AddressToURL(targetURL, targetURL.Hostname())

	// Locks earlier if using cAdvisor calls - makes a new list each time
	// rather than updating and removing from the same list
	if !p.isNodeScrapeScope {
		p.lock.Lock()
		defer p.lock.Unlock()
	}
	p.kubernetesPods[podURL.String()] = URLAndAddress{
		URL:         podURL,
		Address:     targetURL.Hostname(),
		OriginalURL: targetURL,
		Tags:        tags,
	}
}

func getScrapeURL(pod *corev1.Pod) (*url.URL, error) {
	ip := pod.Status.PodIP
	if ip == "" {
		// return as if scrape was disabled, we will be notified again once the pod
		// has an IP
		return nil, nil
	}

	scheme := pod.Annotations["prometheus.io/scheme"]
	pathAndQuery := pod.Annotations["prometheus.io/path"]
	port := pod.Annotations["prometheus.io/port"]

	if scheme == "" {
		scheme = "http"
	}
	if port == "" {
		port = "9102"
	}
	if pathAndQuery == "" {
		pathAndQuery = "/metrics"
	}

	base, err := url.Parse(pathAndQuery)
	if err != nil {
		return nil, err
	}

	base.Scheme = scheme
	base.Host = net.JoinHostPort(ip, port)

	return base, nil
}

func unregisterPod(pod *corev1.Pod, p *Prometheus) {
	targetURL, err := getScrapeURL(pod)
	if err != nil {
		p.Log.Errorf("failed to parse url: %s", err)
		return
	} else if targetURL == nil {
		return
	}

	p.Log.Debugf("registered a delete request for %q in namespace %q", pod.Name, pod.Namespace)

	p.lock.Lock()
	defer p.lock.Unlock()
	if _, ok := p.kubernetesPods[targetURL.String()]; ok {
		delete(p.kubernetesPods, targetURL.String())
		p.Log.Debugf("will stop scraping for %q", targetURL.String())
	}
}
<|endoftext|>


<commit_before>package system

import (
	"fmt"
	"runtime"
	"testing"

	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestProcesses(t *testing.T) {
	processes := &Processes{
		execPS:       execPS,
		readProcFile: readProcFile,
	}
	var acc testutil.Accumulator

	err := processes.Gather(&acc)
	require.NoError(t, err)

	assert.True(t, acc.HasInt64Field("processes", "running"))
	assert.True(t, acc.HasInt64Field("processes", "sleeping"))
	assert.True(t, acc.HasInt64Field("processes", "stopped"))
	assert.True(t, acc.HasInt64Field("processes", "total"))
	total, ok := acc.Get("processes")
	require.True(t, ok)
	assert.True(t, total.Fields["total"].(int64) > 0)
}

func TestFromPS(t *testing.T) {
	processes := &Processes{
		execPS:  testExecPS,
		forcePS: true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.NoError(t, err)

	fields := getEmptyFields()
	fields["blocked"] = int64(4)
	fields["zombies"] = int64(1)
	fields["running"] = int64(4)
	fields["sleeping"] = int64(34)
	fields["total"] = int64(43)

	acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
}

func TestFromPSError(t *testing.T) {
	processes := &Processes{
		execPS:  testExecPSError,
		forcePS: true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.Error(t, err)
}

func TestFromProcFiles(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skip("This test only runs on linux")
	}
	tester := tester{}
	processes := &Processes{
		readProcFile: tester.testProcFile,
		forceProc:    true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.NoError(t, err)

	fields := getEmptyFields()
	fields["sleeping"] = tester.calls
	fields["total_threads"] = tester.calls * 2
	fields["total"] = tester.calls

	acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
}

func TestFromProcFilesWithSpaceInCmd(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skip("This test only runs on linux")
	}
	tester := tester{}
	processes := &Processes{
		readProcFile: tester.testProcFile2,
		forceProc:    true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.NoError(t, err)

	fields := getEmptyFields()
	fields["sleeping"] = tester.calls
	fields["total_threads"] = tester.calls * 2
	fields["total"] = tester.calls

	acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
}

func testExecPS() ([]byte, error) {
	return []byte(testPSOut), nil
}

// struct for counting calls to testProcFile
type tester struct {
	calls int64
}

func (t *tester) testProcFile(_ string) ([]byte, error) {
	t.calls++
	return []byte(fmt.Sprintf(testProcStat, "S", "2")), nil
}

func (t *tester) testProcFile2(_ string) ([]byte, error) {
	t.calls++
	return []byte(fmt.Sprintf(testProcStat2, "S", "2")), nil
}

func testExecPSError() ([]byte, error) {
	return []byte(testPSOut), fmt.Errorf("ERROR!")
}

const testPSOut = `
STAT
S
S
S
S
R
R
S
S
Ss
Ss
S
SNs
Ss
Ss
S
R+
S
U
S
S
S
S
Ss
S+
Ss
S
S+
S+
Ss
S+
Ss
S
R+
Ss
S
S+
S+
Ss
L
U
Z
D
S+
`

const testProcStat = `10 (rcuob/0) %s 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 %s 0 11 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0
`

const testProcStat2 = `10 (rcuob 0) %s 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 %s 0 11 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0
`
<commit_msg>Add idle state to processes test<commit_after>package system

import (
	"fmt"
	"runtime"
	"testing"

	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestProcesses(t *testing.T) {
	processes := &Processes{
		execPS:       execPS,
		readProcFile: readProcFile,
	}
	var acc testutil.Accumulator

	err := processes.Gather(&acc)
	require.NoError(t, err)

	assert.True(t, acc.HasInt64Field("processes", "running"))
	assert.True(t, acc.HasInt64Field("processes", "sleeping"))
	assert.True(t, acc.HasInt64Field("processes", "stopped"))
	assert.True(t, acc.HasInt64Field("processes", "total"))
	total, ok := acc.Get("processes")
	require.True(t, ok)
	assert.True(t, total.Fields["total"].(int64) > 0)
}

func TestFromPS(t *testing.T) {
	processes := &Processes{
		execPS:  testExecPS,
		forcePS: true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.NoError(t, err)

	fields := getEmptyFields()
	fields["blocked"] = int64(4)
	fields["zombies"] = int64(1)
	fields["running"] = int64(4)
	fields["sleeping"] = int64(34)
	fields["idle"] = int64(2)
	fields["total"] = int64(45)

	acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
}

func TestFromPSError(t *testing.T) {
	processes := &Processes{
		execPS:  testExecPSError,
		forcePS: true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.Error(t, err)
}

func TestFromProcFiles(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skip("This test only runs on linux")
	}
	tester := tester{}
	processes := &Processes{
		readProcFile: tester.testProcFile,
		forceProc:    true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.NoError(t, err)

	fields := getEmptyFields()
	fields["sleeping"] = tester.calls
	fields["total_threads"] = tester.calls * 2
	fields["total"] = tester.calls

	acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
}

func TestFromProcFilesWithSpaceInCmd(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skip("This test only runs on linux")
	}
	tester := tester{}
	processes := &Processes{
		readProcFile: tester.testProcFile2,
		forceProc:    true,
	}

	var acc testutil.Accumulator
	err := processes.Gather(&acc)
	require.NoError(t, err)

	fields := getEmptyFields()
	fields["sleeping"] = tester.calls
	fields["total_threads"] = tester.calls * 2
	fields["total"] = tester.calls

	acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
}

func testExecPS() ([]byte, error) {
	return []byte(testPSOut), nil
}

// struct for counting calls to testProcFile
type tester struct {
	calls int64
}

func (t *tester) testProcFile(_ string) ([]byte, error) {
	t.calls++
	return []byte(fmt.Sprintf(testProcStat, "S", "2")), nil
}

func (t *tester) testProcFile2(_ string) ([]byte, error) {
	t.calls++
	return []byte(fmt.Sprintf(testProcStat2, "S", "2")), nil
}

func testExecPSError() ([]byte, error) {
	return []byte(testPSOut), fmt.Errorf("ERROR!")
}

const testPSOut = `
STAT
S
S
S
S
R
R
S
S
Ss
Ss
S
SNs
Ss
Ss
S
R+
S
U
S
S
S
S
Ss
S+
Ss
S
S+
S+
Ss
S+
Ss
S
R+
Ss
S
S+
S+
Ss
L
U
Z
D
S+
I
I
`

const testProcStat = `10 (rcuob/0) %s 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 %s 0 11 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0
`

const testProcStat2 = `10 (rcuob 0) %s 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 %s 0 11 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0
`
<|endoftext|>


<commit_before>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package formatter

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/parser"
	"github.com/serulian/compiler/sourceshape"
	"github.com/stretchr/testify/assert"
)

// ignoredNodeTypes defines the list of node types that can be skipped in the formatting
// tests.
var ignoredNodeTypes = []sourceshape.NodeType{
	// SKIPPED: Parser currently cannot parse >>, as it conflicts with generic type refs.
	sourceshape.NodeBitwiseShiftRightExpression,
}

type goldenTest struct {
	name     string
	filename string
}

func (ft *goldenTest) input() []byte {
	b, err := ioutil.ReadFile(fmt.Sprintf("tests/%s.in.seru", ft.filename))
	if err != nil {
		panic(err)
	}

	return b
}

func (ft *goldenTest) output() []byte {
	b, err := ioutil.ReadFile(fmt.Sprintf("tests/%s.out.seru", ft.filename))
	if err != nil {
		panic(err)
	}

	return b
}

func (ft *goldenTest) writeFormatted(value []byte) {
	err := ioutil.WriteFile(fmt.Sprintf("tests/%s.out.seru", ft.filename), value, 0644)
	if err != nil {
		panic(err)
	}
}

var goldenTests = []goldenTest{
	{"basic test", "basic"},
	{"comments test", "comments"},
	{"unary precedence test", "unary"},
	{"binary precedence test", "binary"},
	{"imports test", "imports"},
	{"class test", "class"},
	{"agent test", "agent"},
	{"interface test", "interface"},
	{"struct test", "struct"},
	{"nominal test", "nominal"},
	{"relative imports test", "relative"},
	{"template strings test", "templatestrings"},
	{"match test", "match"},
	{"switch test", "switch"},
	{"typerefs test", "typerefs"},
	{"statements test", "statements"},
	{"expressions test", "expressions"},
	{"nullable precedence test", "nullable"},
	{"sml test", "sml"},
	{"nested sml test", "nestedsml"},
	{"sml text with tag test", "smltextwithtag"},
	{"sml long text test", "smllongtext"},
	{"sml long text 2 test", "smllongtext2"},
	{"simple return test", "simplereturn"},
	{"if-else return test", "ifelsereturn"},
	{"null assert test", "nullassert"},
	{"mappings test", "mappings"},
	{"nested sml loop test", "nestedsmlloop"},
	{"known issue test", "knownissue"},
	{"precedence test", "precedence"},
}

func conductParsing(t *testing.T, test goldenTest, source []byte) (*parseTree, formatterNode) {
	parseTree := newParseTree(source)
	inputSource := compilercommon.InputSource(test.filename)
	rootNode := parser.Parse(parseTree.createAstNode, nil, inputSource, string(source))
	if !assert.Empty(t, parseTree.errors, "Expected no parse errors for test %s", test.name) {
		return nil, formatterNode{}
	}

	return parseTree, rootNode.(formatterNode)
}

func addEncounteredNodeTypes(node formatterNode, encounteredNodeTypes map[sourceshape.NodeType]bool) {
	encounteredNodeTypes[node.GetType()] = true
	for _, child := range node.getAllChildren() {
		addEncounteredNodeTypes(child, encounteredNodeTypes)
	}
}

func TestGolden(t *testing.T) {
	encounteredNodeTypes := map[sourceshape.NodeType]bool{}

	for _, test := range goldenTests {
		if os.Getenv("FILTER") != "" {
			if !strings.Contains(test.name, os.Getenv("FILTER")) {
				continue
			} else {
				fmt.Printf("Matched Test: %v\n", test.name)
			}
		}

		parseTree, rootNode := conductParsing(t, test, test.input())
		if parseTree == nil {
			continue
		}

		if os.Getenv("FILTER") == "" {
			addEncounteredNodeTypes(rootNode, encounteredNodeTypes)
		}

		formatted := buildFormattedSource(parseTree, rootNode, importHandlingInfo{})
		if os.Getenv("REGEN") == "true" {
			test.writeFormatted(formatted)
		} else {
			expected := string(test.output())

			// Make sure the output is equal to that expected.
			if !assert.Equal(t, expected, string(formatted), test.name) {
				t.Log(string(formatted))
			}

			// Process the formatted source again and ensure it doesn't change.
			reparsedTree, reparsedRootNode := conductParsing(t, test, formatted)
			if reparsedTree == nil {
				continue
			}

			formattedAgain := buildFormattedSource(reparsedTree, reparsedRootNode, importHandlingInfo{})
			if !assert.Equal(t, string(formatted), string(formattedAgain), test.name) {
				t.Log(string(formattedAgain))
			}
		}
	}

	// Ensure that all parser node types were encountered. This makes sure that we have handled
	// all formatting cases in our tests. Note that we only run this check if we are not filtering
	// tests, as the filter will almost certainly skip node types we need.
	if os.Getenv("FILTER") == "" {
	outer:
		for i := int(sourceshape.NodeTypeError) + 1; i < int(sourceshape.NodeTypeTagged); i++ {
			current := sourceshape.NodeType(i)
			for _, skipped := range ignoredNodeTypes {
				if current == skipped {
					continue outer
				}
			}

			if _, ok := encounteredNodeTypes[current]; !ok {
				t.Errorf("Missing formatting of %v", current)
			}
		}
	}
}
<commit_msg>Have formatter test check formatting of every source file in the compiler package<commit_after>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package formatter

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/compilerutil"
	"github.com/serulian/compiler/packageloader"
	"github.com/serulian/compiler/parser"
	"github.com/serulian/compiler/sourceshape"
	"github.com/stretchr/testify/assert"
)

// ignoredNodeTypes defines the list of node types that can be skipped in the formatting
// tests.
var ignoredNodeTypes = []sourceshape.NodeType{
	// SKIPPED: Parser currently cannot parse >>, as it conflicts with generic type refs.
	sourceshape.NodeBitwiseShiftRightExpression,
}

type goldenTest struct {
	name     string
	filename string
}

func (ft *goldenTest) input() []byte {
	b, err := ioutil.ReadFile(fmt.Sprintf("tests/%s.in.seru", ft.filename))
	if err != nil {
		panic(err)
	}

	return b
}

func (ft *goldenTest) output() []byte {
	b, err := ioutil.ReadFile(fmt.Sprintf("tests/%s.out.seru", ft.filename))
	if err != nil {
		panic(err)
	}

	return b
}

func (ft *goldenTest) writeFormatted(value []byte) {
	err := ioutil.WriteFile(fmt.Sprintf("tests/%s.out.seru", ft.filename), value, 0644)
	if err != nil {
		panic(err)
	}
}

var goldenTests = []goldenTest{
	{"basic test", "basic"},
	{"comments test", "comments"},
	{"unary precedence test", "unary"},
	{"binary precedence test", "binary"},
	{"imports test", "imports"},
	{"class test", "class"},
	{"agent test", "agent"},
	{"interface test", "interface"},
	{"struct test", "struct"},
	{"nominal test", "nominal"},
	{"relative imports test", "relative"},
	{"template strings test", "templatestrings"},
	{"match test", "match"},
	{"switch test", "switch"},
	{"typerefs test", "typerefs"},
	{"statements test", "statements"},
	{"expressions test", "expressions"},
	{"nullable precedence test", "nullable"},
	{"sml test", "sml"},
	{"nested sml test", "nestedsml"},
	{"sml text with tag test", "smltextwithtag"},
	{"sml long text test", "smllongtext"},
	{"sml long text 2 test", "smllongtext2"},
	{"simple return test", "simplereturn"},
	{"if-else return test", "ifelsereturn"},
	{"null assert test", "nullassert"},
	{"mappings test", "mappings"},
	{"nested sml loop test", "nestedsmlloop"},
	{"known issue test", "knownissue"},
	{"precedence test", "precedence"},
}

func conductParsing(t *testing.T, test goldenTest, source []byte) (*parseTree, formatterNode) {
	parseTree := newParseTree(source)
	inputSource := compilercommon.InputSource(test.filename)
	rootNode := parser.Parse(parseTree.createAstNode, nil, inputSource, string(source))
	if !assert.Empty(t, parseTree.errors, "Expected no parse errors for test %s", test.name) {
		return nil, formatterNode{}
	}

	return parseTree, rootNode.(formatterNode)
}

func addEncounteredNodeTypes(node formatterNode, encounteredNodeTypes map[sourceshape.NodeType]bool) {
	encounteredNodeTypes[node.GetType()] = true
	for _, child := range node.getAllChildren() {
		addEncounteredNodeTypes(child, encounteredNodeTypes)
	}
}

func TestGolden(t *testing.T) {
	encounteredNodeTypes := map[sourceshape.NodeType]bool{}

	for _, test := range goldenTests {
		if os.Getenv("FILTER") != "" {
			if !strings.Contains(test.name, os.Getenv("FILTER")) {
				continue
			} else {
				fmt.Printf("Matched Test: %v\n", test.name)
			}
		}

		parseTree, rootNode := conductParsing(t, test, test.input())
		if parseTree == nil {
			continue
		}

		if os.Getenv("FILTER") == "" {
			addEncounteredNodeTypes(rootNode, encounteredNodeTypes)
		}

		formatted := buildFormattedSource(parseTree, rootNode, importHandlingInfo{})
		if os.Getenv("REGEN") == "true" {
			test.writeFormatted(formatted)
		} else {
			expected := string(test.output())

			// Make sure the output is equal to that expected.
			if !assert.Equal(t, expected, string(formatted), test.name) {
				t.Log(string(formatted))
			}

			// Process the formatted source again and ensure it doesn't change.
			reparsedTree, reparsedRootNode := conductParsing(t, test, formatted)
			if reparsedTree == nil {
				continue
			}

			formattedAgain := buildFormattedSource(reparsedTree, reparsedRootNode, importHandlingInfo{})
			if !assert.Equal(t, string(formatted), string(formattedAgain), test.name) {
				t.Log(string(formattedAgain))
			}
		}
	}

	// Ensure that all parser node types were encountered. This makes sure that we have handled
	// all formatting cases in our tests. Note that we only run this check if we are not filtering
	// tests, as the filter will almost certainly skip node types we need.
	if os.Getenv("FILTER") == "" {
	outer:
		for i := int(sourceshape.NodeTypeError) + 1; i < int(sourceshape.NodeTypeTagged); i++ {
			current := sourceshape.NodeType(i)
			for _, skipped := range ignoredNodeTypes {
				if current == skipped {
					continue outer
				}
			}

			if _, ok := encounteredNodeTypes[current]; !ok {
				t.Errorf("Missing formatting of %v", current)
			}
		}
	}
}

func TestAll(t *testing.T) {
	// For every .seru file found in the entire compiler package, parse, and if parseable, format
	// and parse again. This ensures that formatting never breaks on any test files.
	compilerutil.WalkSourcePath("../...", func(filePath string, info os.FileInfo) (bool, error) {
		if !strings.HasSuffix(info.Name(), sourceshape.SerulianFileExtension) {
			return false, nil
		}

		// Load the source file.
		source, err := ioutil.ReadFile(filePath)
		if err != nil {
			return false, err
		}

		// Parse the source file.
		parseTree := newParseTree(source)
		inputSource := compilercommon.InputSource(filePath)
		rootNode := parser.Parse(parseTree.createAstNode, nil, inputSource, string(source))
		if len(parseTree.errors) > 0 {
			t.Logf("Skipping file %s\n", filePath)
			return true, nil
		}

		t.Logf("Checking file %s\n", filePath)

		// Format the source file.
		formatted := buildFormattedSource(parseTree, rootNode.(formatterNode), importHandlingInfo{})

		// Re-parse the file, and ensure there are no errors.
		formattedParseTree := newParseTree(source)
		parser.Parse(formattedParseTree.createAstNode, nil, inputSource, string(formatted))
		assert.Equal(t, 0, len(parseTree.errors), "Got parse error on formatted source file %s: %v", inputSource, parseTree.errors)
		return true, nil
	}, packageloader.SerulianPackageDirectory)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/pellaeon/goas/v3/logger"
	"github.com/pellaeon/skicka/gdrive"
	"golang.org/x/net/context"
)

var _ fs.Node = (*File)(nil)

type File struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n File) Attr(ctx context.Context, attr *fuse.Attr) error {
	logger.Debugf("%s.Attr()", n.sk_file.Path)
	sum := uint64(0)
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	attr.Inode = sum
	attr.Size = uint64(n.sk_file.FileSize)
	attr.Blocks = uint64(n.sk_file.FileSize / 1024) // XXX: block size 1024 bytes
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = os.ModeDir | 0755
	attr.Nlink = 0

	return nil
}

var _ fs.NodeOpener = (*File)(nil)

func (n File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	resp.Flags |= fuse.OpenNonSeekable
	return &FileHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.Handle = (*FileHandle)(nil)

type FileHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReleaser = (*FileHandle)(nil)

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	return nil
}

var _ fs.HandleReader = (*FileHandle)(nil)

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	logger.Debugf("FileHandle Read()")
	buf := make([]byte, req.Size)
	reader, err := gd.GetFileContents(fh.sk_file)
	if err != nil {
		log.Panicf("FileHandle Read GetFileContents failed: %v", err)
	}
	n, err := reader.Read(buf)
	if err != nil {
		log.Panicf("Filehandle Read reader.Read failed: %v", err)
	}
	resp.Data = buf[:n]
	return err
}

var _ fs.NodeAccesser = (*File)(nil)

func (f *File) Access(ctx context.Context, req *fuse.AccessRequest) error {
	logger.Debugf(f.sk_file.Path + " Access()")
	return nil
}
<commit_msg>Fix# file showing up as directory<commit_after>package main

import (
	"log"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/pellaeon/goas/v3/logger"
	"github.com/pellaeon/skicka/gdrive"
	"golang.org/x/net/context"
)

var _ fs.Node = (*File)(nil)

type File struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n File) Attr(ctx context.Context, attr *fuse.Attr) error {
	logger.Debugf("%s.Attr()", n.sk_file.Path)
	sum := uint64(0)
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	attr.Inode = sum
	attr.Size = uint64(n.sk_file.FileSize)
	attr.Blocks = uint64(n.sk_file.FileSize / 1024) // XXX: block size 1024 bytes
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = 0600
	attr.Nlink = 0

	return nil
}

var _ fs.NodeOpener = (*File)(nil)

func (n File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	resp.Flags |= fuse.OpenNonSeekable
	return &FileHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.Handle = (*FileHandle)(nil)

type FileHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReleaser = (*FileHandle)(nil)

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	return nil
}

var _ fs.HandleReader = (*FileHandle)(nil)

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	logger.Debugf("FileHandle Read()")
	buf := make([]byte, req.Size)
	reader, err := gd.GetFileContents(fh.sk_file)
	if err != nil {
		log.Panicf("FileHandle Read GetFileContents failed: %v", err)
	}
	n, err := reader.Read(buf)
	if err != nil {
		log.Panicf("Filehandle Read reader.Read failed: %v", err)
	}
	resp.Data = buf[:n]
	return err
}

var _ fs.NodeAccesser = (*File)(nil)

func (f *File) Access(ctx context.Context, req *fuse.AccessRequest) error {
	logger.Debugf(f.sk_file.Path + " Access()")
	return nil
}
<|endoftext|>


<commit_before>// Copyright ©2012 The bíogo.bam Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bam

// A Flags represents a BAM record's alignment FLAG field.
type Flags uint16

const (
	Paired       Flags = 1 << iota // The read is paired in sequencing, no matter whether it is mapped in a pair.
	ProperPair                     // The read is mapped in a proper pair.
	Unmapped                       // The read itself is unmapped; conflictive with ProperPair.
	MateUnmapped                   // The mate is unmapped.
	Reverse                        // The read is mapped to the reverse strand.
	MateReverse                    // The mate is mapped to the reverse strand.
	Read1                          // This is read1.
	Read2                          // This is read2.
	Secondary                      // Not primary alignment.
	QCFail                         // QC failure.
	Duplicate                      // Optical or PCR duplicate.
)

// String representation of BAM alignment flags:
//  0x001 - p - Paired
//  0x002 - P - ProperPair
//  0x004 - u - Unmapped
//  0x008 - U - MateUnmapped
//  0x010 - r - Reverse
//  0x020 - R - MateReverse
//  0x040 - 1 - Read1
//  0x080 - 2 - Read2
//  0x100 - s - Secondary
//  0x200 - f - QCFail
//  0x400 - d - Duplicate
//
// Note that flag bits are represented high order to the right.
func (f Flags) String() string {
	// If 0x01 is unset, no assumptions can be made about 0x02, 0x08, 0x20, 0x40 and 0x80
	const pairedMask = ProperPair | MateUnmapped | MateReverse | MateReverse | Read1 | Read2
	if f&1 == 0 {
		f &^= pairedMask
	}

	const flags = "pPuUrR12sfd"

	b := make([]byte, len(flags))
	for i, c := range flags {
		if f&(1<<uint(i)) != 0 {
			b[i] = byte(c)
		} else {
			b[i] = '-'
		}
	}

	return string(b)
}
<commit_msg>Add Supplementary flag<commit_after>// Copyright ©2012 The bíogo.bam Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bam

// A Flags represents a BAM record's alignment FLAG field.
type Flags uint16

const (
	Paired        Flags = 1 << iota // The read is paired in sequencing, no matter whether it is mapped in a pair.
	ProperPair                      // The read is mapped in a proper pair.
	Unmapped                        // The read itself is unmapped; conflictive with ProperPair.
	MateUnmapped                    // The mate is unmapped.
	Reverse                         // The read is mapped to the reverse strand.
	MateReverse                     // The mate is mapped to the reverse strand.
	Read1                           // This is read1.
	Read2                           // This is read2.
	Secondary                       // Not primary alignment.
	QCFail                          // QC failure.
	Duplicate                       // Optical or PCR duplicate.
	Supplementary                   // Supplementary alignment, indicates alignment is part of a chimeric alignment.
)

// String representation of BAM alignment flags:
//  0x001 - p - Paired
//  0x002 - P - ProperPair
//  0x004 - u - Unmapped
//  0x008 - U - MateUnmapped
//  0x010 - r - Reverse
//  0x020 - R - MateReverse
//  0x040 - 1 - Read1
//  0x080 - 2 - Read2
//  0x100 - s - Secondary
//  0x200 - f - QCFail
//  0x400 - d - Duplicate
//  0x800 - S - Supplementary
//
// Note that flag bits are represented high order to the right.
func (f Flags) String() string {
	// If 0x01 is unset, no assumptions can be made about 0x02, 0x08, 0x20, 0x40 and 0x80
	const pairedMask = ProperPair | MateUnmapped | MateReverse | MateReverse | Read1 | Read2
	if f&1 == 0 {
		f &^= pairedMask
	}

	const flags = "pPuUrR12sfdS"

	b := make([]byte, len(flags))
	for i, c := range flags {
		if f&(1<<uint(i)) != 0 {
			b[i] = byte(c)
		} else {
			b[i] = '-'
		}
	}

	return string(b)
}
<|endoftext|>


<commit_before>package main

import (
  "database/sql"
  "html/template"
  "log"
	"net/http"
	"os"
  "regexp"
  "github.com/coopernurse/gorp"
  _ "github.com/lib/pq"
)
var dbmap *gorp.DbMap

type Page struct {
  Id int64 `db:"post_id"`
  Title string
  Body string
}

func handler(w http.ResponseWriter, r *http.Request) {
  re := regexp.MustCompile("^/([^/]*)/?([^/]*)$")
  params := re.FindStringSubmatch(r.URL.Path)
  wiki := params[1]
  page := params[2]

  if wiki == "" {
    t, _ :=  template.ParseFiles("index.html")
    t.Execute(w, nil)
  } else if page == "" {
    count, err := dbmap.SelectInt("select count(*) from posts")
    checkErr(err, "select count(*) failed")
    log.Println(count, "pages")

    p := &Page{Title: wiki, Body: ""}
    t, _ := template.ParseFiles("page.html")
    t.Execute(w, p)
  } else {
    p1 := newPage(page, "")
    err := dbmap.Insert(&p1)
    checkErr(err, "Insert failed")

    p := &Page{Title: page, Body: ""}
    t, _ := template.ParseFiles("page.html")
    t.Execute(w, p)
  }
}

func initDb() *gorp.DbMap {
  db, err := sql.Open("postgres", "user=zakuni dbname=foon sslmode=disable")
  checkErr(err, "sql.Open failed")

  dbmap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}
  dbmap.AddTableWithName(Page{}, "posts").SetKeys(true, "Id")

  err = dbmap.CreateTablesIfNotExists()
  checkErr(err, "Create tables failed")

  return dbmap
}

func newPage(title, body string) Page {
  return Page{
    Title: title,
    Body:  body,
  }
}

func checkErr(err error, msg string) {
  if err != nil {
    log.Fatalln(msg, err)
  }
}

func main() {
  dbmap = initDb()
  defer dbmap.Db.Close()

	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}
<commit_msg>go fmt was not correctly working<commit_after>package main

import (
	"database/sql"
	"html/template"
	"log"
	"net/http"
	"os"
	"regexp"

	"github.com/coopernurse/gorp"
	_ "github.com/lib/pq"
)

var dbmap *gorp.DbMap

type Wiki struct {
	Id    int64 `db:"wiki_id"`
	Title string
}

type Page struct {
	Id     int64 `db:"post_id"`
	Title  string
	Body   string
	WikiId int64
}

func handler(w http.ResponseWriter, r *http.Request) {
	re := regexp.MustCompile("^/([^/]*)/?([^/]*)$")
	params := re.FindStringSubmatch(r.URL.Path)
	wiki := params[1]
	page := params[2]

	if wiki == "" {
		t, _ := template.ParseFiles("index.html")
		t.Execute(w, nil)
	} else if page == "" {
		count, err := dbmap.SelectInt("select count(*) from posts")
		checkErr(err, "select count(*) failed")
		log.Println(count, "pages")

		p := &Page{Title: wiki, Body: ""}
		t, _ := template.ParseFiles("page.html")
		t.Execute(w, p)
	} else {
		p1 := newPage(page, "")
		err := dbmap.Insert(&p1)
		checkErr(err, "Insert failed")

		p := &Page{Title: page, Body: ""}
		t, _ := template.ParseFiles("page.html")
		t.Execute(w, p)
	}
}

func initDb() *gorp.DbMap {
	db, err := sql.Open("postgres", "user=zakuni dbname=foon sslmode=disable")
	checkErr(err, "sql.Open failed")

	dbmap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}
	dbmap.AddTableWithName(Page{}, "posts").SetKeys(true, "Id")

	err = dbmap.CreateTablesIfNotExists()
	checkErr(err, "Create tables failed")

	return dbmap
}

func newPage(title, body string) Page {
	return Page{
		Title: title,
		Body:  body,
	}
}

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatalln(msg, err)
	}
}

func main() {
	dbmap = initDb()
	defer dbmap.Db.Close()

	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}
<|endoftext|>


<commit_before>package xpath

import (
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
)

// The XPath function list.

func predicate(q query) func(NodeNavigator) bool {
	type Predicater interface {
		Test(NodeNavigator) bool
	}
	if p, ok := q.(Predicater); ok {
		return p.Test
	}
	return func(NodeNavigator) bool { return true }
}

// positionFunc is a XPath Node Set functions position().
func positionFunc(q query, t iterator) interface{} {
	var (
		count = 1
		node  = t.Current()
	)
	test := predicate(q)
	for node.MoveToPrevious() {
		if test(node) {
			count++
		}
	}
	return float64(count)
}

// lastFunc is a XPath Node Set functions last().
func lastFunc(q query, t iterator) interface{} {
	var (
		count = 0
		node  = t.Current()
	)
	node.MoveToFirst()
	test := predicate(q)
	for {
		if test(node) {
			count++
		}
		if !node.MoveToNext() {
			break
		}
	}
	return float64(count)
}

// countFunc is a XPath Node Set functions count(node-set).
func countFunc(q query, t iterator) interface{} {
	var count = 0
	test := predicate(q)
	switch typ := q.Evaluate(t).(type) {
	case query:
		for node := typ.Select(t); node != nil; node = typ.Select(t) {
			if test(node) {
				count++
			}
		}
	}
	return float64(count)
}

// sumFunc is a XPath Node Set functions sum(node-set).
func sumFunc(q query, t iterator) interface{} {
	var sum float64
	switch typ := q.Evaluate(t).(type) {
	case query:
		for node := typ.Select(t); node != nil; node = typ.Select(t) {
			if v, err := strconv.ParseFloat(node.Value(), 64); err == nil {
				sum += v
			}
		}
	case float64:
		sum = typ
	case string:
		v, err := strconv.ParseFloat(typ, 64)
		if err != nil {
			panic(errors.New("sum() function argument type must be a node-set or number"))
		}
		sum = v
	}
	return sum
}

func asNumber(t iterator, o interface{}) float64 {
	switch typ := o.(type) {
	case query:
		node := typ.Select(t)
		if node == nil {
			return float64(0)
		}
		if v, err := strconv.ParseFloat(node.Value(), 64); err == nil {
			return v
		}
	case float64:
		return typ
	case string:
		v, err := strconv.ParseFloat(typ, 64)
		if err != nil {
			panic(errors.New("ceiling() function argument type must be a node-set or number"))
		}
		return v
	}
	return 0
}

// ceilingFunc is a XPath Node Set functions ceiling(node-set).
func ceilingFunc(q query, t iterator) interface{} {
	val := asNumber(t, q.Evaluate(t))
	return math.Ceil(val)
}

// floorFunc is a XPath Node Set functions floor(node-set).
func floorFunc(q query, t iterator) interface{} {
	val := asNumber(t, q.Evaluate(t))
	return math.Floor(val)
}

// roundFunc is a XPath Node Set functions round(node-set).
func roundFunc(q query, t iterator) interface{} {
	val := asNumber(t, q.Evaluate(t))
	//return math.Round(val)
	return round(val)
}

// nameFunc is a XPath functions name([node-set]).
func nameFunc(q query, t iterator) interface{} {
	v := q.Select(t)
	if v == nil {
		return ""
	}
	ns := v.Prefix()
	if ns == "" {
		return v.LocalName()
	}
	return ns + ":" + v.LocalName()
}

// localNameFunc is a XPath functions local-name([node-set]).
func localNameFunc(q query, t iterator) interface{} {
	v := q.Select(t)
	if v == nil {
		return ""
	}
	return v.LocalName()
}

// namespaceFunc is a XPath functions namespace-uri([node-set]).
func namespaceFunc(q query, t iterator) interface{} {
	v := q.Select(t)
	if v == nil {
		return ""
	}
	return v.Prefix()
}

func asBool(t iterator, v interface{}) bool {
	switch v := v.(type) {
	case nil:
		return false
	case *NodeIterator:
		return v.MoveNext()
	case bool:
		return bool(v)
	case float64:
		return v != 0
	case string:
		return v != ""
	case query:
		return v.Select(t) != nil
	default:
		panic(fmt.Errorf("unexpected type: %T", v))
	}
}

func asString(t iterator, v interface{}) string {
	switch v := v.(type) {
	case nil:
		return ""
	case bool:
		if v {
			return "true"
		}
		return "false"
	case float64:
		return strconv.FormatFloat(v, 'g', -1, 64)
	case string:
		return v
	default:
		panic(fmt.Errorf("unexpected type: %T", v))
	}
}

// booleanFunc is a XPath functions boolean([node-set]).
func booleanFunc(q query, t iterator) interface{} {
	v := q.Evaluate(t)
	return asBool(t, v)
}

// numberFunc is a XPath functions number([node-set]).
func numberFunc(q query, t iterator) interface{} {
	v := q.Evaluate(t)
	return asNumber(t, v)
}

// stringFunc is a XPath functions string([node-set]).
func stringFunc(q query, t iterator) interface{} {
	v := q.Evaluate(t)
	return asString(t, v)
}

// startwithFunc is a XPath functions starts-with(string, string).
func startwithFunc(arg1, arg2 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var (
			m, n string
			ok   bool
		)
		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return false
			}
			m = node.Value()
		default:
			panic(errors.New("starts-with() function argument type must be string"))
		}
		n, ok = arg2.Evaluate(t).(string)
		if !ok {
			panic(errors.New("starts-with() function argument type must be string"))
		}
		return strings.HasPrefix(m, n)
	}
}

// endwithFunc is a XPath functions ends-with(string, string).
func endwithFunc(arg1, arg2 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var (
			m, n string
			ok   bool
		)
		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return false
			}
			m = node.Value()
		default:
			panic(errors.New("ends-with() function argument type must be string"))
		}
		n, ok = arg2.Evaluate(t).(string)
		if !ok {
			panic(errors.New("ends-with() function argument type must be string"))
		}
		return strings.HasSuffix(m, n)
	}
}

// containsFunc is a XPath functions contains(string or @attr, string).
func containsFunc(arg1, arg2 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var (
			m, n string
			ok   bool
		)

		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return false
			}
			m = node.Value()
		default:
			panic(errors.New("contains() function argument type must be string"))
		}

		n, ok = arg2.Evaluate(t).(string)
		if !ok {
			panic(errors.New("contains() function argument type must be string"))
		}

		return strings.Contains(m, n)
	}
}

// normalizespaceFunc is XPath functions normalize-space(string?)
func normalizespaceFunc(q query, t iterator) interface{} {
	var m string
	switch typ := q.Evaluate(t).(type) {
	case string:
		m = typ
	case query:
		node := typ.Select(t)
		if node == nil {
			return false
		}
		m = node.Value()
	}
	return strings.TrimSpace(m)
}

// substringFunc is XPath functions substring function returns a part of a given string.
func substringFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var m string
		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return ""
			}
			m = node.Value()
		}

		var start, length float64
		var ok bool

		if start, ok = arg2.Evaluate(t).(float64); !ok {
			panic(errors.New("substring() function first argument type must be int"))
		} else if start < 1 {
			panic(errors.New("substring() function first argument type must be >= 1"))
		}
		start--
		if arg3 != nil {
			if length, ok = arg3.Evaluate(t).(float64); !ok {
				panic(errors.New("substring() function second argument type must be int"))
			}
		}
		if (len(m) - int(start)) < int(length) {
			panic(errors.New("substring() function start and length argument out of range"))
		}
		if length > 0 {
			return m[int(start):int(length+start)]
		}
		return m[int(start):]
	}
}

// substringIndFunc is XPath functions substring-before/substring-after function returns a part of a given string.
func substringIndFunc(arg1, arg2 query, after bool) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var str string
		switch v := arg1.Evaluate(t).(type) {
		case string:
			str = v
		case query:
			node := v.Select(t)
			if node == nil {
				return ""
			}
			str = node.Value()
		}
		var word string
		switch v := arg2.Evaluate(t).(type) {
		case string:
			word = v
		case query:
			node := v.Select(t)
			if node == nil {
				return ""
			}
			word = node.Value()
		}
		if word == "" {
			return ""
		}

		i := strings.Index(str, word)
		if i < 0 {
			return ""
		}
		if after {
			return str[i+len(word):]
		}
		return str[:i]
	}
}

// stringLengthFunc is XPATH string-length( [string] ) function that returns a number
// equal to the number of characters in a given string.
func stringLengthFunc(arg1 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		switch v := arg1.Evaluate(t).(type) {
		case string:
			return float64(len(v))
		case query:
			node := v.Select(t)
			if node == nil {
				break
			}
			return float64(len(node.Value()))
		}
		return float64(0)
	}
}

// translateFunc is XPath functions translate() function returns a replaced string.
func translateFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		str := asString(t, arg1.Evaluate(t))
		src := asString(t, arg2.Evaluate(t))
		dst := asString(t, arg3.Evaluate(t))

		var replace []string
		for i, s := range src {
			d := ""
			if i < len(dst) {
				d = string(dst[i])
			}
			replace = append(replace, string(s), d)
		}
		return strings.NewReplacer(replace...).Replace(str)
	}
}

// notFunc is XPATH functions not(expression) function operation.
func notFunc(q query, t iterator) interface{} {
	switch v := q.Evaluate(t).(type) {
	case bool:
		return !v
	case query:
		node := v.Select(t)
		return node == nil
	default:
		return false
	}
}

// concatFunc is the concat function concatenates two or more
// strings and returns the resulting string.
// concat( string1 , string2 [, stringn]* )
func concatFunc(args ...query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var a []string
		for _, v := range args {
			switch v := v.Evaluate(t).(type) {
			case string:
				a = append(a, v)
			case query:
				node := v.Select(t)
				if node != nil {
					a = append(a, node.Value())
				}
			}
		}
		return strings.Join(a, "")
	}
}
<commit_msg>fix #28<commit_after>package xpath

import (
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
)

// The XPath function list.

func predicate(q query) func(NodeNavigator) bool {
	type Predicater interface {
		Test(NodeNavigator) bool
	}
	if p, ok := q.(Predicater); ok {
		return p.Test
	}
	return func(NodeNavigator) bool { return true }
}

// positionFunc is a XPath Node Set functions position().
func positionFunc(q query, t iterator) interface{} {
	var (
		count = 1
		node  = t.Current()
	)
	test := predicate(q)
	for node.MoveToPrevious() {
		if test(node) {
			count++
		}
	}
	return float64(count)
}

// lastFunc is a XPath Node Set functions last().
func lastFunc(q query, t iterator) interface{} {
	var (
		count = 0
		node  = t.Current()
	)
	node.MoveToFirst()
	test := predicate(q)
	for {
		if test(node) {
			count++
		}
		if !node.MoveToNext() {
			break
		}
	}
	return float64(count)
}

// countFunc is a XPath Node Set functions count(node-set).
func countFunc(q query, t iterator) interface{} {
	var count = 0
	test := predicate(q)
	switch typ := q.Evaluate(t).(type) {
	case query:
		for node := typ.Select(t); node != nil; node = typ.Select(t) {
			if test(node) {
				count++
			}
		}
	}
	return float64(count)
}

// sumFunc is a XPath Node Set functions sum(node-set).
func sumFunc(q query, t iterator) interface{} {
	var sum float64
	switch typ := q.Evaluate(t).(type) {
	case query:
		for node := typ.Select(t); node != nil; node = typ.Select(t) {
			if v, err := strconv.ParseFloat(node.Value(), 64); err == nil {
				sum += v
			}
		}
	case float64:
		sum = typ
	case string:
		v, err := strconv.ParseFloat(typ, 64)
		if err != nil {
			panic(errors.New("sum() function argument type must be a node-set or number"))
		}
		sum = v
	}
	return sum
}

func asNumber(t iterator, o interface{}) float64 {
	switch typ := o.(type) {
	case query:
		node := typ.Select(t)
		if node == nil {
			return float64(0)
		}
		if v, err := strconv.ParseFloat(node.Value(), 64); err == nil {
			return v
		}
	case float64:
		return typ
	case string:
		v, err := strconv.ParseFloat(typ, 64)
		if err != nil {
			panic(errors.New("ceiling() function argument type must be a node-set or number"))
		}
		return v
	}
	return 0
}

// ceilingFunc is a XPath Node Set functions ceiling(node-set).
func ceilingFunc(q query, t iterator) interface{} {
	val := asNumber(t, q.Evaluate(t))
	return math.Ceil(val)
}

// floorFunc is a XPath Node Set functions floor(node-set).
func floorFunc(q query, t iterator) interface{} {
	val := asNumber(t, q.Evaluate(t))
	return math.Floor(val)
}

// roundFunc is a XPath Node Set functions round(node-set).
func roundFunc(q query, t iterator) interface{} {
	val := asNumber(t, q.Evaluate(t))
	//return math.Round(val)
	return round(val)
}

// nameFunc is a XPath functions name([node-set]).
func nameFunc(q query, t iterator) interface{} {
	v := q.Select(t)
	if v == nil {
		return ""
	}
	ns := v.Prefix()
	if ns == "" {
		return v.LocalName()
	}
	return ns + ":" + v.LocalName()
}

// localNameFunc is a XPath functions local-name([node-set]).
func localNameFunc(q query, t iterator) interface{} {
	v := q.Select(t)
	if v == nil {
		return ""
	}
	return v.LocalName()
}

// namespaceFunc is a XPath functions namespace-uri([node-set]).
func namespaceFunc(q query, t iterator) interface{} {
	v := q.Select(t)
	if v == nil {
		return ""
	}
	return v.Prefix()
}

func asBool(t iterator, v interface{}) bool {
	switch v := v.(type) {
	case nil:
		return false
	case *NodeIterator:
		return v.MoveNext()
	case bool:
		return bool(v)
	case float64:
		return v != 0
	case string:
		return v != ""
	case query:
		return v.Select(t) != nil
	default:
		panic(fmt.Errorf("unexpected type: %T", v))
	}
}

func asString(t iterator, v interface{}) string {
	switch v := v.(type) {
	case nil:
		return ""
	case bool:
		if v {
			return "true"
		}
		return "false"
	case float64:
		return strconv.FormatFloat(v, 'g', -1, 64)
	case string:
		return v
	case query:
		node := v.Select(t)
		if node == nil {
			return ""
		}
		return node.Value()
	default:
		panic(fmt.Errorf("unexpected type: %T", v))
	}
}

// booleanFunc is a XPath functions boolean([node-set]).
func booleanFunc(q query, t iterator) interface{} {
	v := q.Evaluate(t)
	return asBool(t, v)
}

// numberFunc is a XPath functions number([node-set]).
func numberFunc(q query, t iterator) interface{} {
	v := q.Evaluate(t)
	return asNumber(t, v)
}

// stringFunc is a XPath functions string([node-set]).
func stringFunc(q query, t iterator) interface{} {
	v := q.Evaluate(t)
	return asString(t, v)
}

// startwithFunc is a XPath functions starts-with(string, string).
func startwithFunc(arg1, arg2 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var (
			m, n string
			ok   bool
		)
		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return false
			}
			m = node.Value()
		default:
			panic(errors.New("starts-with() function argument type must be string"))
		}
		n, ok = arg2.Evaluate(t).(string)
		if !ok {
			panic(errors.New("starts-with() function argument type must be string"))
		}
		return strings.HasPrefix(m, n)
	}
}

// endwithFunc is a XPath functions ends-with(string, string).
func endwithFunc(arg1, arg2 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var (
			m, n string
			ok   bool
		)
		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return false
			}
			m = node.Value()
		default:
			panic(errors.New("ends-with() function argument type must be string"))
		}
		n, ok = arg2.Evaluate(t).(string)
		if !ok {
			panic(errors.New("ends-with() function argument type must be string"))
		}
		return strings.HasSuffix(m, n)
	}
}

// containsFunc is a XPath functions contains(string or @attr, string).
func containsFunc(arg1, arg2 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var (
			m, n string
			ok   bool
		)

		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return false
			}
			m = node.Value()
		default:
			panic(errors.New("contains() function argument type must be string"))
		}

		n, ok = arg2.Evaluate(t).(string)
		if !ok {
			panic(errors.New("contains() function argument type must be string"))
		}

		return strings.Contains(m, n)
	}
}

// normalizespaceFunc is XPath functions normalize-space(string?)
func normalizespaceFunc(q query, t iterator) interface{} {
	var m string
	switch typ := q.Evaluate(t).(type) {
	case string:
		m = typ
	case query:
		node := typ.Select(t)
		if node == nil {
			return false
		}
		m = node.Value()
	}
	return strings.TrimSpace(m)
}

// substringFunc is XPath functions substring function returns a part of a given string.
func substringFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var m string
		switch typ := arg1.Evaluate(t).(type) {
		case string:
			m = typ
		case query:
			node := typ.Select(t)
			if node == nil {
				return ""
			}
			m = node.Value()
		}

		var start, length float64
		var ok bool

		if start, ok = arg2.Evaluate(t).(float64); !ok {
			panic(errors.New("substring() function first argument type must be int"))
		} else if start < 1 {
			panic(errors.New("substring() function first argument type must be >= 1"))
		}
		start--
		if arg3 != nil {
			if length, ok = arg3.Evaluate(t).(float64); !ok {
				panic(errors.New("substring() function second argument type must be int"))
			}
		}
		if (len(m) - int(start)) < int(length) {
			panic(errors.New("substring() function start and length argument out of range"))
		}
		if length > 0 {
			return m[int(start):int(length+start)]
		}
		return m[int(start):]
	}
}

// substringIndFunc is XPath functions substring-before/substring-after function returns a part of a given string.
func substringIndFunc(arg1, arg2 query, after bool) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var str string
		switch v := arg1.Evaluate(t).(type) {
		case string:
			str = v
		case query:
			node := v.Select(t)
			if node == nil {
				return ""
			}
			str = node.Value()
		}
		var word string
		switch v := arg2.Evaluate(t).(type) {
		case string:
			word = v
		case query:
			node := v.Select(t)
			if node == nil {
				return ""
			}
			word = node.Value()
		}
		if word == "" {
			return ""
		}

		i := strings.Index(str, word)
		if i < 0 {
			return ""
		}
		if after {
			return str[i+len(word):]
		}
		return str[:i]
	}
}

// stringLengthFunc is XPATH string-length( [string] ) function that returns a number
// equal to the number of characters in a given string.
func stringLengthFunc(arg1 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		switch v := arg1.Evaluate(t).(type) {
		case string:
			return float64(len(v))
		case query:
			node := v.Select(t)
			if node == nil {
				break
			}
			return float64(len(node.Value()))
		}
		return float64(0)
	}
}

// translateFunc is XPath functions translate() function returns a replaced string.
func translateFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		str := asString(t, arg1.Evaluate(t))
		src := asString(t, arg2.Evaluate(t))
		dst := asString(t, arg3.Evaluate(t))

		var replace []string
		for i, s := range src {
			d := ""
			if i < len(dst) {
				d = string(dst[i])
			}
			replace = append(replace, string(s), d)
		}
		return strings.NewReplacer(replace...).Replace(str)
	}
}

// notFunc is XPATH functions not(expression) function operation.
func notFunc(q query, t iterator) interface{} {
	switch v := q.Evaluate(t).(type) {
	case bool:
		return !v
	case query:
		node := v.Select(t)
		return node == nil
	default:
		return false
	}
}

// concatFunc is the concat function concatenates two or more
// strings and returns the resulting string.
// concat( string1 , string2 [, stringn]* )
func concatFunc(args ...query) func(query, iterator) interface{} {
	return func(q query, t iterator) interface{} {
		var a []string
		for _, v := range args {
			switch v := v.Evaluate(t).(type) {
			case string:
				a = append(a, v)
			case query:
				node := v.Select(t)
				if node != nil {
					a = append(a, node.Value())
				}
			}
		}
		return strings.Join(a, "")
	}
}
<|endoftext|>


<commit_before>package luar

import (
	"reflect"

	"github.com/yuin/gopher-lua"
)

// LState is an wrapper for gopher-lua's LState. It should be used when you
// wish to have a function/method with the standard "func(*lua.LState) int"
// signature.
type LState struct {
	*lua.LState
}

var (
	refTypeLStatePtr  reflect.Type
	refTypeLuaLValue  reflect.Type
	refTypeInt        reflect.Type
	refTypeEmptyIface reflect.Type
)

func init() {
	refTypeLStatePtr = reflect.TypeOf(&LState{})
	refTypeLuaLValue = reflect.TypeOf((*lua.LValue)(nil)).Elem()
	refTypeInt = reflect.TypeOf(int(0))
	refTypeEmptyIface = reflect.TypeOf((*interface{})(nil)).Elem()
}

func getFunc(L *lua.LState) (ref reflect.Value, refType reflect.Type) {
	ref = L.Get(lua.UpvalueIndex(1)).(*lua.LUserData).Value.(reflect.Value)
	refType = ref.Type()
	return
}

func isPtrReceiverMethod(L *lua.LState) bool {
	return bool(L.Get(lua.UpvalueIndex(2)).(lua.LBool))
}

func funcIsBypass(t reflect.Type) bool {
	if t.NumIn() == 1 && t.NumOut() == 1 && t.In(0) == refTypeLStatePtr && t.Out(0) == refTypeInt {
		return true
	}
	if t.NumIn() == 2 && t.NumOut() == 1 && t.In(1) == refTypeLStatePtr && t.Out(0) == refTypeInt {
		return true
	}
	return false
}

func funcBypass(L *lua.LState) int {
	ref, refType := getFunc(L)

	convertedPtr := false
	var receiver reflect.Value
	var ud lua.LValue

	luarState := LState{L}
	args := make([]reflect.Value, 0, 2)
	if refType.NumIn() == 2 {
		receiverHint := refType.In(0)
		ud = L.Get(1)
		var err error
		if isPtrReceiverMethod(L) {
			receiver, err = lValueToReflect(L, ud, receiverHint, &convertedPtr)
		} else {
			receiver, err = lValueToReflect(L, ud, receiverHint, nil)
		}
		if err != nil {
			L.ArgError(1, err.Error())
		}
		args = append(args, receiver)
		L.Remove(1)
	}
	args = append(args, reflect.ValueOf(&luarState))
	ret := ref.Call(args)[0].Interface().(int)
	if convertedPtr {
		ud.(*lua.LUserData).Value = receiver.Elem().Interface()
	}
	return ret
}

func funcRegular(L *lua.LState) int {
	ref, refType := getFunc(L)

	top := L.GetTop()
	expected := refType.NumIn()
	variadic := refType.IsVariadic()
	if !variadic && top != expected {
		L.RaiseError("invalid number of function arguments (%d expected, got %d)", expected, top)
	}
	if variadic && top < expected-1 {
		L.RaiseError("invalid number of function arguments (%d or more expected, got %d)", expected-1, top)
	}

	convertedPtr := false
	var receiver reflect.Value
	var ud lua.LValue

	args := make([]reflect.Value, top)
	for i := 0; i < L.GetTop(); i++ {
		var hint reflect.Type
		if variadic && i >= expected-1 {
			hint = refType.In(expected - 1).Elem()
		} else {
			hint = refType.In(i)
		}
		var arg reflect.Value
		var err error
		if i == 0 && isPtrReceiverMethod(L) {
			ud = L.Get(1)
			v := ud
			arg, err = lValueToReflect(L, v, hint, &convertedPtr)
			if err != nil {
				L.ArgError(1, err.Error())
			}
			receiver = arg
		} else {
			v := L.Get(i + 1)
			arg, err = lValueToReflect(L, v, hint, nil)
			if err != nil {
				L.ArgError(i+1, err.Error())
			}
		}
		args[i] = arg
	}
	ret := ref.Call(args)

	if convertedPtr {
		ud.(*lua.LUserData).Value = receiver.Elem().Interface()
	}

	for _, val := range ret {
		L.Push(New(L, val.Interface()))
	}
	return len(ret)
}

func funcWrapper(L *lua.LState, fn reflect.Value, isPtrReceiverMethod bool) *lua.LFunction {
	up := L.NewUserData()
	up.Value = fn

	if funcIsBypass(fn.Type()) {
		return L.NewClosure(funcBypass, up, lua.LBool(isPtrReceiverMethod))
	}
	return L.NewClosure(funcRegular, up, lua.LBool(isPtrReceiverMethod))
}
<commit_msg>simplify refType* initialization<commit_after>package luar

import (
	"reflect"

	"github.com/yuin/gopher-lua"
)

// LState is an wrapper for gopher-lua's LState. It should be used when you
// wish to have a function/method with the standard "func(*lua.LState) int"
// signature.
type LState struct {
	*lua.LState
}

var (
	refTypeLStatePtr  = reflect.TypeOf(&LState{})
	refTypeLuaLValue  = reflect.TypeOf((*lua.LValue)(nil)).Elem()
	refTypeInt        = reflect.TypeOf(int(0))
	refTypeEmptyIface = reflect.TypeOf((*interface{})(nil)).Elem()
)

func getFunc(L *lua.LState) (ref reflect.Value, refType reflect.Type) {
	ref = L.Get(lua.UpvalueIndex(1)).(*lua.LUserData).Value.(reflect.Value)
	refType = ref.Type()
	return
}

func isPtrReceiverMethod(L *lua.LState) bool {
	return bool(L.Get(lua.UpvalueIndex(2)).(lua.LBool))
}

func funcIsBypass(t reflect.Type) bool {
	if t.NumIn() == 1 && t.NumOut() == 1 && t.In(0) == refTypeLStatePtr && t.Out(0) == refTypeInt {
		return true
	}
	if t.NumIn() == 2 && t.NumOut() == 1 && t.In(1) == refTypeLStatePtr && t.Out(0) == refTypeInt {
		return true
	}
	return false
}

func funcBypass(L *lua.LState) int {
	ref, refType := getFunc(L)

	convertedPtr := false
	var receiver reflect.Value
	var ud lua.LValue

	luarState := LState{L}
	args := make([]reflect.Value, 0, 2)
	if refType.NumIn() == 2 {
		receiverHint := refType.In(0)
		ud = L.Get(1)
		var err error
		if isPtrReceiverMethod(L) {
			receiver, err = lValueToReflect(L, ud, receiverHint, &convertedPtr)
		} else {
			receiver, err = lValueToReflect(L, ud, receiverHint, nil)
		}
		if err != nil {
			L.ArgError(1, err.Error())
		}
		args = append(args, receiver)
		L.Remove(1)
	}
	args = append(args, reflect.ValueOf(&luarState))
	ret := ref.Call(args)[0].Interface().(int)
	if convertedPtr {
		ud.(*lua.LUserData).Value = receiver.Elem().Interface()
	}
	return ret
}

func funcRegular(L *lua.LState) int {
	ref, refType := getFunc(L)

	top := L.GetTop()
	expected := refType.NumIn()
	variadic := refType.IsVariadic()
	if !variadic && top != expected {
		L.RaiseError("invalid number of function arguments (%d expected, got %d)", expected, top)
	}
	if variadic && top < expected-1 {
		L.RaiseError("invalid number of function arguments (%d or more expected, got %d)", expected-1, top)
	}

	convertedPtr := false
	var receiver reflect.Value
	var ud lua.LValue

	args := make([]reflect.Value, top)
	for i := 0; i < L.GetTop(); i++ {
		var hint reflect.Type
		if variadic && i >= expected-1 {
			hint = refType.In(expected - 1).Elem()
		} else {
			hint = refType.In(i)
		}
		var arg reflect.Value
		var err error
		if i == 0 && isPtrReceiverMethod(L) {
			ud = L.Get(1)
			v := ud
			arg, err = lValueToReflect(L, v, hint, &convertedPtr)
			if err != nil {
				L.ArgError(1, err.Error())
			}
			receiver = arg
		} else {
			v := L.Get(i + 1)
			arg, err = lValueToReflect(L, v, hint, nil)
			if err != nil {
				L.ArgError(i+1, err.Error())
			}
		}
		args[i] = arg
	}
	ret := ref.Call(args)

	if convertedPtr {
		ud.(*lua.LUserData).Value = receiver.Elem().Interface()
	}

	for _, val := range ret {
		L.Push(New(L, val.Interface()))
	}
	return len(ret)
}

func funcWrapper(L *lua.LState, fn reflect.Value, isPtrReceiverMethod bool) *lua.LFunction {
	up := L.NewUserData()
	up.Value = fn

	if funcIsBypass(fn.Type()) {
		return L.NewClosure(funcBypass, up, lua.LBool(isPtrReceiverMethod))
	}
	return L.NewClosure(funcRegular, up, lua.LBool(isPtrReceiverMethod))
}
<|endoftext|>


<commit_before>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
)

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{
		Mode:	fuse.S_IFREG | 0644,
		Size:		1024,
	}, fuse.OK
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case "zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		ret := make(chan string)
		zipRequests <- zipRequest{
			Game:	gamename,
			Return:	ret,
		}
		zipname := <-ret
		close(ret)
		if zipname == "" {		// none given
			// TODO handle error
			return nil, fuse.ENOENT
		}
		f, err := os.Open(zipname)
		if err != nil {
			// TODO report error
			return nil, fuse.EIO	// TODO proper error
		}
		// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
		loopfile := new(fuse.LoopbackFile)
		loopfile.File =f
		return loopfile, fuse.OK
	case "chd":				// CHD
		// ...
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// ...
	return nil, fuse.ENOENT	// for now
}<commit_msg>Fix whitespace typo.<commit_after>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
)

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{
		Mode:	fuse.S_IFREG | 0644,
		Size:		1024,
	}, fuse.OK
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case "zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		ret := make(chan string)
		zipRequests <- zipRequest{
			Game:	gamename,
			Return:	ret,
		}
		zipname := <-ret
		close(ret)
		if zipname == "" {		// none given
			// TODO handle error
			return nil, fuse.ENOENT
		}
		f, err := os.Open(zipname)
		if err != nil {
			// TODO report error
			return nil, fuse.EIO	// TODO proper error
		}
		// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
		loopfile := new(fuse.LoopbackFile)
		loopfile.File = f
		return loopfile, fuse.OK
	case "chd":				// CHD
		// ...
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// ...
	return nil, fuse.ENOENT	// for now
}<|endoftext|>


<commit_before>/* Copyright 2019 Google Inc.

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 gonids

// FuzzParseRule is used by OSS-Fuzz to fuzz the library.
func FuzzParseRule(data []byte) int {
	r, err := ParseRule(string(data))
	if err != nil {
		// Handle parse error
		return 0
	}
	r.OptimizeHTTP()
	return 1
}
<commit_msg>Extends fuzz target by recomputing rule string<commit_after>/* Copyright 2019 Google Inc.

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 gonids

// FuzzParseRule is used by OSS-Fuzz to fuzz the library.
func FuzzParseRule(data []byte) int {
	r, err := ParseRule(string(data))
	if err != nil {
		// Handle parse error
		return 0
	}
	r.OptimizeHTTP()
	r.String()
	return 1
}
<|endoftext|>


<commit_before>package router_test

import (
	"os"
	"path/filepath"
	"testing"

	proto "github.com/golang/protobuf/proto"
	"v2ray.com/core/app/router"
	"v2ray.com/core/common"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/platform"
	"v2ray.com/ext/sysio"
)

func TestGeoIPMatcher(t *testing.T) {
	cidrList := router.CIDRList{
		{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
		{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
		{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
		{Ip: []byte{127, 0, 0, 0}, Prefix: 8},
		{Ip: []byte{169, 254, 0, 0}, Prefix: 16},
		{Ip: []byte{172, 16, 0, 0}, Prefix: 12},
		{Ip: []byte{192, 0, 0, 0}, Prefix: 24},
		{Ip: []byte{192, 0, 2, 0}, Prefix: 24},
		{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
		{Ip: []byte{192, 18, 0, 0}, Prefix: 15},
		{Ip: []byte{198, 51, 100, 0}, Prefix: 24},
		{Ip: []byte{203, 0, 113, 0}, Prefix: 24},
		{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
		{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
	}

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(cidrList))

	testCases := []struct {
		Input  string
		Output bool
	}{
		{
			Input:  "192.168.1.1",
			Output: true,
		},
		{
			Input:  "192.0.0.0",
			Output: true,
		},
		{
			Input:  "192.0.1.0",
			Output: false,
		}, {
			Input:  "0.1.0.0",
			Output: true,
		},
		{
			Input:  "1.0.0.1",
			Output: false,
		},
		{
			Input:  "8.8.8.7",
			Output: false,
		},
		{
			Input:  "8.8.8.8",
			Output: true,
		},
		{
			Input:  "2001:cdba::3257:9652",
			Output: false,
		},
		{
			Input:  "91.108.255.254",
			Output: true,
		},
	}

	for _, testCase := range testCases {
		ip := net.ParseAddress(testCase.Input).IP()
		actual := matcher.Match(ip)
		if actual != testCase.Output {
			t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
		}
	}
}

func TestGeoIPMatcher4CN(t *testing.T) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("CN")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	if matcher.Match([]byte{8, 8, 8, 8}) {
		t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
	}
}

func TestGeoIPMatcher6US(t *testing.T) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("US")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
		t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
	}
}

func loadGeoIP(country string) ([]*router.CIDR, error) {
	geoipBytes, err := sysio.ReadAsset("geoip.dat")
	if err != nil {
		return nil, err
	}
	var geoipList router.GeoIPList
	if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
		return nil, err
	}

	for _, geoip := range geoipList.Entry {
		if geoip.CountryCode == country {
			return geoip.Cidr, nil
		}
	}

	panic("country not found: " + country)
}

func BenchmarkGeoIPMatcher4CN(b *testing.B) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("CN")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = matcher.Match([]byte{8, 8, 8, 8})
	}
}

func BenchmarkGeoIPMatcher6US(b *testing.B) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("US")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP())
	}
}
<commit_msg>more test cases<commit_after>package router_test

import (
	"os"
	"path/filepath"
	"testing"

	proto "github.com/golang/protobuf/proto"
	"v2ray.com/core/app/router"
	"v2ray.com/core/common"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/platform"
	"v2ray.com/ext/sysio"
)

func TestGeoIPMatcherContainer(t *testing.T) {
	container := &router.GeoIPMatcherContainer{}

	m1, err := container.Add(&router.GeoIP{
		CountryCode: "CN",
	})
	common.Must(err)

	m2, err := container.Add(&router.GeoIP{
		CountryCode: "US",
	})
	common.Must(err)

	m3, err := container.Add(&router.GeoIP{
		CountryCode: "CN",
	})
	common.Must(err)

	if m1 != m3 {
		t.Error("expect same matcher for same geoip, but not")
	}

	if m1 == m2 {
		t.Error("expect different matcher for different geoip, but actually same")
	}
}

func TestGeoIPMatcher(t *testing.T) {
	cidrList := router.CIDRList{
		{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
		{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
		{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
		{Ip: []byte{127, 0, 0, 0}, Prefix: 8},
		{Ip: []byte{169, 254, 0, 0}, Prefix: 16},
		{Ip: []byte{172, 16, 0, 0}, Prefix: 12},
		{Ip: []byte{192, 0, 0, 0}, Prefix: 24},
		{Ip: []byte{192, 0, 2, 0}, Prefix: 24},
		{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
		{Ip: []byte{192, 18, 0, 0}, Prefix: 15},
		{Ip: []byte{198, 51, 100, 0}, Prefix: 24},
		{Ip: []byte{203, 0, 113, 0}, Prefix: 24},
		{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
		{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
	}

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(cidrList))

	testCases := []struct {
		Input  string
		Output bool
	}{
		{
			Input:  "192.168.1.1",
			Output: true,
		},
		{
			Input:  "192.0.0.0",
			Output: true,
		},
		{
			Input:  "192.0.1.0",
			Output: false,
		}, {
			Input:  "0.1.0.0",
			Output: true,
		},
		{
			Input:  "1.0.0.1",
			Output: false,
		},
		{
			Input:  "8.8.8.7",
			Output: false,
		},
		{
			Input:  "8.8.8.8",
			Output: true,
		},
		{
			Input:  "2001:cdba::3257:9652",
			Output: false,
		},
		{
			Input:  "91.108.255.254",
			Output: true,
		},
	}

	for _, testCase := range testCases {
		ip := net.ParseAddress(testCase.Input).IP()
		actual := matcher.Match(ip)
		if actual != testCase.Output {
			t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
		}
	}
}

func TestGeoIPMatcher4CN(t *testing.T) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("CN")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	if matcher.Match([]byte{8, 8, 8, 8}) {
		t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
	}
}

func TestGeoIPMatcher6US(t *testing.T) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("US")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
		t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
	}
}

func loadGeoIP(country string) ([]*router.CIDR, error) {
	geoipBytes, err := sysio.ReadAsset("geoip.dat")
	if err != nil {
		return nil, err
	}
	var geoipList router.GeoIPList
	if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
		return nil, err
	}

	for _, geoip := range geoipList.Entry {
		if geoip.CountryCode == country {
			return geoip.Cidr, nil
		}
	}

	panic("country not found: " + country)
}

func BenchmarkGeoIPMatcher4CN(b *testing.B) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("CN")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = matcher.Match([]byte{8, 8, 8, 8})
	}
}

func BenchmarkGeoIPMatcher6US(b *testing.B) {
	common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))

	ips, err := loadGeoIP("US")
	common.Must(err)

	matcher := &router.GeoIPMatcher{}
	common.Must(matcher.Init(ips))

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP())
	}
}
<|endoftext|>