// Copyright (c) Facebook, Inc. and its affiliates // SPDX-License-Identifier: MIT OR Apache-2.0 use heck::CamelCase; use serde_generate::{ golang, test_utils, test_utils::{Choice, Runtime, Test}, CodeGeneratorConfig, }; use std::{fs::File, io::Write, process::Command}; use tempfile::tempdir; #[test] fn test_golang_runtime_autotests() { let runtime_mod_path = std::env::current_exe() .unwrap() .parent() .unwrap() .join("../../../serde-generate/runtime/golang"); let status = Command::new("go") .current_dir(runtime_mod_path.to_str().unwrap()) .arg("test") .arg("./...") .status() .unwrap(); assert!(status.success()); } #[test] fn test_golang_bcs_runtime_on_simple_data() { test_golang_runtime_on_simple_data(Runtime::Bcs); } #[test] fn test_golang_bincode_runtime_on_simple_data() { test_golang_runtime_on_simple_data(Runtime::Bincode); } fn test_golang_runtime_on_simple_data(runtime: Runtime) { let registry = test_utils::get_simple_registry().unwrap(); let dir = tempdir().unwrap(); let source_path = dir.path().join("test.go"); let mut source = File::create(&source_path).unwrap(); let config = CodeGeneratorConfig::new("main".to_string()) .with_encodings(vec![runtime.into()]) .with_external_definitions( vec![("github.com/google/go-cmp/cmp".to_string(), vec![])] .into_iter() .collect(), ); let generator = golang::CodeGenerator::new(&config); generator.output(&mut source, ®istry).unwrap(); let reference = runtime.serialize(&Test { a: vec![4, 6], b: (-3, 5), c: Choice::C { x: 7 }, }); writeln!( source, r#" func main() {{ input := []byte{{{0}}} value, err := {1}DeserializeTest(input) if err != nil {{ panic("failed to deserialize") }} value2 := Test {{ A: []uint32{{ 4, 6 }}, B: struct {{ Field0 int64; Field1 uint64 }} {{ -3, 5 }}, C: &Choice__C {{ X: 7 }}, }} if !cmp.Equal(value, value2) {{ panic("value != value2") }} output, err := value2.{1}Serialize() if err != nil {{ panic("failed to serialize") }} if !cmp.Equal(input, output) {{ panic("input != output") }} input2 := []byte{{{0}, 1}} value2, err2 := {1}DeserializeTest(input2) if err2 == nil {{ panic("was expecting an error") }} }} "#, reference .iter() .map(|x| format!("{}", x)) .collect::>() .join(", "), runtime.name().to_camel_case(), ) .unwrap(); let status = Command::new("go") .current_dir(dir.path()) .arg("mod") .arg("init") .arg("testing") .status() .unwrap(); assert!(status.success()); let runtime_mod_path = std::env::current_exe() .unwrap() .parent() .unwrap() .join("../../../serde-generate/runtime/golang"); let status = Command::new("go") .current_dir(dir.path()) .arg("mod") .arg("edit") .arg("-replace") .arg(format!( "github.com/novifinancial/serde-reflection/serde-generate/runtime/golang={}", runtime_mod_path.to_str().unwrap() )) .status() .unwrap(); assert!(status.success()); let status = Command::new("go") .current_dir(dir.path()) .arg("run") .arg(&source_path) .status() .unwrap(); assert!(status.success()); } #[test] fn test_golang_bcs_runtime_on_supported_types() { test_golang_runtime_on_supported_types(Runtime::Bcs); } #[test] fn test_golang_bincode_runtime_on_supported_types() { test_golang_runtime_on_supported_types(Runtime::Bincode); } fn quote_bytes(bytes: &[u8]) -> String { format!( "{{{}}}", bytes .iter() .map(|x| format!("{}", x)) .collect::>() .join(", ") ) } fn test_golang_runtime_on_supported_types(runtime: Runtime) { let registry = test_utils::get_registry().unwrap(); let dir = tempdir().unwrap(); let source_path = dir.path().join("test.go"); let mut source = File::create(&source_path).unwrap(); let config = CodeGeneratorConfig::new("main".to_string()) .with_encodings(vec![runtime.into()]) .with_external_definitions( vec![("github.com/google/go-cmp/cmp".to_string(), vec![])] .into_iter() .collect(), ); let generator = golang::CodeGenerator::new(&config); generator.output(&mut source, ®istry).unwrap(); let positive_encodings = runtime .get_positive_samples_quick() .iter() .map(|bytes| quote_bytes(bytes)) .collect::>() .join(", "); let negative_encodings = runtime .get_negative_samples() .iter() .map(|bytes| quote_bytes(bytes)) .collect::>() .join(", "); writeln!( source, r#" func main() {{ positive_inputs := [][]byte{{{0}}} negative_inputs := [][]byte{{{1}}} for _, input := range(positive_inputs) {{ value, err := {2}DeserializeSerdeData(input) if err != nil {{ panic(fmt.Sprintf("failed to deserialize input: %v", err)) }} output, err := value.{2}Serialize() if err != nil {{ panic(fmt.Sprintf("failed to serialize: %v", err)) }} if !cmp.Equal(input, output) {{ panic(fmt.Sprintf("input != output:\n %v\n %v", input, output)) }} // Test self-equality for the Serde value. {{ value2, err := {2}DeserializeSerdeData(input) if err != nil {{ panic(fmt.Sprintf("failed to deserialize input: %v", err)) }} if !cmp.Equal(value, value2) {{ panic("Value should test equal to itself.") }} }} // Test simple mutations of the input. for i := 0; i < len(input); i++ {{ input2 := make([]byte, len(input)) copy(input2, input) input2[i] ^= 0x80 value2, err := {2}DeserializeSerdeData(input2) if err != nil {{ continue }} if cmp.Equal(value, value2) {{ panic("Modified input should give a different value.") }} }} }} for _, input := range(negative_inputs) {{ _, err := {2}DeserializeSerdeData(input) if err == nil {{ panic(fmt.Sprintf("Input should fail to deserialize: %v", input)) }} }} }} "#, positive_encodings, negative_encodings, runtime.name().to_camel_case(), ) .unwrap(); let status = Command::new("go") .current_dir(dir.path()) .arg("mod") .arg("init") .arg("testing") .status() .unwrap(); assert!(status.success()); let runtime_mod_path = std::env::current_exe() .unwrap() .parent() .unwrap() .join("../../../serde-generate/runtime/golang"); let status = Command::new("go") .current_dir(dir.path()) .arg("mod") .arg("edit") .arg("-replace") .arg(format!( "github.com/novifinancial/serde-reflection/serde-generate/runtime/golang={}", runtime_mod_path.to_str().unwrap() )) .status() .unwrap(); assert!(status.success()); let status = Command::new("go") .current_dir(dir.path()) .arg("run") .arg(source_path) .status() .unwrap(); assert!(status.success()); }