func TestDCMTKCStore(t *testing.T) {
	storescuPath, err := exec.LookPath("storescu")
	if err != nil {
		t.Skip("storescu not found.")
		return
	}
	cstoreData = nil
	cmd := exec.Command(storescuPath, "localhost", getProviderPort(), "testdata/reportsi.dcm")
	require.NoError(t, cmd.Run())
	require.True(t, len(cstoreData) > 0, "No data received")
	ds, err := dicom.ReadDataSetInBytes(cstoreData, dicom.ReadOptions{})
	require.NoError(t, err)
	expected := mustReadDICOMFile("testdata/reportsi.dcm")
	checkFileBodiesEqual(t, expected, ds)
}
Test using "storescu" command from dcmtk.
package netdicom
import (
	"errors"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"testing"
	"github.com/grailbio/go-dicom"
	"github.com/grailbio/go-dicom/dicomio"
	"github.com/grailbio/go-dicom/dicomtag"
	"github.com/grailbio/go-dicom/dicomuid"
	"github.com/grailbio/go-netdicom/dimse"
	"github.com/grailbio/go-netdicom/sopclass"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)
var provider *ServiceProvider
var cstoreData []byte             
var cstoreStatus = dimse.Success  
var nEchoRequests int
var once sync.Once
func TestMain(m *testing.M) {
	flag.Parse()
	var err error
	provider, err = NewServiceProvider(ServiceProviderParams{
		CEcho:  onCEchoRequest,
		CStore: onCStoreRequest,
		CFind:  onCFindRequest,
		CGet:   onCGetRequest,
	}, ":0")
	if err != nil {
		panic(err)
	}
	go provider.Run()
	os.Exit(m.Run())
}
func onCEchoRequest(connState ConnectionState) dimse.Status {
	nEchoRequests++
	return dimse.Success
}
func onCStoreRequest(
	connState ConnectionState,
	transferSyntaxUID string,
	sopClassUID string,
	sopInstanceUID string,
	data []byte) dimse.Status {
	log.Printf("Start C-STORE handler, transfersyntax=%s, sopclass=%s, sopinstance=%s",
		dicomuid.UIDString(transferSyntaxUID),
		dicomuid.UIDString(sopClassUID),
		dicomuid.UIDString(sopInstanceUID))
	e := dicomio.NewBytesEncoder(nil, dicomio.UnknownVR)
	dicom.WriteFileHeader(e,
		[]*dicom.Element{
			dicom.MustNewElement(dicomtag.TransferSyntaxUID, transferSyntaxUID),
			dicom.MustNewElement(dicomtag.MediaStorageSOPClassUID, sopClassUID),
			dicom.MustNewElement(dicomtag.MediaStorageSOPInstanceUID, sopInstanceUID),
		})
	e.WriteBytes(data)
	cstoreData = e.Bytes()
	log.Printf("Received C-STORE request, %d bytes", len(cstoreData))
	return cstoreStatus
}
func onCFindRequest(
	connState ConnectionState,
	transferSyntaxUID string,
	sopClassUID string,
	filters []*dicom.Element,
	ch chan CFindResult) {
	log.Printf("Received cfind request")
	found := 0
	for _, elem := range filters {
		log.Printf("Filter %v", elem)
		if elem.Tag == dicomtag.QueryRetrieveLevel {
			if elem.MustGetString() != "PATIENT" {
				log.Panicf("Wrong QR level: %v", elem)
			}
			found++
		}
		if elem.Tag == dicomtag.PatientName {
			if elem.MustGetString() != "foohah" {
				log.Panicf("Wrong patient name: %v", elem)
			}
			found++
		}
	}
	if found != 2 {
		log.Panicf("Didn't find expected filters: %v", filters)
	}
	ch <- CFindResult{
		Elements: []*dicom.Element{dicom.MustNewElement(dicomtag.PatientName, "johndoe")},
	}
	ch <- CFindResult{
		Elements: []*dicom.Element{dicom.MustNewElement(dicomtag.PatientName, "johndoe2")},
	}
	close(ch)
}
func onCGetRequest(
	connState ConnectionState,
	transferSyntaxUID string,
	sopClassUID string,
	filters []*dicom.Element,
	ch chan CMoveResult) {
	log.Printf("Received cget request")
	path := "testdata/reportsi.dcm"
	dataset := mustReadDICOMFile(path)
	ch <- CMoveResult{
		Remaining: -1,
		Path:      path,
		DataSet:   dataset,
	}
	close(ch)
}
func checkFileBodiesEqual(t *testing.T, in, out *dicom.DataSet) {
	var normalize = func(s string) string {
		s = strings.Replace(s, "NA u", "NA ", -1)
		s = strings.Replace(s, "SQ u", "SQ ", -1)
		return s
	}
	var removeMetaElems = func(f *dicom.DataSet) []*dicom.Element {
		var elems []*dicom.Element
		for _, elem := range f.Elements {
			if elem.Tag.Group != dicomtag.MetadataGroup {
				elems = append(elems, elem)
			}
		}
		return elems
	}
	inElems := removeMetaElems(in)
	outElems := removeMetaElems(out)
	assert.Equal(t, len(inElems), len(outElems))
	for i := 0; i < len(inElems); i++ {
		ins := normalize(inElems[i].String())
		outs := normalize(outElems[i].String())
		if ins != outs {
			t.Errorf("%dth element mismatch: %v <-> %v", i, ins, outs)
		}
	}
}
func getCStoreData() (*dicom.DataSet, error) {
	if len(cstoreData) == 0 {
		return nil, errors.New("Did not receive C-STORE data")
	}
	f, err := dicom.ReadDataSetInBytes(cstoreData, dicom.ReadOptions{})
	if err != nil {
		return nil, err
	}
	return f, nil
}
func mustReadDICOMFile(path string) *dicom.DataSet {
	dataset, err := dicom.ReadDataSetFromFile(path, dicom.ReadOptions{})
	if err != nil {
		log.Panic(err)
	}
	return dataset
}
func mustNewServiceUser(t *testing.T, sopClasses []string) *ServiceUser {
	su, err := NewServiceUser(ServiceUserParams{SOPClasses: sopClasses})
	require.NoError(t, err)
	log.Printf("Connecting to %v", provider.ListenAddr().String())
	su.Connect(provider.ListenAddr().String())
	return su
}
func TestStore(t *testing.T) {
	dataset := mustReadDICOMFile("testdata/IM-0001-0003.dcm")
	su := mustNewServiceUser(t, sopclass.StorageClasses)
	defer su.Release()
	err := su.CStore(dataset)
	if err != nil {
		log.Panic(err)
	}
	log.Printf("Store done!!")
	out, err := getCStoreData()
	if err != nil {
		log.Panic(err)
	}
	checkFileBodiesEqual(t, dataset, out)
}
func TestStoreFailure0(t *testing.T) {
	dataset := mustReadDICOMFile("testdata/IM-0001-0003.dcm")
	cstoreStatus = dimse.Status{Status: dimse.StatusNotAuthorized, ErrorComment: "Foohah"}
	defer func() { cstoreStatus = dimse.Success }()
	su := mustNewServiceUser(t, sopclass.StorageClasses)
	defer su.Release()
	err := su.CStore(dataset)
	if err == nil || strings.Index(err.Error(), "Foohah") < 0 {
		log.Panic(err)
	}
}
func getProviderPort() string {
	match := regexp.MustCompile("(\\d+)$").FindStringSubmatch(provider.ListenAddr().String())
	return match[1]
}