branch: externals/org
commit 526a7d1cc65409d5546b009f54fed28726a9457d
Author: Maximilian Kueffner <poverobuosodon...@gmail.com>
Commit: Maximilian Kueffner <poverobuosodon...@gmail.com>

    lisp/ob-csharp.el: Include support for evaluating C# code blocks
    
    * ob-csharp.el: implements the babel contract to expand and evaluate
    C# code blocks in org-mode using the dotnet SDK.
    * testing/lisp/test-ob-csharp.el: comprehensive tests for
    ob-csharp.el.
    * mk/default.mk (`csharp' to `BTEST_OB_LANGUAGES'): the new babel
    language `csharp' is added to the list of `BTEST_OB_LANGUAGES'.  It is
    commented out because a .NET SDK is required to run respective tests.
    * ORG-NEWS: describes the newly introduced C# code block feature.
    
    This change aims to officially support C# code blocks using
    ~dotnet~ (over ~mono~) as the default compiler.  C#-Code block
    evaluation with this module happens in context of a temporary
    ~.csproj~ file allowing for idiomatic run- and compile time
    dependencies like existing projects, assemblies or NuGet packages.
---
 etc/ORG-NEWS                   |   4 +
 lisp/ob-csharp.el              | 299 ++++++++++++++++++++++++++++++++++++++
 mk/default.mk                  |   1 +
 testing/lisp/test-ob-csharp.el | 320 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 624 insertions(+)

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 1f7cb2cbc1..3f17e223e6 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -239,6 +239,10 @@ appropriate major mode is unavailable.
 
 When editing Dot source blocks, Org now uses Graphviz Dot mode, if installed.
 
+*** C# code block support with =ob-csharp.el=
+
+Org now officially enables C# code block evaluation based on the .NET SDK.
+
 ** New and changed options
 
 # Changes dealing with changing default values of customizations,
diff --git a/lisp/ob-csharp.el b/lisp/ob-csharp.el
new file mode 100644
index 0000000000..48dd7bfdc7
--- /dev/null
+++ b/lisp/ob-csharp.el
@@ -0,0 +1,299 @@
+;;; ob-csharp.el --- org-babel functions for csharp evaluation -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2024-2025 Free Software Foundation, Inc.
+
+;; Author:     Maximilian Kueffner
+;; Maintainer: Maximilian Kueffner <poverobuosodon...@gmail.com>
+;; Keywords: literate programming, reproducible research
+;; Homepage: https://orgmode.org
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Requirements:
+
+;; Some .NET runtime environment should be installed.
+;; The `dotnet' command should be available to the system's environment
+;; (PATH discoverable for example).
+
+;;; Code:
+(require 'ob)
+
+;; file extension for C#
+(add-to-list 'org-babel-tangle-lang-exts '("csharp" . "cs"))
+
+;; default header arguments for C#
+(defvar org-babel-default-header-args:csharp
+  '((main . ((no)))
+    (nugetconfig . :any)
+    (framework . :any)
+    (class . ((no nil :any)))
+    (references . :any)
+    (usings . :any)
+    (cmdline . :any))
+  "Csharp specific header arguments.")
+
+(defcustom org-babel-csharp-compiler "dotnet"
+  "The program to call for compiling a csharp project."
+  :group 'org-babel
+  :package-version '(Org. "9.8")
+  :type 'string)
+
+(defun org-babel-csharp--default-compile-command (dir-proj-sln bin-dir)
+  "Construct the default compilation command for C#.
+
+DIR-PROJ-SLN is either a directory containing a \".csproj\" or \".sln\" file
+or a full path to either of these.
+BIN-DIR is the directory for the compiled output."
+  (format "%s build --output %S %S"
+          org-babel-csharp-compiler bin-dir dir-proj-sln))
+
+(defun org-babel-csharp--default-restore-command (project-file)
+  "Construct the default restore command for C# projects.
+
+PROJECT-FILE is a path to a \".csproj\" file on which the restore command
+takes effect."
+  (format "%s restore %S" org-babel-csharp-compiler project-file))
+
+(defun org-babel-csharp--find-dotnet-version ()
+  "Get a list of dotnet major versions from a list of dotnet sdks."
+  (cl-delete-if #'(lambda (v) (= 0 v))
+             (delete-dups
+              (mapcar #'(lambda (n)
+                          (let ((fr (string-match "^[0-9.]+\\." n))
+                                (to (string-match "\\." n)))
+                            (string-to-number (substring n fr to))))
+                      (split-string
+                       (shell-command-to-string
+                        (format "%s --list-sdks" org-babel-csharp-compiler))
+                       "\n")))))
+
+(defcustom org-babel-csharp-default-target-framework
+  (format "net%s.0"
+          (let ((net-sdks (org-babel-csharp--find-dotnet-version)))
+            (when net-sdks
+                (apply #'max net-sdks))))
+  "The desired target framework to use."
+  :group 'org-babel
+  :package-version '(Org. "9.8")
+  :type 'string)
+
+(defcustom org-babel-csharp-generate-compile-command
+  #'org-babel-csharp--default-compile-command
+  "A function creating the compile command.
+
+It must take two parameters intended for the target binary directory and
+a .sln file, .csproj file, or a base directory where either can be found."
+  :group 'org-babel
+  :package-version '(Org. "9.8")
+  :type 'function)
+
+(defcustom org-babel-csharp-generate-restore-command
+  #'org-babel-csharp--default-restore-command
+  "A function creating a project restore command.
+
+It must take one parameter defining the project to perform a restore on."
+  :group 'org-babel
+  :package-version '(Org. "9.8")
+  :type 'function)
+
+(defcustom org-babel-csharp-additional-project-flags nil
+  "Will be passed in the \"PropertyGroup\" defining the project.
+
+This is taken as-is. It should be a string in XML-format."
+  :group 'org-babel
+  :package-version '(Org. "9.8")
+  :type 'string)
+
+(defun org-babel-csharp--generate-project-file (refs framework)
+  "Generate the file content to be used in a csproj-file.
+
+REFS is a list of references. Check `org-babel-csharp--format-refs' for
+the allowed semantics.
+FRAMEWORK is the target framework."
+  (unless framework
+    (error "framework cannot be nil"))
+  (concat "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  "
+          (when refs
+            (org-babel-csharp--format-refs refs))
+          "\n\n  <PropertyGroup>"
+          "\n    <OutputType>Exe</OutputType>\n"
+          (format "\n    <TargetFramework>%s</TargetFramework>" framework)
+          "\n    <ImplicitUsings>enable</ImplicitUsings>"
+          "\n    <Nullable>enable</Nullable>"
+          (when org-babel-csharp-additional-project-flags
+            (format "\n    %s" org-babel-csharp-additional-project-flags))
+          "\n  </PropertyGroup>"
+          "\n</Project>"))
+
+(defun org-babel-csharp--format-usings (usings)
+  "Format USINGS into a string suitable for inclusion in a C# source file.
+
+USINGS should be a list of strings, each representing a using directive.
+Returns a string with each using directive on a new line."
+  (mapconcat
+   (lambda (u)
+     (unless (stringp u) (error "Usings must be of type string."))
+     (format "using %s;" u))
+   usings "\n"))
+
+(defun org-babel-expand-body:csharp (body params)
+  "Expand a block of C# code in BODY according to PARAMS.
+
+See `org-babel-default-header-args:csharp' for available parameters."
+  (let* ((main-p (not (string= (cdr (assq :main params)) "no")))
+         (class (pcase (alist-get :class params)
+                  ("no" nil)
+                  (`nil "Program")
+                  (_ (alist-get :class params))))
+         (namespace "org.babel.autogen")
+         (usings (alist-get :usings params)))
+    (with-temp-buffer
+      (when (alist-get :prologue params)
+        (insert (alist-get :prologue params) "\n"))
+      (insert "namespace " namespace ";\n")
+      (when usings
+        (insert (format "\n%s\n" (org-babel-csharp--format-usings usings))))
+      (when class
+        (insert "\nclass " class "\n{\n"))
+      (when main-p
+        (insert "static void Main(string[] args)\n{\n"))
+      (insert (if (alist-get :var params)
+                  (mapconcat #'identity (org-babel-variable-assignments:csharp 
params) "\n")
+                "")
+              "\n")
+      (insert body)
+      (when main-p
+        (insert "\n}"))
+      (when class
+        (insert "\n}"))
+      (when (alist-get :epilogue params)
+        (insert "\n" (alist-get :epilogue params)))
+      (buffer-string))))
+
+(defun org-babel-csharp--format-refs (refs)
+  "Format REFS into a string suitable for inclusion in a .csproj file.
+
+REFS should be a list of strings or cons cells, each representing a reference.
+If an entry is a cons cell, the car denotes the reference name and
+the cdr is the version.
+
+Returns a formatted string representing the references, categorized into
+project reference, assembly reference, and package reference.
+Reference types are distinguished by their file extension.
+'.csproj' is interpreted as a project reference,
+'.dll' as an assembly reference.
+When a version is present, it will be treated as a package reference."
+  (let ((projectref)
+        (assemblyref)
+        (systemref))
+    (dolist (ref refs)
+      (let* ((version (if (consp ref)
+                          (cdr ref)
+                        nil))
+             (ref-string (if (consp ref)
+                             (car ref)
+                           ref))
+             (full-ref (if version
+                           (file-truename (car ref))
+                         (file-truename ref))))
+        (cond
+         ((string= "csproj" (file-name-extension full-ref))
+          (setf projectref
+                (concat projectref
+                        (format "\n    <ProjectReference Include=\"%s\" />"
+                                full-ref))))
+         ((string= "dll" (file-name-extension full-ref))
+          (setf assemblyref
+                (concat assemblyref
+                        (format "\n    <Reference Include=%S>\n      
<HintPath>%s</HintPath>\n    </Reference>"
+                                (file-name-base full-ref) full-ref))))
+         (t (setf systemref
+                  (concat systemref
+                          (format "\n    <PackageReference Include=%s />"
+                                  (if version
+                                      (format "%S Version=%S" ref-string 
version)
+                                    (format "%S" ref-string)))))))))
+    (format "%s\n\n  %s\n\n  %s"
+            (if projectref
+                (format "<ItemGroup>%s\n  </ItemGroup>" projectref)
+              "")
+            (if assemblyref
+                (format "<ItemGroup>%s\n  </ItemGroup>" assemblyref)
+              "")
+            (if systemref
+                (format "<ItemGroup>%s\n  </ItemGroup>" systemref)
+              ""))))
+
+(defun org-babel-execute:csharp (body params)
+  "Execute a block of Csharp code with org-babel.
+This function is called by `org-babel-execute-src-block'"
+  (let* ((full-body (org-babel-expand-body:csharp body params))
+         (base-dir  (make-temp-name (file-name-concat 
org-babel-temporary-directory "obcs")))
+         (project-name (file-name-base base-dir))
+         (bin-dir (file-name-concat base-dir "bin"))
+         (framework (or (alist-get :framework params) 
org-babel-csharp-default-target-framework))
+         (program-file (file-name-concat base-dir "Program.cs"))
+         (project-file (file-name-concat base-dir (concat project-name 
".csproj")))
+         (nuget-file (alist-get :nugetconfig params))
+         (cmdline (alist-get :cmdline params))
+         (cmdline (if cmdline cmdline ""))
+         (restore-cmd (funcall org-babel-csharp-generate-restore-command 
project-file))
+         (compile-cmd (funcall org-babel-csharp-generate-compile-command
+                               (file-truename project-file)
+                               (file-truename bin-dir)))
+         (run-cmd (format "%S %S" (file-truename (file-name-concat bin-dir 
project-name)) cmdline)))
+    (unless (org-babel-csharp--find-dotnet-version)
+      (error "Could not find a .NET SDK for compiling."))
+    (unless (file-exists-p base-dir)
+      (make-directory base-dir))
+    (with-temp-file program-file
+      (insert full-body))
+    (with-temp-file project-file
+      (insert
+       (let ((refs (alist-get :references params)))
+         (org-babel-csharp--generate-project-file refs framework))))
+    (when (and nuget-file (file-exists-p (file-truename nuget-file)))
+      (copy-file nuget-file (file-name-concat base-dir (file-name-nondirectory 
(file-truename nuget-file)))))
+    ;; nuget restore
+    (org-babel-eval restore-cmd "")
+    (let ((compile-result (org-babel-eval compile-cmd "")))
+      (when (string-match ": error" compile-result)
+        (org-babel-eval-error-notify 1 compile-result)))
+    (let ((results (org-babel-eval run-cmd "")))
+      (when results
+        (setq results (org-remove-indentation results))
+        ;; results
+        (org-babel-reassemble-table
+        (org-babel-result-cond (cdr (assq :result-params params))
+          results
+          (let ((tmp-file (org-babel-temp-file "c-")))
+            (with-temp-file tmp-file (insert results))
+            (org-babel-import-elisp-from-file tmp-file)))
+        (org-babel-pick-name
+         (cdr (assq :colname-names params)) (cdr (assq :colnames params)))
+        (org-babel-pick-name
+         (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))))
+
+(defun org-babel-variable-assignments:csharp (params)
+  "Return a list of C# variable assignments from header arguments."
+  (mapcar
+   #'(lambda (pair) (format "var %s = %S;" (car pair) (cdr pair)))
+   (org-babel--get-vars params)))
+
+(provide 'ob-csharp)
+;;; ob-csharp.el ends here
diff --git a/mk/default.mk b/mk/default.mk
index c5ea1ba01d..9ecda3c10a 100644
--- a/mk/default.mk
+++ b/mk/default.mk
@@ -56,6 +56,7 @@ BTEST_POST  =
 BTEST_OB_LANGUAGES = awk C fortran maxima lilypond octave perl python java 
sqlite eshell calc
               # R                     # requires ESS to be installed and 
configured
               # ruby                  # requires inf-ruby to be installed and 
configured
+              # csharp                # requires a .NET SDK to be installed
 # extra packages to require for testing
 BTEST_EXTRA =
               # ess-site  # load ESS for R tests
diff --git a/testing/lisp/test-ob-csharp.el b/testing/lisp/test-ob-csharp.el
new file mode 100644
index 0000000000..afcfae38d4
--- /dev/null
+++ b/testing/lisp/test-ob-csharp.el
@@ -0,0 +1,320 @@
+;;; test-ob-csharp.el --- Tests for ob-csharp        -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2025  Maximilian Kueffner
+
+;; Author: Maximilian Kueffner <poverobuosodon...@gmail.com>
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(unless (featurep 'ob-csharp)
+  (signal 'missing-test-dependency '("Support for C# code blocks")))
+
+(require 'ob-core)
+
+(org-test-for-executable org-babel-csharp-compiler)
+
+(ert-deftest test-ob-csharp/customized-compile-command-used ()
+  "User specified compile command is used."
+  (let* ((custom-fun (lambda (p b) (format "custom-compiler %s %s" p b)))
+         (project "/tmp/placeholder/dummy.csproj")
+         (binary "/tmp/placeholder/bin")
+         (default-command (funcall org-babel-csharp-generate-compile-command 
project binary))
+         (cmd-backup org-babel-csharp-generate-compile-command))
+    (unwind-protect
+        (progn
+          (setq org-babel-csharp-generate-compile-command custom-fun)
+          (should-not (string=
+                       default-command
+                       (funcall org-babel-csharp-generate-compile-command 
project binary)))
+          (should (string= (funcall custom-fun project binary)
+                           (funcall org-babel-csharp-generate-compile-command 
project binary))))
+      (setq org-babel-csharp-generate-compile-command cmd-backup))))
+
+(ert-deftest test-ob-csharp/customized-restore-command-used ()
+  "User specified compile command is used."
+  (let* ((custom-fun (lambda (p) (format "custom-restore %s" p)))
+         (project "/tmp/placeholder/dummy.csproj")
+         (default-command (funcall org-babel-csharp-generate-restore-command 
project))
+         (cmd-backup org-babel-csharp-generate-restore-command))
+    (unwind-protect
+        (progn
+          (setq org-babel-csharp-generate-restore-command custom-fun)
+          (should-not (string=
+                       default-command
+                       (funcall org-babel-csharp-generate-restore-command 
project)))
+          (should (string= (funcall custom-fun project)
+                           (funcall org-babel-csharp-generate-restore-command 
project))))
+      (setq org-babel-csharp-generate-restore-command cmd-backup))))
+
+(ert-deftest test-ob-csharp/generate-project-file ()
+  "Test intended parameterization of the project file generator."
+  (should (stringp (org-babel-csharp--generate-project-file nil "net6.0")))
+  (should (stringp (org-babel-csharp--generate-project-file '("a-ref") 
"net6.0")))
+  (should (stringp (org-babel-csharp--generate-project-file '("a-ref" "b-ref") 
"net6.0")))
+  (should-error (org-babel-csharp--generate-project-file nil nil))
+  (should-error (org-babel-csharp--generate-project-file '(nil) "net6.0"))
+  (should-error (org-babel-csharp--generate-project-file "a-ref" "net6.0")))
+
+(ert-deftest test-ob-csharp/format-usings ()
+  "Test intended parameterization of the C# using formatter."
+  (should (string=
+           "using namespaceA;\nusing namesaceB;"
+           (org-babel-csharp--format-usings '("namespaceA" "namesaceB"))))
+  (should (string=
+           ""
+           (org-babel-csharp--format-usings nil)))
+  (should-error (org-babel-csharp--format-usings '("namespaceA" nil 
"namesaceB")))
+  (should-error (org-babel-csharp--format-usings "singleUsing")))
+
+(ert-deftest test-ob-csharp/extension-based-reference-types ()
+  "Test if the references as supplied to the \"references\" header-arg are 
correctly parsed."
+  (let ((nuget-inp '(("Nugetref" . "0.0.1")))
+        (assembly-inp '("assembly.dll"))
+        (project-inp '("project.csropj")))
+    (should (string= (org-babel-csharp--format-refs nuget-inp) "\n\n  \n\n  
<ItemGroup>\n    <PackageReference Include=\"Nugetref\" Version=\"0.0.1\" />\n  
</ItemGroup>"))
+    (should (string-search "\n\n  <ItemGroup>\n    <Reference 
Include=\"assembly\">\n      <HintPath>"
+                           (org-babel-csharp--format-refs assembly-inp)))
+    (should (string= (org-babel-csharp--format-refs project-inp) "\n\n  \n\n  
<ItemGroup>\n    <PackageReference Include=\"project.csropj\" />\n  
</ItemGroup>"))
+    (should-error (org-babel-csharp--format-refs "not-a-list"))))
+(ert-deftest test-ob-csharp/custom-class-name-header-argument ()
+  "The generated class name matches the provided string or defaults to 
\"Program\"."
+  (let ((cs-block (org-test-with-temp-text
+                      "#+begin_src csharp :class \"MyAwesomeClass\"
+  Console.WriteLine(\"ok\");
+#+end_src"
+                    (org-babel-expand-src-block))))
+    (should     (string-search "class MyAwesomeClass" cs-block))
+    (should-not (string-search "class Program" cs-block))))
+
+(ert-deftest test-ob-csharp/custom-main-function-wrapping ()
+  "Lax main function wrapping works."
+  (let* ((dummy-block "#+begin_src csharp %s \nvar a = 1 + 1;\n#+end_src")
+         (disable-main-quote (org-test-with-temp-text
+                                 (format dummy-block ":main \"no\"")
+                               (org-babel-expand-src-block)))
+         (disable-main-plain (org-test-with-temp-text
+                                 (format dummy-block ":main no")
+                               (org-babel-expand-src-block)))
+         (main-implicit (org-test-with-temp-text
+                            (format dummy-block "")
+                          (org-babel-expand-src-block)))
+         (main-explicit (org-test-with-temp-text
+                            (format dummy-block ":main anything")
+                          (org-babel-expand-src-block)))
+         (str-after-break (lambda (s) (substring s (+ 1 (string-search "\n" 
s)) -1))))
+    (should (equal (funcall str-after-break disable-main-plain) (funcall 
str-after-break disable-main-quote)))
+    (should (equal (funcall str-after-break main-implicit) (funcall 
str-after-break main-explicit)))
+    (should-not (string-search "static void Main" disable-main-quote))
+    (should-not (string-search "static void Main" disable-main-plain))
+    (should (string-search "static void Main" main-implicit))
+    (should (string-search "static void Main" main-explicit))))
+
+(ert-deftest test-ob-csharp/int-from-var ()
+  "Test of an integer variable."
+  (org-test-with-temp-text "#+begin_src csharp :var i=42 :results silent
+  Console.WriteLine(i);
+#+end_src"
+    (should (= 42 (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/float-from-var ()
+  "Test of a float variable."
+  (org-test-with-temp-text "#+begin_src csharp :var f=3.14 :results silent
+  Console.WriteLine(f);
+#+end_src"
+    (should (= 3.14 (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/string-from-var ()
+  "Test of a string variable."
+      (org-test-with-temp-text "#+begin_src csharp :var s=\"pi\" :results 
silent
+  Console.WriteLine(s);
+#+end_src"
+        (should (string= "pi" (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/outputs-list ()
+  "Test list output."
+  (org-test-with-temp-text "#+begin_src csharp :results raw list silent
+  Console.WriteLine(\"Item 1\");
+  Console.WriteLine(\"Item 2\");
+  Console.WriteLine(\"Item 3\");
+  Console.WriteLine(\"Item 4\");
+#+end_src"
+    (should (equal "Item 1\nItem 2\nItem 3\nItem 4\n" 
(org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/commandline-input ()
+  "Test command line input."
+  (org-test-with-temp-text "#+begin_src csharp :cmdline 3 :usings '(\"System\" 
\"System.Text\") :results silent
+  int argInt = 0;
+  Int32.TryParse(args[0], out argInt);
+
+  Console.WriteLine(argInt * 14);
+#+end_src"
+    (should (= 42 (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/custom-class-and-main ()
+  "Test custom class with custom main function."
+  (org-test-with-temp-text "#+begin_src csharp :class no :main no :results 
silent
+  internal class ClassA
+  {
+      public ClassA(int i)
+      {
+          this.AnInt = i;
+      }
+
+      public int AnInt { get; set; }
+  }
+
+  public class Program
+  {
+      public static void Main(string[] args)
+      {
+          ClassA daInstance = new(123);
+
+          Console.WriteLine(daInstance.AnInt);
+      }
+  }
+#+end_src"
+    (should (= 123 (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/tabular-format-output ()
+  "Test for tabular output format."
+  (org-test-with-temp-text "#+begin_src csharp :results table silent
+  Console.WriteLine($\"In, questo, mondo, una, cosa\");
+  Console.WriteLine($\"si, perde,  una,   si, trova\");
+#+end_src"
+    (should (equal '(("In" "questo" "mondo" "una" "cosa")
+                     ("si" "perde" "una" "si" "trova"))
+                   (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/nuget-reference ()
+  "Test with nuget reference."
+  (org-test-with-temp-text "#+begin_src csharp :references 
'((\"Newtonsoft.Json\" . \"13.0.3\")) :usings '(\"System\" \"Newtonsoft.Json\") 
:main no :project \"json-test\" :results verbatim silent
+  public class DTO
+  {
+      public int TheInt { get; set; }
+      public string TheString { get; set; }
+  }
+
+  static void Main(string[] args)
+  {
+      DTO myDto = new() { TheInt = 12, TheString = \"ok\" };
+
+      string json = JsonConvert.SerializeObject(myDto, Formatting.Indented);
+      Console.WriteLine($\"{json}\");
+  }
+#+end_src"
+    (should (string= "{\n  \"TheInt\": 12,\n  \"TheString\": \"ok\"\n}\n"
+                     (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-csharp/respects-custom-nuget-config ()
+  "Check that the provided NuGet.config is taken into account when evaluating 
a source block."
+      (let ((nugetconf (make-temp-file "nuget")))
+        (unwind-protect
+            (progn
+              (with-temp-buffer
+                (insert "<?xml version=\"1.0\" encoding=\"utf-8\"?>
+  <configuration>
+      <packageSources>
+          <clear />
+          <add key=\"local\" value=\"./local_packages\" />
+      </packageSources>
+  </configuration>")
+                (write-file nugetconf))
+              (org-test-with-temp-text (format "#+begin_src csharp :references 
'((\"Newtonsoft.Json\" . \"13.0.3\")) :usings '(\"Newtonsoft.Json.Linq\") 
:nugetconfig %S :results raw
+    var js = JObject.Parse(\"{\\\"TheInt\\\": 12, \\\"TheString\\\": 
\\\"ok\\\"}\");
+    Console.Write(js);
+  ,#+end_src" nugetconf)
+                (should-error (org-babel-execute-src-block))))
+          (delete-file nugetconf))))
+
+(ert-deftest test-ob-csharp/additional-project-flags-fails-with-invalid-syntax 
()
+  "Compilation fails when the `org-babel-csharp-additional-project-flags' is 
not xml formatted."
+  (unwind-protect
+      (progn
+        (setq org-babel-csharp-additional-project-flags "somegarbage/>")
+        (org-test-with-temp-text "#+begin_src csharp
+  Console.WriteLine(\"ok\");
+#+end_src"
+          (should (eq nil (org-babel-execute-src-block)))))
+    (setq org-babel-csharp-additional-project-flags nil)))
+
+(ert-deftest test-ob-csharp/additional-project-flags-executes-with-xml-syntax 
()
+  "Compilation succeeds when the `org-babel-csharp-additional-project-flags' 
is xml formatted."
+  (unwind-protect
+      (progn
+        (setq org-babel-csharp-additional-project-flags 
"<LangVersion>latest</LangVersion>")
+        (org-test-with-temp-text "#+begin_src csharp
+  Console.WriteLine(\"ok\");
+#+end_src"
+          (should (string= "ok"
+                           (org-babel-execute-src-block)))))
+    (setq org-babel-csharp-additional-project-flags nil)))
+
+(ert-deftest test-ob-csharp/prologue-and-epilouge-expanded ()
+  "Check if prologue and epilogue are written plain to start and end of the 
expanded block."
+  (org-test-with-temp-text "#+begin_src csharp :prologue \"// File header\" 
:epilogue \"// file ends here\"
+  Console.WriteLine(\"ok\");
+#+end_src"
+    (let ((block-expand (org-babel-expand-src-block)))
+      (should (string= (substring block-expand 0 14) "// File header"))
+      (should (string= (substring block-expand -17) "// file ends here")))))
+
+(ert-deftest test-ob-csharp/invalid-additional-project-flags-fail ()
+  "An invalid setting in `org-babel-csharp-additional-project-flags' fails."
+  (let ((block "#+begin_src csharp\nConsole.WriteLine(1);\n#+end_src")
+        (invalid-flags "<UserCustomPoperty>This is an invalid xml string 
(property not closed)"))
+    (unwind-protect
+        (progn
+          (setq org-babel-csharp-additional-project-flags invalid-flags)
+          (should-not (org-test-with-temp-text block 
(org-babel-execute-src-block))))
+      (setq org-babel-csharp-additional-project-flags nil))))
+
+(ert-deftest test-ob-csharp/valid-additional-project-flags-are-respected ()
+  "A valid setting of `org-babel-csharp-additional-project-flags' is respected 
code block compilation."
+  (let ((block "#+begin_src csharp\nConsole.WriteLine(1);\n#+end_src")
+        (valid-flags "<Configuration>Release</Configuration>"))
+    (unwind-protect
+        (progn
+          (setq org-babel-csharp-additional-project-flags valid-flags)
+          (should (= 1 (org-test-with-temp-text block 
(org-babel-execute-src-block)))))
+      (setq org-babel-csharp-additional-project-flags nil))))
+
+;; requires at least 2 dotnet frameworks installed
+(ert-deftest test-ob-csharp/framework-header-is-configurable ()
+  "Check for additional framework header arguments."
+  (skip-when (< (length (org-babel-csharp--find-dotnet-version)) 2))
+  (let* ((src-result (lambda (v) (org-test-with-temp-text
+                                     (format "#+begin_src csharp :framework 
\"net%s.0\"
+  Console.WriteLine(\"ok\");
+#+end_src" v)
+                                   (org-babel-execute-src-block))))
+         (res-first  (funcall src-result (car 
(org-babel-csharp--find-dotnet-version))))
+         (res-second (funcall src-result (cadr 
(org-babel-csharp--find-dotnet-version)))))
+    (should (string= res-first "ok"))
+    (should (string= res-second "ok"))
+    (should (string= res-first res-second))
+    (should (eq nil (funcall src-result "nonexisting")))))
+
+(ert-deftest test-ob-csharp/runtime-error-without-valid-dotnet-sdk ()
+  "Unless there is a valid dotnet SDK found, evaluating a csharp block fails."
+  (cl-letf (((symbol-function 'org-babel-csharp--find-dotnet-version) 
#'ignore))
+    (org-test-with-temp-text "#+begin_src csharp
+  Console.WriteLine(\"hi\");
+#+end_src"
+      (should-error (org-babel-execute-src-block)))))
+
+
+(provide 'test-ob-csharp)
+;;; test-ob-csharp.el ends here

Reply via email to