public void CompareProtoToParser(string path)
        {
            var schemaPath = Path.Combine(Directory.GetCurrentDirectory(), SchemaPath);

            _output.WriteLine(Path.GetDirectoryName(
                                  Path.Combine(schemaPath, path).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)));

            bool includeComments = IncludeComments(path);

            var protocBinPath = Path.Combine(schemaPath, Path.ChangeExtension(path, "protoc.bin"));
            int exitCode;

            using (var proc = new Process())
            {
                var psi = proc.StartInfo;
                psi.FileName  = "protoc";
                psi.Arguments = $"--descriptor_set_out={protocBinPath} {path}";
                if (includeComments)
                {
                    psi.Arguments += " --include_source_info";
                }
                psi.RedirectStandardError = psi.RedirectStandardOutput = true;
                psi.UseShellExecute       = false;
                psi.WorkingDirectory      = schemaPath;
                proc.Start();
                var stdout = proc.StandardOutput.ReadToEndAsync();
                var stderr = proc.StandardError.ReadToEndAsync();
                if (!proc.WaitForExit(5000))
                {
                    try { proc.Kill(); } catch { }
                }
                exitCode = proc.ExitCode;
                string err = "", @out = "";
                if (stdout.Wait(1000))
                {
                    @out = stdout.Result;
                }
                if (stderr.Wait(1000))
                {
                    err = stderr.Result;
                }

                if (!string.IsNullOrWhiteSpace(@out))
                {
                    _output.WriteLine("stdout: ");
                    _output.WriteLine(@out);
                }
                if (!string.IsNullOrWhiteSpace(err))
                {
                    _output.WriteLine("stderr: ");
                    _output.WriteLine(err);
                }
            }
            FileDescriptorSet set;
            string            protocJson = null, jsonPath;

            if (exitCode == 0)
            {
                using (var file = File.OpenRead(protocBinPath))
                {
                    set        = Serializer.Deserialize <FileDescriptorSet>(file);
                    protocJson = JsonConvert.SerializeObject(set, Formatting.Indented, jsonSettings);
                    jsonPath   = Path.Combine(schemaPath, Path.ChangeExtension(path, "protoc.json"));
                    File.WriteAllText(jsonPath, protocJson);
                }
            }



            set = new FileDescriptorSet();

            set.AddImportPath(schemaPath);
            bool isProto3 = set.Add(path, includeInOutput: true) && set.Files[0].Syntax == "proto3";

            if (isProto3)
            {
                using (var proc = new Process())
                {
                    var psi = proc.StartInfo;
                    psi.FileName              = "protoc";
                    psi.Arguments             = $"--csharp_out={Path.GetDirectoryName(protocBinPath)} {path}";
                    psi.RedirectStandardError = psi.RedirectStandardOutput = true;
                    psi.UseShellExecute       = false;
                    psi.WorkingDirectory      = schemaPath;
                    proc.Start();
                    var stdout = proc.StandardOutput.ReadToEndAsync();
                    var stderr = proc.StandardError.ReadToEndAsync();
                    if (!proc.WaitForExit(5000))
                    {
                        try { proc.Kill(); } catch { }
                    }
                    exitCode = proc.ExitCode;
                    string err = "", @out = "";
                    if (stdout.Wait(1000))
                    {
                        @out = stdout.Result;
                    }
                    if (stderr.Wait(1000))
                    {
                        err = stderr.Result;
                    }

                    if (!string.IsNullOrWhiteSpace(@out))
                    {
                        _output.WriteLine("stdout (C#): ");
                        _output.WriteLine(@out);
                    }
                    if (!string.IsNullOrWhiteSpace(err))
                    {
                        _output.WriteLine("stderr (C#): ");
                        _output.WriteLine(err);
                    }
                    _output.WriteLine("exit code(C#): " + exitCode);
                }
            }

            set.Process();

            var parserBinPath = Path.Combine(schemaPath, Path.ChangeExtension(path, "parser.bin"));

            using (var file = File.Create(parserBinPath))
            {
                set.Serialize(file, false);
            }

            var parserJson = set.Serialize((s, o) => JsonConvert.SerializeObject(s, Formatting.Indented, jsonSettings), false);

            var       errors   = set.GetErrors();
            Exception genError = null;

            try
            {
                foreach (var file in CSharpCodeGenerator.Default.Generate(set))
                {
                    var newExtension = "parser" + Path.GetExtension(file.Name);
                    var newFileName  = Path.ChangeExtension(file.Name, newExtension);
                    File.WriteAllText(Path.Combine(schemaPath, newFileName), file.Text);
                }
            }
            catch (Exception ex)
            {
                genError = ex;
                _output.WriteLine(ex.Message);
                _output.WriteLine(ex.StackTrace);
            }



            jsonPath = Path.Combine(schemaPath, Path.ChangeExtension(path, "parser.json"));
            File.WriteAllText(jsonPath, parserJson);


            if (errors.Any())
            {
                _output.WriteLine("Parser errors:");
                foreach (var err in errors)
                {
                    _output.WriteLine(err.ToString());
                }
            }

            _output.WriteLine("Protoc exited with code " + exitCode);

            var errorCount = errors.Count(x => x.IsError);

            if (exitCode == 0)
            {
                Assert.Equal(0, errorCount);
            }
            else
            {
                Assert.NotEqual(0, errorCount);
            }



            var parserBytes = File.ReadAllBytes(parserBinPath);

            using (var ms = new MemoryStream(parserBytes))
            {
                var selfLoad     = Serializer.Deserialize <FileDescriptorSet>(ms);
                var selfLoadJson = JsonConvert.SerializeObject(selfLoad, Formatting.Indented, jsonSettings);
                // should still be the same!
                Assert.Equal(parserJson, selfLoadJson);
            }
            var parserHex = GetPrettyHex(parserBytes);

            File.WriteAllText(Path.ChangeExtension(parserBinPath, "parser.hex"), parserHex);

            if (exitCode == 0)
            {
                var protocHex = GetPrettyHex(File.ReadAllBytes(protocBinPath));
                File.WriteAllText(Path.ChangeExtension(protocBinPath, "protoc.hex"), protocHex);

                switch (path)
                {
                case "google/protobuf/unittest_custom_options.proto":
                    // this is a special case; the two encoders choose slightly different
                    // layouts for the same data; both are valid; I'm happy that this is OK
                    // - this was why the "decode" tool (on the website) was written!
                    break;

                default:
                    // compare results
                    Assert.Equal(protocJson, parserJson);
                    Assert.Equal(protocHex, parserHex);
                    break;
                }
            }



            Assert.Null(genError);
        }
Exemple #2
0
        public void CompareProtoToParser(string path, bool includeImports)
        {
            if (path == "google/protobuf/map_unittest_proto3.proto")
            {
                return;                                                      // TODO known oddity
            }
            var schemaPath = Path.Combine(Directory.GetCurrentDirectory(), SchemaPath);

            _output.WriteLine(Path.GetDirectoryName(
                                  Path.Combine(schemaPath, path).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)));

            bool includeComments = IncludeComments(path);

            var    protocBinPath = Path.Combine(schemaPath, Path.ChangeExtension(path, "protoc.bin"));
            int    exitCode;
            string protocExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"windows\protoc" :
                               RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? @"macosx/protoc" : "";

            if (string.IsNullOrWhiteSpace(protocExe))
            {
                throw new PlatformNotSupportedException(RuntimeInformation.OSDescription);
            }
            using (var proc = new Process())
            {
                var psi = proc.StartInfo;
                psi.FileName = protocExe;

                psi.Arguments = $"--experimental_allow_proto3_optional --descriptor_set_out={protocBinPath} {path}";
                if (includeComments)
                {
                    psi.Arguments += " --include_source_info";
                }
                if (includeImports)
                {
                    psi.Arguments += " --include_imports";
                }
                psi.RedirectStandardError = psi.RedirectStandardOutput = true;
                psi.CreateNoWindow        = true;
                psi.UseShellExecute       = false;
                psi.WorkingDirectory      = schemaPath;
                proc.Start();
                var stdout = proc.StandardOutput.ReadToEndAsync();
                var stderr = proc.StandardError.ReadToEndAsync();
                if (!proc.WaitForExit(5000))
                {
                    try { proc.Kill(); } catch { }
                }
                exitCode = proc.ExitCode;
                string err = "", @out = "";
                if (stdout.Wait(1000))
                {
                    @out = stdout.Result;
                }
                if (stderr.Wait(1000))
                {
                    err = stderr.Result;
                }

                if (!string.IsNullOrWhiteSpace(@out))
                {
                    _output.WriteLine("stdout: ");
                    _output.WriteLine(@out);
                }
                if (!string.IsNullOrWhiteSpace(err))
                {
                    _output.WriteLine("stderr: ");
                    _output.WriteLine(err);
                }
            }
            FileDescriptorSet set;
            string            protocJson = null, jsonPath;

            if (exitCode == 0)
            {
                using var file = File.OpenRead(protocBinPath);
                set            = CustomProtogenSerializer.Instance.Deserialize <FileDescriptorSet>(file);
                protocJson     = JsonConvert.SerializeObject(set, Formatting.Indented, jsonSettings);
                jsonPath       = Path.Combine(schemaPath, Path.ChangeExtension(path, "protoc.json"));
                File.WriteAllText(jsonPath, protocJson);
            }

            set = new FileDescriptorSet();

            set.AddImportPath(schemaPath);
            bool isProto3 = set.Add(path, includeInOutput: true) && set.Files[0].Syntax == "proto3";

            if (isProto3)
            {
                using var proc = new Process();
                var psi = proc.StartInfo;
                psi.FileName              = protocExe;
                psi.Arguments             = $"--experimental_allow_proto3_optional --csharp_out={Path.GetDirectoryName(protocBinPath)} {path}";
                psi.RedirectStandardError = psi.RedirectStandardOutput = true;
                psi.CreateNoWindow        = true;
                psi.UseShellExecute       = false;
                psi.WorkingDirectory      = schemaPath;
                proc.Start();
                var stdout = proc.StandardOutput.ReadToEndAsync();
                var stderr = proc.StandardError.ReadToEndAsync();
                if (!proc.WaitForExit(5000))
                {
                    try { proc.Kill(); } catch { }
                }
                exitCode = proc.ExitCode;
                string err = "", @out = "";
                if (stdout.Wait(1000))
                {
                    @out = stdout.Result;
                }
                if (stderr.Wait(1000))
                {
                    err = stderr.Result;
                }

                if (!string.IsNullOrWhiteSpace(@out))
                {
                    _output.WriteLine("stdout (C#): ");
                    _output.WriteLine(@out);
                }
                if (!string.IsNullOrWhiteSpace(err))
                {
                    _output.WriteLine("stderr (C#): ");
                    _output.WriteLine(err);
                }
                _output.WriteLine("exit code(C#): " + exitCode);
            }

            set.Process();
            set.ApplyFileDependencyOrder();

            var parserBinPath = Path.Combine(schemaPath, Path.ChangeExtension(path, "parser.bin"));

            using (var file = File.Create(parserBinPath))
            {
                set.Serialize(CustomProtogenSerializer.Instance, file, includeImports);
            }

            var parserJson = set.Serialize((s, _) => JsonConvert.SerializeObject(s, Formatting.Indented, jsonSettings), includeImports);

            var       errors   = set.GetErrors();
            Exception genError = null;

            try
            {
                var options = new Dictionary <string, string>
                {
                    { "services", "true" },
                };
                foreach (var file in CSharpCodeGenerator.Default.Generate(set, options: options))
                {
                    var newExtension = "parser" + Path.GetExtension(file.Name);
                    var newFileName  = Path.ChangeExtension(file.Name, newExtension);
                    File.WriteAllText(Path.Combine(schemaPath, newFileName), file.Text);
                }
            }
            catch (Exception ex)
            {
                genError = ex;
                _output.WriteLine(ex.Message);
                _output.WriteLine(ex.StackTrace);
            }

            jsonPath = Path.Combine(schemaPath, Path.ChangeExtension(path, "parser.json"));
            File.WriteAllText(jsonPath, parserJson);

            if (errors.Length > 0)
            {
                _output.WriteLine("Parser errors:");
                foreach (var err in errors)
                {
                    _output.WriteLine(err.ToString());
                }
            }

            _output.WriteLine("Protoc exited with code " + exitCode);

            var errorCount = errors.Count(x => x.IsError);

            if (exitCode == 0)
            {
                Assert.Equal(0, errorCount);
            }
            else
            {
                Assert.NotEqual(0, errorCount);
            }

            var parserBytes = File.ReadAllBytes(parserBinPath);

            using (var ms = new MemoryStream(parserBytes))
            {
                var selfLoad     = CustomProtogenSerializer.Instance.Deserialize <FileDescriptorSet>(ms);
                var selfLoadJson = JsonConvert.SerializeObject(selfLoad, Formatting.Indented, jsonSettings);
                // should still be the same!
                Assert.Equal(parserJson, selfLoadJson);
            }
            var parserHex = GetPrettyHex(parserBytes);

            File.WriteAllText(Path.ChangeExtension(parserBinPath, "parser.hex"), parserHex);

            if (exitCode == 0)
            {
                var protocHex = GetPrettyHex(File.ReadAllBytes(protocBinPath));
                File.WriteAllText(Path.ChangeExtension(protocBinPath, "protoc.hex"), protocHex);

                switch (path)
                {
                case "google/protobuf/unittest_custom_options.proto":
                case "advancedOptions.proto":
                    // these are special cases; the two encoders choose slightly different
                    // layouts for the same data; both are valid; I'm happy that this is OK
                    // - this was why the "decode" tool (on the website) was written!
                    break;

                case "google/protobuf/unittest.proto":
                // ^^^ different layout of an integer; "2e+8" vs "200000000" - I'm fine with it
                //
                // the following end up importing unittest.proto, so have the same symptom
                case "google/protobuf/map_unittest.proto" when(includeImports):
                case "google/protobuf/unittest_optimize_for.proto" when(includeImports):
                case "google/protobuf/unittest_embed_optimize_for.proto" when(includeImports):
                case "google/protobuf/unittest_lite_imports_nonlite.proto" when(includeImports):
                case "google/protobuf/unittest_no_field_presence.proto" when(includeImports):
                    break;

                default:
                    // compare results
                    Assert.Equal(protocJson, parserJson);
                    Assert.Equal(protocHex, parserHex);
                    break;
                }
            }
            Assert.Null(genError);
        }