feat: add extended language validators and fix tokenizer crash bugs
Expand nimcheck with validators and tokenizers for C, C++, C#, Go, Rust, Ruby, CSS, SQL, Markdown, Dockerfile, Makefile, Kotlin, Lua, Swift, TypeScript, and XML. Register all flavors in the validator factory and improve auto-detection scoring for the new languages. Fix infinite tokenizer loops that caused OOM kills: closeBracket now advances position, finishTokenizeStep guards stalled tokenization, and Jinja/JS tokenizers no longer double-advance on brackets. Fix block-balance false positives in Lua (for/do) and Ruby (postfix unless), SQL trailing-comma detection across whitespace, and Makefile tab literals in test fixtures.
This commit is contained in:
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <locale.h>
|
||||
|
||||
/* Unicode in comments: Привет, 世界, 🌍 */
|
||||
|
||||
int main(void) {
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
/* Unicode string literals */
|
||||
wchar_t *greeting = L"こんにちは";
|
||||
wprintf(L"%ls\n", greeting);
|
||||
|
||||
/* Null byte embedded */
|
||||
char buf[] = "Hello\x00World";
|
||||
printf("Length: %zu\n", strlen(buf)); /* stops at null byte */
|
||||
|
||||
/* Deeply nested structs */
|
||||
struct a { int x; };
|
||||
struct b { struct a a; };
|
||||
struct c { struct b b; };
|
||||
struct d { struct c c; };
|
||||
struct e { struct d d; };
|
||||
struct f { struct e e; };
|
||||
struct f val = {{{{{{42}}}}}};
|
||||
printf("%d\n", val.e.d.c.b.a.x);
|
||||
|
||||
/* Very long function name */
|
||||
void this_is_an_extremely_long_function_name_that_goes_on_and_on_and_on(void) {
|
||||
printf("deep\n");
|
||||
}
|
||||
this_is_an_extremely_long_function_name_that_goes_on_and_on_and_on();
|
||||
|
||||
/* Encoding: BOM-like byte sequences */
|
||||
unsigned char raw[] = {0xEF, 0xBB, 0xBF, 'A', 'B', 'C', 0};
|
||||
printf("%s\n", raw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX 10
|
||||
|
||||
int main(void) {
|
||||
int x = 5
|
||||
printf("x is %d\n", x)
|
||||
|
||||
char *s = "unclosed string;
|
||||
printf("%s\n", s);
|
||||
|
||||
int arr[3] = {1, 2, 3;
|
||||
printf("%d\n", arr[1]);
|
||||
|
||||
if (x > 0 {
|
||||
printf("positive\n");
|
||||
}
|
||||
|
||||
char *p = malloc(10;
|
||||
free(p);
|
||||
|
||||
struct { int a; int b; } s = {1, 2};
|
||||
s.a = ;
|
||||
|
||||
return 0;
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_NAME 64
|
||||
#define GREETING "Hello, World!"
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[MAX_NAME];
|
||||
double score;
|
||||
} Player;
|
||||
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
void greet(const char *name) {
|
||||
printf("%s, %s!\n", GREETING, name);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
Player p = {1, "Alice", 95.5};
|
||||
greet(p.name);
|
||||
|
||||
int result = add(3, 4);
|
||||
printf("3 + 4 = %d\n", result);
|
||||
|
||||
FILE *fp = fopen("test.txt", "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "id=%d,name=%s,score=%.1f\n", p.id, p.name, p.score);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
/* Unicode comment: 你好, 世界, 🌍🚀 */
|
||||
|
||||
int main() {
|
||||
// Unicode string literals
|
||||
std::string utf8 = "こんにちは世界";
|
||||
std::cout << utf8 << std::endl;
|
||||
|
||||
// Null byte in string
|
||||
std::string null_str = std::string("hello\x00world", 11);
|
||||
std::cout << "Null-str size: " << null_str.size() << std::endl;
|
||||
|
||||
// Deeply nested templates
|
||||
template <typename T>
|
||||
struct Wrap { T value; };
|
||||
|
||||
Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<int>>>>>>>> deep = {{{{{{{{42}}}}}}}};
|
||||
std::cout << deep.value.value.value.value.value.value.value.value << std::endl;
|
||||
|
||||
// Extremely long string
|
||||
std::string long_str(10000, 'x');
|
||||
std::cout << "Long string length: " << long_str.length() << std::endl;
|
||||
|
||||
// BOM-like bytes
|
||||
const unsigned char bom[] = {0xEF, 0xBB, 0xBF, 'H', 'i', 0};
|
||||
std::cout << reinterpret_cast<const char*>(bom) << std::endl;
|
||||
|
||||
// Deep lambda nesting
|
||||
auto f1 = [](int x) {
|
||||
return [x](int y) {
|
||||
return [x, y](int z) {
|
||||
return [x, y, z](int w) {
|
||||
return x + y + z + w;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
std::cout << "Nested lambdas: " << f1(1)(2)(3)(4) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
class Broken {
|
||||
public:
|
||||
Broken(int x) : x_(x) {}
|
||||
|
||||
void show() {
|
||||
std::cout << x_ << std::endl;
|
||||
|
||||
int missing_semicolon()
|
||||
return x_;
|
||||
}
|
||||
|
||||
private:
|
||||
int x_
|
||||
};
|
||||
|
||||
int main() {
|
||||
Broken b(42);
|
||||
b.show()
|
||||
|
||||
char *s = "unclosed string;
|
||||
std::cout << s << std::endl;
|
||||
|
||||
if (b.x_ > 0 {
|
||||
std::cout << "positive" << std::endl;
|
||||
}
|
||||
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
std::cout << v[5] << std::endl;
|
||||
|
||||
return 0;
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
class Person {
|
||||
public:
|
||||
Person(int id, std::string name)
|
||||
: id_(id), name_(std::move(name)) {}
|
||||
|
||||
int id() const { return id_; }
|
||||
const std::string& name() const { return name_; }
|
||||
|
||||
virtual void greet() const {
|
||||
std::cout << "Hello, I'm " << name_ << std::endl;
|
||||
}
|
||||
|
||||
virtual ~Person() = default;
|
||||
|
||||
private:
|
||||
int id_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
class Student : public Person {
|
||||
public:
|
||||
Student(int id, std::string name, double gpa)
|
||||
: Person(id, std::move(name)), gpa_(gpa) {}
|
||||
|
||||
void greet() const override {
|
||||
std::cout << "Hi, I'm " << name() << " (GPA: " << gpa_ << ")" << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
double gpa_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T max_value(T a, T b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto p = std::make_unique<Student>(1, "Alice", 3.9);
|
||||
p->greet();
|
||||
|
||||
std::vector<int> nums = {3, 1, 4, 1, 5, 9};
|
||||
std::sort(nums.begin(), nums.end());
|
||||
|
||||
for (int n : nums) {
|
||||
std::cout << n << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "Max of 10 and 20: " << max_value(10, 20) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
// Unicode comment: 你好世界 🌍🚀
|
||||
|
||||
public class EdgeCases
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
// Unicode string literals
|
||||
string unicode = "こんにちは世界";
|
||||
Console.WriteLine(unicode);
|
||||
|
||||
// Null byte embedded
|
||||
string nullStr = "hello\x00world";
|
||||
Console.WriteLine($"Null-str length: {nullStr.Length}");
|
||||
|
||||
// Deeply nested generics
|
||||
var deep = new Dictionary<int, Dictionary<int, Dictionary<int, Dictionary<int, Dictionary<int, string>>>>>
|
||||
{
|
||||
{ 1, new Dictionary<int, Dictionary<int, Dictionary<int, Dictionary<int, string>>>>
|
||||
{
|
||||
{ 2, new Dictionary<int, Dictionary<int, Dictionary<int, string>>>
|
||||
{
|
||||
{ 3, new Dictionary<int, Dictionary<int, string>>
|
||||
{
|
||||
{ 4, new Dictionary<int, string> { { 5, "deep" } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Console.WriteLine(deep[1][2][3][4][5]);
|
||||
|
||||
// Very long string
|
||||
string longStr = new string('x', 10000);
|
||||
Console.WriteLine($"Long string length: {longStr.Length}");
|
||||
|
||||
// BOM prefix
|
||||
byte[] bom = { 0xEF, 0xBB, 0xBF, (byte)'H', (byte)'i' };
|
||||
Console.WriteLine(Encoding.UTF8.GetString(bom));
|
||||
|
||||
// Deep exception nesting
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
throw new InvalidOperationException("inner");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ApplicationException("middle", ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("outer", ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Nested exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Broken
|
||||
{
|
||||
public class BrokenClass
|
||||
{
|
||||
public int X { get; set; }
|
||||
public string Name;
|
||||
|
||||
public BrokenClass(int x, string name)
|
||||
{
|
||||
X = x;
|
||||
Name = name
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
Console.WriteLine($"{Name}: {X}")
|
||||
}
|
||||
|
||||
public int MissingBody()
|
||||
}
|
||||
|
||||
public class Derived : BrokenClass
|
||||
{
|
||||
public Derived() : base(42, "test")
|
||||
{
|
||||
}
|
||||
|
||||
public void BrokenMethod()
|
||||
{
|
||||
string s = "unclosed string;
|
||||
Console.WriteLine(s);
|
||||
|
||||
if (X > 0
|
||||
Console.WriteLine("positive");
|
||||
}
|
||||
|
||||
int[] arr = {1, 2, 3;
|
||||
Console.WriteLine(arr[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
var obj = new Derived();
|
||||
obj.Show()
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Fixtures
|
||||
{
|
||||
public class Person
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
public Person(int id, string name, string email)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Email = email;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name} <{Email}>";
|
||||
}
|
||||
}
|
||||
|
||||
public class Repository<T>
|
||||
{
|
||||
private readonly Dictionary<int, T> _items = new();
|
||||
|
||||
public void Add(int key, T item)
|
||||
{
|
||||
_items[key] = item;
|
||||
}
|
||||
|
||||
public T? Find(int key)
|
||||
{
|
||||
return _items.TryGetValue(key, out var item) ? item : default;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAll()
|
||||
{
|
||||
return _items.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Helpers
|
||||
{
|
||||
public static int Add(int a, int b) => a + b;
|
||||
|
||||
public static IEnumerable<T> Filter<T>(IEnumerable<T> items, Func<T, bool> predicate)
|
||||
{
|
||||
return items.Where(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var repo = new Repository<Person>();
|
||||
repo.Add(1, new Person(1, "Alice", "alice@example.com"));
|
||||
repo.Add(2, new Person(2, "Bob", "bob@example.com"));
|
||||
|
||||
var active = repo.GetAll();
|
||||
foreach (var p in active)
|
||||
{
|
||||
Console.WriteLine(p);
|
||||
}
|
||||
|
||||
Console.WriteLine($"3 + 4 = {Helpers.Add(3, 4)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
:root {
|
||||
--color-primary: #2563eb;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
background: linear-gradient(135deg, var(--color-primary), #7c3aed);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.625rem 1.25rem;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateX(-20px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.navbar {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
:root {
|
||||
--color-primary: #2563eb;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
background: linear-gradient(135deg, var(--color-primary), #7c3aed);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.625rem 1.25rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.navbar
|
||||
flex-direction: column;
|
||||
}
|
||||
Vendored
+159
@@ -0,0 +1,159 @@
|
||||
:root {
|
||||
--color-primary: #2563eb;
|
||||
--color-primary-hover: #1d4ed8;
|
||||
--color-bg: #f8fafc;
|
||||
--color-text: #1e293b;
|
||||
--color-border: #e2e8f0;
|
||||
--radius-md: 0.5rem;
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--font-sans: "Inter", system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 2rem;
|
||||
background: linear-gradient(135deg, var(--color-primary), #7c3aed);
|
||||
}
|
||||
|
||||
.navbar .logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: rgb(255 255 255 / 0.85);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-sm);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.card__title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.card__body {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease, transform 0.1s ease;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.btn--primary {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn--primary:hover {
|
||||
background: var(--color-primary-hover);
|
||||
}
|
||||
|
||||
.btn--outline {
|
||||
background: transparent;
|
||||
border: 2px solid var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.animate-in {
|
||||
animation: fadeIn 0.4s ease-out both;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.navbar {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-bg: #0f172a;
|
||||
--color-text: #e2e8f0;
|
||||
--color-border: #334155;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #1e293b;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM alpine:3.20 as builder
|
||||
RUN apk add --no-cache gcc musl-dev make
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
RUN make release
|
||||
FROM alpine:3.20
|
||||
RUN apk add --no-cache pcre openssl
|
||||
COPY --from=builder /build/bin/app /usr/local/bin/app
|
||||
ENTRYPOINT ["app"]
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
FROM alpine:3.20 AS builder
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
musl-dev \
|
||||
make \
|
||||
|
||||
FROM alpine:3.20
|
||||
|
||||
COPY --from=builder /build/bin/app /usr/local/bin/app
|
||||
|
||||
ENTRYPOINT ["app"
|
||||
CMD ["--help"]
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
FROM alpine:3.20 AS builder
|
||||
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
musl-dev \
|
||||
pcre-dev \
|
||||
openssl-dev \
|
||||
make \
|
||||
git
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN make release \
|
||||
&& strip bin/nimcheck
|
||||
|
||||
FROM alpine:3.20 AS runtime
|
||||
|
||||
RUN apk add --no-cache \
|
||||
pcre \
|
||||
openssl \
|
||||
ca-certificates \
|
||||
tzdata
|
||||
|
||||
ENV TZ=UTC
|
||||
ENV NIMCHECK_LOG_LEVEL=info
|
||||
ENV NIMCHECK_THREADS=4
|
||||
|
||||
RUN addgroup -g 1000 nimcheck \
|
||||
&& adduser -D -u 1000 -G nimcheck nimcheck
|
||||
|
||||
COPY --from=builder /build/bin/nimcheck /usr/local/bin/nimcheck
|
||||
|
||||
RUN chmod 755 /usr/local/bin/nimcheck
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
USER nimcheck
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
CMD nimcheck --version || exit 1
|
||||
|
||||
ENTRYPOINT ["nimcheck"]
|
||||
CMD ["--help"]
|
||||
Vendored
+181
@@ -0,0 +1,181 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"math"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxRetries = 3
|
||||
BaseDelay = 100 * time.Millisecond
|
||||
MaxDelay = 5 * time.Second
|
||||
)
|
||||
|
||||
type Executor[T any] struct {
|
||||
workers int
|
||||
tasks chan func() T
|
||||
results chan T
|
||||
}
|
||||
|
||||
func NewExecutor[T any](workers int) *Executor[T] {
|
||||
return &Executor[T]{
|
||||
workers: workers,
|
||||
tasks: make(chan func() T, workers*2),
|
||||
results: make(chan T, workers*2),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Executor[T]) Submit(task func() T) {
|
||||
e.tasks <- task
|
||||
}
|
||||
|
||||
func (e *Executor[T]) Run(ctx context.Context) []T {
|
||||
for i := 0; i < e.workers; i++ {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case task := <-e.tasks:
|
||||
e.results <- task()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var results []T
|
||||
for i := 0; i < cap(e.tasks); i++ {
|
||||
select {
|
||||
case r := <-e.results:
|
||||
results = append(results, r)
|
||||
case <-ctx.Done():
|
||||
return results
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func retryWithBackoff(operation func() error) error {
|
||||
var err error
|
||||
delay := BaseDelay
|
||||
|
||||
for i := 0; i < MaxRetries; i++ {
|
||||
if err = operation(); err == nil {
|
||||
return nil
|
||||
}
|
||||
if i < MaxRetries-1 {
|
||||
jitter := time.Duration(rand.Int63n(int64(delay / 2)))
|
||||
time.Sleep(delay + jitter)
|
||||
delay = time.Duration(math.Min(float64(delay*2), float64(MaxDelay)))
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("operation failed after %d retries: %w", MaxRetries, err)
|
||||
}
|
||||
|
||||
func HashContent(r io.Reader) (string, error) {
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, r); err != nil {
|
||||
return "", fmt.Errorf("hashing failed: %w", err)
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func SliceToMap[K comparable, V any](items []V, keyFn func(V) K) map[K]V {
|
||||
result := make(map[K]V, len(items))
|
||||
for _, item := range items {
|
||||
result[keyFn(item)] = item
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func InverseMap[K, V comparable](m map[K]V) map[V]K {
|
||||
result := make(map[V]K, len(m))
|
||||
for k, v := range m {
|
||||
result[v] = k
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var counter atomic.Int64
|
||||
|
||||
func nextID() int64 {
|
||||
return counter.Add(1)
|
||||
}
|
||||
|
||||
type Enum interface {
|
||||
~int
|
||||
String() string
|
||||
}
|
||||
|
||||
type Color int
|
||||
|
||||
const (
|
||||
Red Color = iota
|
||||
Green
|
||||
Blue
|
||||
)
|
||||
|
||||
func (c Color) String() string {
|
||||
return [...]string{"red", "green", "blue"}[c]
|
||||
}
|
||||
|
||||
func Must[T any](val T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
type TransformFunc[T, U any] func(T) U
|
||||
|
||||
func Chain[T, U, V any](f TransformFunc[T, U], g TransformFunc[U, V]) TransformFunc[T, V] {
|
||||
return func(t T) V {
|
||||
return g(f(t))
|
||||
}
|
||||
}
|
||||
|
||||
func zero[T any]() T {
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
|
||||
func Map[T, U any](items []T, fn func(T) U) []U {
|
||||
result := make([]U, len(items))
|
||||
for i, item := range items {
|
||||
result[i] = fn(item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func Filter[T any](items []T, fn func(T) bool) []T {
|
||||
var result []T
|
||||
for _, item := range items {
|
||||
if fn(item) {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func Reduce[T, U any](items []T, init U, fn func(U, T) U) U {
|
||||
acc := init
|
||||
for _, item := range items {
|
||||
acc = fn(acc, item)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func init() {
|
||||
fmt.Println("initializing package main")
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
x := 42
|
||||
fmt.Println(x
|
||||
|
||||
func brokenFunc(x int) string {
|
||||
return x
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
func (u User) Greet() string {
|
||||
return "Hello, " + .Name
|
||||
|
||||
switch x {
|
||||
case 1:
|
||||
fmt.Println("one")
|
||||
case 2:
|
||||
fmt.Println("two")
|
||||
}
|
||||
Vendored
+149
@@ -0,0 +1,149 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Active bool `json:"active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type UserService struct {
|
||||
mu sync.RWMutex
|
||||
users map[int]*User
|
||||
}
|
||||
|
||||
func NewUserService() *UserService {
|
||||
return &UserService{
|
||||
users: make(map[int]*User),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserService) Add(user *User) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if user.Name == "" {
|
||||
return errors.New("name is required")
|
||||
}
|
||||
if !strings.Contains(user.Email, "@") {
|
||||
return errors.New("invalid email")
|
||||
}
|
||||
if _, exists := s.users[user.ID]; exists {
|
||||
return errors.New("user already exists")
|
||||
}
|
||||
s.users[user.ID] = user
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *UserService) FindByID(id int) (*User, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
user, ok := s.users[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("user %d not found", id)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) FindByEmail(email string) *User {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
for _, u := range s.users {
|
||||
if u.Email == email {
|
||||
return u
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *UserService) All() []*User {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
result := make([]*User, 0, len(s.users))
|
||||
for _, u := range s.users {
|
||||
result = append(result, u)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func loggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
log.Printf("%s %s %s", r.Method, r.URL.Path, r.RemoteAddr)
|
||||
next.ServeHTTP(w, r)
|
||||
log.Printf("%s completed in %v", r.URL.Path, time.Since(start))
|
||||
})
|
||||
}
|
||||
|
||||
func handleUsers(svc *UserService) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
users := svc.All()
|
||||
json.NewEncoder(w).Encode(users)
|
||||
|
||||
case http.MethodPost:
|
||||
var user User
|
||||
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := svc.Add(&user); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusConflict)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(user)
|
||||
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
svc := NewUserService()
|
||||
svc.Add(&User{
|
||||
ID: 1,
|
||||
Name: "Alice",
|
||||
Email: "alice@example.com",
|
||||
Active: true,
|
||||
})
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/users", handleUsers(svc))
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: loggingMiddleware(mux),
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
log.Printf("Server starting on :%s", port)
|
||||
log.Fatal(server.ListenAndServe())
|
||||
}
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
package fixtures;
|
||||
|
||||
import java.util.*;
|
||||
import java.nio.charset.*;
|
||||
|
||||
// Unicode comment: 你好世界 🌍🚀
|
||||
|
||||
public class EdgeCases {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Unicode string literals
|
||||
String unicode = "こんにちは世界";
|
||||
System.out.println(unicode);
|
||||
|
||||
// Null byte in string
|
||||
String nullStr = "hello\u0000world";
|
||||
System.out.println("Null-str length: " + nullStr.length());
|
||||
|
||||
// Deeply nested classes
|
||||
Map<Integer, Map<Integer, Map<Integer, Map<Integer, Map<Integer, String>>>>> deep =
|
||||
new HashMap<>();
|
||||
Map<Integer, Map<Integer, Map<Integer, Map<Integer, String>>>> l4 = new HashMap<>();
|
||||
Map<Integer, Map<Integer, Map<Integer, String>>> l3 = new HashMap<>();
|
||||
Map<Integer, Map<Integer, String>> l2 = new HashMap<>();
|
||||
Map<Integer, String> l1 = new HashMap<>();
|
||||
l1.put(5, "deep");
|
||||
l2.put(4, l1);
|
||||
l3.put(3, l2);
|
||||
l4.put(2, l3);
|
||||
deep.put(1, l4);
|
||||
System.out.println(deep.get(1).get(2).get(3).get(4).get(5));
|
||||
|
||||
// Very long string
|
||||
StringBuilder sb = new StringBuilder(10000);
|
||||
for (int i = 0; i < 10000; i++) sb.append('x');
|
||||
System.out.println("Long string length: " + sb.length());
|
||||
|
||||
// BOM prefix
|
||||
byte[] bom = {(byte)0xEF, (byte)0xBB, (byte)0xBF, (byte)'H', (byte)'i'};
|
||||
System.out.println(new String(bom, StandardCharsets.UTF_8));
|
||||
|
||||
// Deep exception chaining
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
throw new RuntimeException("inner");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("middle", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("outer", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Chained: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Deep array nesting
|
||||
int[][][][][][] deepArray = new int[2][2][2][2][2][2];
|
||||
deepArray[0][0][0][0][0][0] = 42;
|
||||
System.out.println("Deep array: " + deepArray[0][0][0][0][0][0]);
|
||||
}
|
||||
}
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
package fixtures;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Invalid {
|
||||
private int x;
|
||||
|
||||
public Invalid(int x) {
|
||||
this.x = x
|
||||
}
|
||||
|
||||
public void show() {
|
||||
System.out.println(x)
|
||||
}
|
||||
|
||||
public String broken() {
|
||||
String s = "unclosed string;
|
||||
return s;
|
||||
|
||||
public int missingBody()
|
||||
|
||||
public void badIf() {
|
||||
if (x > 0 {
|
||||
System.out.println("positive");
|
||||
}
|
||||
}
|
||||
|
||||
public void badArray() {
|
||||
int[] arr = {1, 2, 3;
|
||||
System.out.println(arr[0]);
|
||||
}
|
||||
|
||||
public void missingSemicolon() {
|
||||
int a = 5
|
||||
int b = 10;
|
||||
int c = a + b
|
||||
System.out.println(c);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Invalid obj = new Invalid(42);
|
||||
obj.show()
|
||||
}
|
||||
}
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
package fixtures;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
public class Valid {
|
||||
public static class Person {
|
||||
private final int id;
|
||||
private final String name;
|
||||
private final String email;
|
||||
|
||||
public Person(int id, String name, String email) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public int getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public String getEmail() { return email; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " <" + email + ">";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Repository<T> {
|
||||
private final Map<Integer, T> items = new HashMap<>();
|
||||
|
||||
public void add(int key, T item) {
|
||||
items.put(key, item);
|
||||
}
|
||||
|
||||
public Optional<T> find(int key) {
|
||||
return Optional.ofNullable(items.get(key));
|
||||
}
|
||||
|
||||
public List<T> getAll() {
|
||||
return new ArrayList<>(items.values());
|
||||
}
|
||||
}
|
||||
|
||||
public static int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Repository<Person> repo = new Repository<>();
|
||||
repo.add(1, new Person(1, "Alice", "alice@example.com"));
|
||||
repo.add(2, new Person(2, "Bob", "bob@example.com"));
|
||||
|
||||
repo.getAll().stream()
|
||||
.map(Person::getName)
|
||||
.forEach(System.out::println);
|
||||
|
||||
System.out.println("3 + 4 = " + add(3, 4));
|
||||
}
|
||||
}
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
package fixtures
|
||||
|
||||
// Unicode comment: 你好世界 🌍🚀
|
||||
|
||||
fun main() {
|
||||
// Unicode string literals
|
||||
val unicode = "こんにちは世界"
|
||||
println(unicode)
|
||||
|
||||
// Null byte in string
|
||||
val nullStr = "hello\u0000world"
|
||||
println("Null-str length: ${nullStr.length}")
|
||||
|
||||
// Deeply nested generics
|
||||
val deep = mapOf(
|
||||
1 to mapOf(
|
||||
2 to mapOf(
|
||||
3 to mapOf(
|
||||
4 to mapOf(
|
||||
5 to "deep"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
println(deep[1]?.get(2)?.get(3)?.get(4)?.get(5))
|
||||
|
||||
// Very long string
|
||||
val longStr = "x".repeat(10000)
|
||||
println("Long string length: ${longStr.length}")
|
||||
|
||||
// BOM bytes
|
||||
val bom = byteArrayOf(0xEF.toByte(), 0xBB.toByte(), 0xBF.toByte(), 'H'.code.toByte(), 'i'.code.toByte())
|
||||
println(String(bom))
|
||||
|
||||
// Deep lambda nesting
|
||||
val f1: (Int) -> (Int) -> (Int) -> (Int) -> Int = { x ->
|
||||
{ y ->
|
||||
{ z ->
|
||||
{ w -> x + y + z + w }
|
||||
}
|
||||
}
|
||||
}
|
||||
println("Nested lambdas: ${f1(1)(2)(3)(4)}")
|
||||
|
||||
// Infinite sequence (lazy)
|
||||
val naturals = generateSequence(0) { it + 1 }
|
||||
println("First 5 naturals: ${naturals.take(5).toList()}")
|
||||
|
||||
// Deep data class nesting
|
||||
data class A(val x: Int)
|
||||
data class B(val a: A)
|
||||
data class C(val b: B)
|
||||
data class D(val c: C)
|
||||
data class E(val d: D)
|
||||
data class F(val e: E)
|
||||
val deepData = F(E(D(C(B(A(42))))))
|
||||
println("Deep data: ${deepData.e.d.c.b.a.x}")
|
||||
}
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
package fixtures
|
||||
|
||||
data class Broken(
|
||||
val x: Int,
|
||||
val name: String
|
||||
)
|
||||
|
||||
fun show() {
|
||||
println("hello")
|
||||
val x = 5
|
||||
println(x)
|
||||
}
|
||||
|
||||
class BadRepo<T> {
|
||||
private val items = mutableMapOf<Int, T>()
|
||||
|
||||
fun add(key: Int, item: T) {
|
||||
items[key] = item
|
||||
}
|
||||
|
||||
fun find(key: Int): T? = items[key]
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val repo = BadRepo<String>()
|
||||
repo.add(1, "test")
|
||||
|
||||
val s = "unclosed string;
|
||||
println(s)
|
||||
|
||||
val x = 5
|
||||
if (x > 0 {
|
||||
println("positive")
|
||||
}
|
||||
|
||||
val list = listOf(1, 2, 3
|
||||
println(list)
|
||||
|
||||
fun missingReturn(): Int {
|
||||
val a = 5
|
||||
}
|
||||
|
||||
println("done")
|
||||
}
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
package fixtures
|
||||
|
||||
data class Person(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val email: String
|
||||
)
|
||||
|
||||
class Repository<T> {
|
||||
private val items = mutableMapOf<Int, T>()
|
||||
|
||||
fun add(key: Int, item: T) {
|
||||
items[key] = item
|
||||
}
|
||||
|
||||
fun find(key: Int): T? = items[key]
|
||||
|
||||
fun getAll(): List<T> = items.values.toList()
|
||||
}
|
||||
|
||||
fun add(a: Int, b: Int): Int = a + b
|
||||
|
||||
fun <T> filter(items: List<T>, predicate: (T) -> Boolean): List<T> {
|
||||
return items.filter(predicate)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val repo = Repository<Person>()
|
||||
repo.add(1, Person(1, "Alice", "alice@example.com"))
|
||||
repo.add(2, Person(2, "Bob", "bob@example.com"))
|
||||
|
||||
repo.getAll().forEach { println(it) }
|
||||
println("3 + 4 = ${add(3, 4)}")
|
||||
|
||||
val numbers = listOf(1, 2, 3, 4, 5)
|
||||
val evens = filter(numbers) { it % 2 == 0 }
|
||||
println("Evens: $evens")
|
||||
}
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
-- Unicode comment: 你好世界 🌍🚀
|
||||
|
||||
-- Unicode string literals
|
||||
local unicode = "こんにちは世界"
|
||||
print(unicode)
|
||||
|
||||
-- Null byte in string
|
||||
local null_str = "hello\0world"
|
||||
print("Null-str length: " .. #null_str)
|
||||
|
||||
-- Deeply nested tables
|
||||
local deep = {
|
||||
a = {
|
||||
b = {
|
||||
c = {
|
||||
d = {
|
||||
e = "deep"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print(deep.a.b.c.d.e)
|
||||
|
||||
-- Very long string
|
||||
local long_str = string.rep("x", 10000)
|
||||
print("Long string length: " .. #long_str)
|
||||
|
||||
-- Metatable chains
|
||||
local a = {}
|
||||
local b = {}
|
||||
local c = {}
|
||||
setmetatable(a, {__index = b})
|
||||
setmetatable(b, {__index = c})
|
||||
c.value = 42
|
||||
print("Metatable chain: " .. a.value)
|
||||
|
||||
-- Deep function nesting
|
||||
local function level1()
|
||||
return function()
|
||||
return function()
|
||||
return function()
|
||||
return function()
|
||||
return 42
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
print("Deep functions: " .. level1()()()()())
|
||||
|
||||
-- Coroutine with deep stack
|
||||
local co = coroutine.create(function()
|
||||
local function recurse(n)
|
||||
if n == 0 then
|
||||
coroutine.yield("done")
|
||||
else
|
||||
return recurse(n - 1)
|
||||
end
|
||||
end
|
||||
recurse(100)
|
||||
end)
|
||||
local _, msg = coroutine.resume(co)
|
||||
print("Coroutine: " .. msg)
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
local function broken()
|
||||
local x = 5
|
||||
print(x)
|
||||
end
|
||||
|
||||
local s = "unclosed string
|
||||
print(s)
|
||||
|
||||
local function bad_if(x)
|
||||
if x > 0
|
||||
print("positive")
|
||||
end
|
||||
end
|
||||
|
||||
local function missing_end(x)
|
||||
if x > 0 then
|
||||
print("positive")
|
||||
end
|
||||
|
||||
local t = {1, 2, 3
|
||||
print(t[1])
|
||||
|
||||
local function nested()
|
||||
local function inner()
|
||||
local function deeper()
|
||||
return 42
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print("done"
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
local function add(a, b)
|
||||
return a + b
|
||||
end
|
||||
|
||||
local function greet(name)
|
||||
print("Hello, " .. name .. "!")
|
||||
end
|
||||
|
||||
local Person = {}
|
||||
Person.__index = Person
|
||||
|
||||
function Person:new(id, name, email)
|
||||
local obj = {
|
||||
id = id,
|
||||
name = name,
|
||||
email = email
|
||||
}
|
||||
setmetatable(obj, self)
|
||||
return obj
|
||||
end
|
||||
|
||||
function Person:__tostring()
|
||||
return self.name .. " <" .. self.email .. ">"
|
||||
end
|
||||
|
||||
local Repository = {}
|
||||
Repository.__index = Repository
|
||||
|
||||
function Repository:new()
|
||||
return setmetatable({items = {}}, self)
|
||||
end
|
||||
|
||||
function Repository:add(key, item)
|
||||
self.items[key] = item
|
||||
end
|
||||
|
||||
function Repository:find(key)
|
||||
return self.items[key]
|
||||
end
|
||||
|
||||
function Repository:getAll()
|
||||
local result = {}
|
||||
for _, v in pairs(self.items) do
|
||||
table.insert(result, v)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local repo = Repository:new()
|
||||
repo:add(1, Person:new(1, "Alice", "alice@example.com"))
|
||||
repo:add(2, Person:new(2, "Bob", "bob@example.com"))
|
||||
|
||||
for _, p in ipairs(repo:getAll()) do
|
||||
print(p)
|
||||
end
|
||||
|
||||
print("3 + 4 = " .. add(3, 4))
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
.PHONY: all clean
|
||||
|
||||
CC := gcc
|
||||
CFLAGS := -O2 -Wall
|
||||
|
||||
all: program
|
||||
|
||||
program: main.o util.o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f program *.o
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
.PHONY: all clean
|
||||
|
||||
CC := gcc
|
||||
CFLAGS := -O2 -Wall
|
||||
|
||||
all: program
|
||||
|
||||
program: main.o util.o
|
||||
$(CC) $(CFLAGS) -o $@
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $<
|
||||
echo "unclosed
|
||||
|
||||
clean:
|
||||
rm -f program *.o
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
.PHONY: all build release debug clean test coverage lint install uninstall
|
||||
|
||||
NIM := nim
|
||||
NIM_FLAGS := --hints:off --warnings:off
|
||||
SRC_DIR := src
|
||||
BIN_DIR := bin
|
||||
TEST_DIR := tests
|
||||
BUILD_DIR := build
|
||||
|
||||
SOURCES := $(shell find $(SRC_DIR) -name '*.nim')
|
||||
TESTS := $(shell find $(TEST_DIR) -name '*.nim')
|
||||
OBJECTS := $(patsubst $(SRC_DIR)/%.nim,$(BUILD_DIR)/%.o,$(SOURCES))
|
||||
|
||||
BINARY := $(BIN_DIR)/nimcheck
|
||||
|
||||
all: build
|
||||
|
||||
$(BIN_DIR):
|
||||
mkdir -p $(BIN_DIR)
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.nim | $(BUILD_DIR)
|
||||
$(NIM) compile --compileOnly -d:release -o:$@ $<
|
||||
|
||||
$(BINARY): $(OBJECTS) | $(BIN_DIR)
|
||||
$(NIM) compile -d:release --out:$(BINARY) $(SRC_DIR)/nimcheck.nim
|
||||
|
||||
build: $(BINARY)
|
||||
|
||||
release:
|
||||
$(NIM) compile -d:release --opt:speed --passC:-flto --passL:-flto \
|
||||
--out:$(BINARY) $(SRC_DIR)/nimcheck.nim
|
||||
|
||||
debug:
|
||||
$(NIM) compile -d:debug --lineDir:on --stacktrace:on \
|
||||
--out:$(BINARY) $(SRC_DIR)/nimcheck.nim
|
||||
|
||||
test: build
|
||||
$(NIM) compile --run $(TEST_DIR)/test_all.nim
|
||||
|
||||
coverage:
|
||||
$(NIM) compile --run -d:coverage $(TEST_DIR)/test_all.nim
|
||||
|
||||
lint:
|
||||
$(NIM) check $(SRC_DIR)/nimcheck.nim
|
||||
|
||||
clean:
|
||||
rm -rf $(BIN_DIR) $(BUILD_DIR)
|
||||
find . -name 'nimcache' -type d -exec rm -rf {} + 2>/dev/null || true
|
||||
|
||||
install: release
|
||||
install -m 755 $(BINARY) /usr/local/bin/nimcheck
|
||||
|
||||
uninstall:
|
||||
rm -f /usr/local/bin/nimcheck
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Nimcheck
|
||||
|
||||
---
|
||||
|
||||
> A fast syntax validator.
|
||||
|
||||
## Code Block
|
||||
|
||||
```nim
|
||||
proc hello(): string =
|
||||
result = "Hello"
|
||||
```
|
||||
|
||||
```python
|
||||
def hello() -> str:
|
||||
return "Hello"
|
||||
```
|
||||
|
||||
| A | B | C |
|
||||
|---|---|---|
|
||||
| 1 | 2 | 3 |
|
||||
|
||||
- [x] Done
|
||||
- [ ] Pending
|
||||
|
||||
~~strikethrough~~
|
||||
|
||||
Footnotes[^1]
|
||||
|
||||
[^1]: A footnote.
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
# Nimcheck
|
||||
|
||||
> A fast syntax validator
|
||||
|
||||
## Features
|
||||
|
||||
- Fast
|
||||
- Accurate
|
||||
- 27+ languages
|
||||
|
||||
## Unclosed Code Block
|
||||
|
||||
```nim
|
||||
proc hello(): string =
|
||||
result = "Hello"
|
||||
|
||||
Still in code block without closing fence.
|
||||
|
||||
## Broken Link
|
||||
|
||||
[Broken](
|
||||
|
||||
## Broken Table
|
||||
|
||||
| A | B | C
|
||||
| 1 | 2 | 3 |
|
||||
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
# Nimcheck: Universal Syntax Validator
|
||||
|
||||
[](https://nim-lang.org)
|
||||
[](LICENSE)
|
||||
|
||||
> **Fast, accurate syntax validation for 27+ languages.**
|
||||
> Built in Nim. Zero dependencies beyond the standard library.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Features](#features)
|
||||
2. [Installation](#installation)
|
||||
3. [Quick Start](#quick-start)
|
||||
4. [Usage](#usage)
|
||||
5. [Supported Languages](#supported-languages)
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-language** — validates Nim, Python, JavaScript, TypeScript, PHP, Ruby, Rust, Go, Lua, C, C++, C#, Java, Swift, Kotlin, Bash, HTML, XML, Jinja, JSON, YAML, TOML, CSS, SQL, Markdown, Dockerfile, Makefile
|
||||
- **Blazing fast** — compiles to native code via Nim
|
||||
- **Auto-detection** — identifies language from extension, shebang, or content analysis
|
||||
- **CI-friendly** — exit codes and structured output
|
||||
|
||||
### Detection Methods
|
||||
|
||||
| Priority | Method | Confidence |
|
||||
|----------|-------------|------------|
|
||||
| 1 | Explicit | 100% |
|
||||
| 2 | Extension | 90% |
|
||||
| 3 | Shebang | 95% |
|
||||
| 4 | Content | 70-95% |
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### From Source
|
||||
|
||||
```bash
|
||||
git clone https://github.com/retoor/nimcheck
|
||||
cd nimcheck
|
||||
make release
|
||||
```
|
||||
|
||||
### Via Nimble
|
||||
|
||||
```bash
|
||||
nimble install nimcheck
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
nimcheck src/ # validate all files in src/
|
||||
nimcheck --flavor nim # validate as Nim
|
||||
nimcheck --json # JSON output
|
||||
nimcheck --watch # watch mode
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Validate a single file
|
||||
|
||||
```bash
|
||||
nimcheck src/main.nim
|
||||
```
|
||||
|
||||
### Validate with explicit language
|
||||
|
||||
```bash
|
||||
nimcheck --flavor python script.py
|
||||
```
|
||||
|
||||
### Recursive directory scan
|
||||
|
||||
```bash
|
||||
nimcheck --recursive project/
|
||||
```
|
||||
|
||||
### JSON output for CI
|
||||
|
||||
```bash
|
||||
nimcheck --json --recursive src/ > report.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Supported Languages
|
||||
|
||||
| Language | Extensions |
|
||||
|-------------|-------------------------------------|
|
||||
| Nim | `.nim`, `.nims`, `.nimble` |
|
||||
| Python | `.py`, `.pyw`, `.pyx`, `.pxd` |
|
||||
| JavaScript | `.js`, `.mjs`, `.cjs` |
|
||||
| TypeScript | `.ts`, `.tsx` |
|
||||
| PHP | `.php`, `.phtml` |
|
||||
| HTML | `.html`, `.htm`, `.xhtml` |
|
||||
| XML | `.xml`, `.svg` |
|
||||
| JSON | `.json`, `.jsonc` |
|
||||
| YAML | `.yaml`, `.yml` |
|
||||
| TOML | `.toml` |
|
||||
| CSS | `.css` |
|
||||
| SQL | `.sql` |
|
||||
| Markdown | `.md`, `.markdown` |
|
||||
| Ruby | `.rb` |
|
||||
| Rust | `.rs` |
|
||||
| Go | `.go` |
|
||||
| Lua | `.lua` |
|
||||
| C | `.c`, `.h` |
|
||||
| C++ | `.cpp`, `.cxx`, `.hpp` |
|
||||
| C# | `.cs` |
|
||||
| Java | `.java` |
|
||||
| Swift | `.swift` |
|
||||
| Kotlin | `.kt`, `.kts` |
|
||||
| Bash | `.sh`, `.bash` |
|
||||
| Dockerfile | `Dockerfile` |
|
||||
| Makefile | `Makefile` |
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Retoor](https://github.com/retoor)
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
module EnumerableExtensions
|
||||
refine Array do
|
||||
def second
|
||||
self[1]
|
||||
end
|
||||
|
||||
def middle
|
||||
self[size / 2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
using EnumerableExtensions
|
||||
|
||||
class Singleton
|
||||
private_class_method :new
|
||||
|
||||
@instance = nil
|
||||
|
||||
def self.instance
|
||||
@instance ||= new
|
||||
end
|
||||
end
|
||||
|
||||
module_function
|
||||
|
||||
def log(level, message)
|
||||
warn "[#{level.upcase}] #{message}"
|
||||
end
|
||||
|
||||
require "ostruct"
|
||||
require "set"
|
||||
require "delegate"
|
||||
|
||||
Point = Struct.new(:x, :y, keyword_init: true)
|
||||
p = Point.new(x: 10, y: 20)
|
||||
|
||||
class Stack
|
||||
include Enumerable
|
||||
|
||||
def initialize
|
||||
@items = []
|
||||
end
|
||||
|
||||
def push(item)
|
||||
@items.push(item)
|
||||
end
|
||||
|
||||
def pop
|
||||
@items.pop
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@items.each(&block)
|
||||
end
|
||||
end
|
||||
|
||||
s = Stack.new
|
||||
s.push(1)
|
||||
s.push(2)
|
||||
s.each { |i| puts i }
|
||||
|
||||
$global_count = 0
|
||||
DEFAULT_TIMEOUT = 30
|
||||
|
||||
puts "Global: #{$global_count}, Timeout: #{DEFAULT_TIMEOUT}"
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
class Broken
|
||||
attr_accessor :x
|
||||
|
||||
def missing_end
|
||||
if x > 0
|
||||
puts "positive"
|
||||
|
||||
def wrong_syntax
|
||||
x = 42
|
||||
{x + 1
|
||||
|
||||
end
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env ruby
|
||||
require "json"
|
||||
require "net/http"
|
||||
require "uri"
|
||||
|
||||
class User
|
||||
attr_accessor :id, :name, :email, :active
|
||||
|
||||
def initialize(id:, name:, email:, active: true)
|
||||
@id = id
|
||||
@name = name
|
||||
@email = email
|
||||
@active = active
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{@name} <#{@email}>"
|
||||
end
|
||||
|
||||
def active?
|
||||
@active
|
||||
end
|
||||
|
||||
def deactivate!
|
||||
@active = false
|
||||
end
|
||||
end
|
||||
|
||||
class UserRepository
|
||||
def initialize
|
||||
@users = {}
|
||||
end
|
||||
|
||||
def add(user)
|
||||
@users[user.id] = user
|
||||
end
|
||||
|
||||
def find(id)
|
||||
@users[id]
|
||||
end
|
||||
|
||||
def find_by_email(email)
|
||||
@users.values.find { |u| u.email == email }
|
||||
end
|
||||
|
||||
def active_users
|
||||
@users.values.select(&:active?)
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@users.values.each(&block)
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
@users.values.map(&:to_h).to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_users(url)
|
||||
uri = URI.parse(url)
|
||||
response = Net::HTTP.get_response(uri)
|
||||
raise "HTTP error: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
||||
JSON.parse(response.body)
|
||||
rescue StandardError => e
|
||||
warn "Failed to fetch users: #{e.message}"
|
||||
[]
|
||||
end
|
||||
|
||||
def process_items(items)
|
||||
items.map { |item| yield item }
|
||||
end
|
||||
|
||||
repo = UserRepository.new
|
||||
repo.add(User.new(id: 1, name: "Alice", email: "alice@example.com"))
|
||||
|
||||
repo.each do |user|
|
||||
puts user.to_s
|
||||
end
|
||||
|
||||
puts "Active: #{repo.active_users.size}"
|
||||
Vendored
+147
@@ -0,0 +1,147 @@
|
||||
use std::collections::{HashMap, HashSet, BTreeMap};
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Status {
|
||||
Active,
|
||||
Inactive,
|
||||
Pending,
|
||||
Banned,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TreeNode<T> {
|
||||
pub value: T,
|
||||
pub left: Option<Box<TreeNode<T>>>,
|
||||
pub right: Option<Box<TreeNode<T>>>,
|
||||
}
|
||||
|
||||
impl<T: Ord> TreeNode<T> {
|
||||
pub fn insert(&mut self, value: T) {
|
||||
let target = if value <= self.value {
|
||||
&mut self.left
|
||||
} else {
|
||||
&mut self.right
|
||||
};
|
||||
match target {
|
||||
Some(node) => node.insert(value),
|
||||
None => *target = Some(Box::new(TreeNode {
|
||||
value,
|
||||
left: None,
|
||||
right: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, value: &T) -> bool {
|
||||
&self.value == value
|
||||
|| self.left.as_ref().map_or(false, |n| n.contains(value))
|
||||
|| self.right.as_ref().map_or(false, |n| n.contains(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Monoid {
|
||||
fn empty() -> Self;
|
||||
fn combine(&self, other: &Self) -> Self;
|
||||
}
|
||||
|
||||
impl Monoid for i32 {
|
||||
fn empty() -> Self { 0 }
|
||||
fn combine(&self, other: &Self) -> Self { self + other }
|
||||
}
|
||||
|
||||
impl Monoid for String {
|
||||
fn empty() -> Self { String::new() }
|
||||
fn combine(&self, other: &Self) -> Self { format!("{}{}", self, other) }
|
||||
}
|
||||
|
||||
pub fn reduce<T: Monoid>(items: &[T]) -> T {
|
||||
items.iter().fold(T::empty(), |acc, x| acc.combine(x))
|
||||
}
|
||||
|
||||
macro_rules! vec_of_strings {
|
||||
($($x:expr),*) => (vec![$($x.to_string()),*]);
|
||||
}
|
||||
|
||||
pub struct Lazy<T, F = fn() -> T>
|
||||
where
|
||||
F: Fn() -> T,
|
||||
{
|
||||
value: Option<T>,
|
||||
factory: F,
|
||||
}
|
||||
|
||||
impl<T, F: Fn() -> T> Lazy<T, F> {
|
||||
pub fn new(factory: F) -> Self {
|
||||
Self {
|
||||
value: None,
|
||||
factory,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&mut self) -> &T {
|
||||
self.value.get_or_insert_with(&self.factory)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn parallel_map<T, U, F>(items: Vec<T>, f: F) -> Vec<U>
|
||||
where
|
||||
T: Send + 'static,
|
||||
U: Send + 'static,
|
||||
F: Fn(T) -> U + Send + Sync + 'static,
|
||||
{
|
||||
use tokio::task;
|
||||
let mut handles = Vec::new();
|
||||
for item in items {
|
||||
handles.push(task::spawn(async move { f(item) }));
|
||||
}
|
||||
let mut results = Vec::new();
|
||||
for handle in handles {
|
||||
results.push(handle.await.unwrap());
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
pub struct CustomIterator<T> {
|
||||
items: Vec<T>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone> Iterator for CustomIterator<T> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.index < self.items.len() {
|
||||
let item = self.items[self.index].clone();
|
||||
self.index += 1;
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_PORT: u16 = 8080;
|
||||
static APP_NAME: &str = "nimcheck-rs";
|
||||
thread_local! {
|
||||
static CACHE: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
|
||||
}
|
||||
|
||||
unsafe impl Send for User {}
|
||||
unsafe impl Sync for User {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let mut root = TreeNode { value: 5, left: None, right: None };
|
||||
root.insert(3);
|
||||
root.insert(7);
|
||||
assert!(root.contains(&3));
|
||||
assert!(root.contains(&7));
|
||||
}
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
fn main() {
|
||||
let x = 42
|
||||
let y = vec![1, 2,
|
||||
println!("{}", x
|
||||
|
||||
fn missing_type(x) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
match x {
|
||||
1 => println!("one"),
|
||||
2 => println!("two"),
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
fn new(x: i32, y: i32 -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct User {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub active: bool,
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn new(id: u64, name: &str, email: &str) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name: name.to_string(),
|
||||
email: email.to_string(),
|
||||
active: true,
|
||||
tags: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deactivate(&mut self) {
|
||||
self.active = false;
|
||||
}
|
||||
|
||||
pub fn add_tag(&mut self, tag: &str) {
|
||||
self.tags.push(tag.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for User {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} <{}> [{}]", self.name, self.email, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ApiError {
|
||||
NotFound(String),
|
||||
Unauthorized,
|
||||
Internal(String),
|
||||
}
|
||||
|
||||
impl Debug for ApiError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ApiError::NotFound(msg) => write!(f, "NotFound({})", msg),
|
||||
ApiError::Unauthorized => write!(f, "Unauthorized"),
|
||||
ApiError::Internal(msg) => write!(f, "Internal({})", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, ApiError>;
|
||||
|
||||
pub trait Repository<T> {
|
||||
fn find_by_id(&self, id: u64) -> Result<T>;
|
||||
fn save(&mut self, item: T) -> Result<()>;
|
||||
fn delete(&mut self, id: u64) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct UserRepository {
|
||||
users: HashMap<u64, User>,
|
||||
}
|
||||
|
||||
impl UserRepository {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
users: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Repository<User> for UserRepository {
|
||||
fn find_by_id(&self, id: u64) -> Result<User> {
|
||||
self.users
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or_else(|| ApiError::NotFound(format!("User {}", id)))
|
||||
}
|
||||
|
||||
fn save(&mut self, user: User) -> Result<()> {
|
||||
let id = user.id;
|
||||
self.users.insert(id, user);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete(&mut self, id: u64) -> Result<()> {
|
||||
self.users.remove(&id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn process_users(repo: &UserRepository) -> Result<Vec<String>> {
|
||||
let names: Vec<String> = repo
|
||||
.users
|
||||
.values()
|
||||
.filter(|u| u.active)
|
||||
.map(|u| u.name.clone())
|
||||
.collect();
|
||||
Ok(names)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut repo = UserRepository::new();
|
||||
repo.save(User::new(1, "Alice", "alice@example.com"))?;
|
||||
let names = process_users(&repo)?;
|
||||
println!("{:?}", names);
|
||||
Ok(())
|
||||
}
|
||||
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username VARCHAR(64) NOT NULL UNIQUE,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password CHAR(64) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'user',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE posts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
author_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
slug VARCHAR(200) NOT NULL UNIQUE,
|
||||
body TEXT NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'draft'
|
||||
CHECK (status IN ('draft', 'published', 'archived')),
|
||||
view_count INTEGER NOT NULL DEFAULT 0,
|
||||
published_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
SELECT
|
||||
u.username,
|
||||
u.email,
|
||||
p.title,
|
||||
p.status,
|
||||
p.view_count
|
||||
FROM users u
|
||||
JOIN posts p ON p.author_id = u.id
|
||||
WHERE u.is_active = TRUE
|
||||
AND p.status != 'archived'
|
||||
ORDER BY p.created_at DESC
|
||||
LIMIT 20;
|
||||
|
||||
WITH cte AS (
|
||||
SELECT author_id, COUNT(*) AS cnt
|
||||
FROM posts
|
||||
GROUP BY author_id
|
||||
)
|
||||
SELECT u.username, cte.cnt
|
||||
FROM users u
|
||||
JOIN cte ON cte.author_id = u.id;
|
||||
|
||||
UPDATE posts
|
||||
SET status = 'archived',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE published_at < datetime('now', '-1 year')
|
||||
AND status = 'published';
|
||||
|
||||
SELECT
|
||||
NULL AS maybe_value,
|
||||
COALESCE(u.email, 'no-email') AS safe_email,
|
||||
CASE
|
||||
WHEN p.view_count > 1000 THEN 'popular'
|
||||
WHEN p.view_count > 100 THEN 'moderate'
|
||||
ELSE 'quiet'
|
||||
END AS popularity
|
||||
FROM users u
|
||||
LEFT JOIN posts p ON p.author_id = u.id;
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username VARCHAR(64) NOT NULL UNIQUE,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password CHAR(64) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'user',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
);
|
||||
|
||||
SELECT
|
||||
u.username,
|
||||
u.email,
|
||||
p.title
|
||||
FROM users u
|
||||
JOIN posts p ON p.author_id = u.id
|
||||
WHERE u.is_active = TRUE
|
||||
ORDER BY p.created_at DESC
|
||||
LIMIT 20
|
||||
|
||||
SELECT * FROM;
|
||||
|
||||
CREATE INDEX ON posts(status);
|
||||
|
||||
INSERT INTO users (username, email, password)
|
||||
VALUES ('alice', 'alice@example.com');
|
||||
|
||||
UPDATE posts
|
||||
SET status = 'archived',
|
||||
WHERE published_at < datetime('now', '-1 year');
|
||||
Vendored
+91
@@ -0,0 +1,91 @@
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username VARCHAR(64) NOT NULL UNIQUE,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password CHAR(64) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'user',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE posts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
author_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
slug VARCHAR(200) NOT NULL UNIQUE,
|
||||
body TEXT NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'draft'
|
||||
CHECK (status IN ('draft', 'published', 'archived')),
|
||||
view_count INTEGER NOT NULL DEFAULT 0,
|
||||
published_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE tags (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(50) NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE post_tags (
|
||||
post_id INTEGER NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
|
||||
tag_id INTEGER NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (post_id, tag_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_posts_author ON posts(author_id);
|
||||
CREATE INDEX idx_posts_status ON posts(status);
|
||||
CREATE INDEX idx_posts_slug ON posts(slug);
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
|
||||
INSERT INTO users (username, email, password, role)
|
||||
VALUES ('alice', 'alice@example.com', 'hashed_placeholder', 'admin');
|
||||
|
||||
INSERT INTO tags (name) VALUES ('nim'), ('parsing'), ('devtools');
|
||||
|
||||
INSERT INTO posts (author_id, title, slug, body, status)
|
||||
VALUES (1, 'Introducing Nimcheck', 'introducing-nimcheck',
|
||||
'Nimcheck is a universal syntax validator.', 'published');
|
||||
|
||||
INSERT INTO post_tags (post_id, tag_id)
|
||||
SELECT p.id, t.id
|
||||
FROM posts p, tags t
|
||||
WHERE p.slug = 'introducing-nimcheck'
|
||||
AND t.name IN ('nim', 'parsing', 'devtools');
|
||||
|
||||
SELECT
|
||||
u.username,
|
||||
u.email,
|
||||
p.title,
|
||||
p.status,
|
||||
p.view_count,
|
||||
GROUP_CONCAT(t.name, ', ') AS tag_list
|
||||
FROM users u
|
||||
JOIN posts p ON p.author_id = u.id
|
||||
LEFT JOIN post_tags pt ON pt.post_id = p.id
|
||||
LEFT JOIN tags t ON t.id = pt.tag_id
|
||||
WHERE u.is_active = TRUE
|
||||
AND p.status != 'archived'
|
||||
GROUP BY p.id
|
||||
ORDER BY p.created_at DESC
|
||||
LIMIT 20;
|
||||
|
||||
UPDATE posts
|
||||
SET status = 'archived',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE published_at < datetime('now', '-1 year')
|
||||
AND status = 'published';
|
||||
|
||||
WITH post_stats AS (
|
||||
SELECT
|
||||
author_id,
|
||||
COUNT(*) AS total_posts,
|
||||
SUM(view_count) AS total_views,
|
||||
AVG(view_count) AS avg_views
|
||||
FROM posts
|
||||
GROUP BY author_id
|
||||
)
|
||||
SELECT u.username, ps.total_posts, ps.total_views, ps.avg_views
|
||||
FROM users u
|
||||
JOIN post_stats ps ON ps.author_id = u.id
|
||||
ORDER BY ps.total_views DESC;
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import Foundation
|
||||
|
||||
// Unicode comment: 你好世界 🌍🚀
|
||||
|
||||
// Unicode string literals
|
||||
let unicode = "こんにちは世界"
|
||||
print(unicode)
|
||||
|
||||
// Null byte in string
|
||||
let nullStr = "hello\0world"
|
||||
print("Null-str length: \(nullStr.count)")
|
||||
|
||||
// Deeply nested optionals
|
||||
let deep: Int????? = 42
|
||||
if let l1 = deep,
|
||||
let l2 = l1,
|
||||
let l3 = l2,
|
||||
let l4 = l3 {
|
||||
print("Deep optional: \(l4)")
|
||||
}
|
||||
|
||||
// Very long string
|
||||
let longStr = String(repeating: "x", count: 10000)
|
||||
print("Long string length: \(longStr.count)")
|
||||
|
||||
// BOM bytes
|
||||
let bom = Data([0xEF, 0xBB, 0xBF, 0x48, 0x69])
|
||||
print(String(data: bom, encoding: .utf8) ?? "")
|
||||
|
||||
// Deep closure nesting
|
||||
let f1: (Int) -> (Int) -> (Int) -> (Int) -> Int = { x in
|
||||
{ y in
|
||||
{ z in
|
||||
{ w in x + y + z + w }
|
||||
}
|
||||
}
|
||||
}
|
||||
print("Nested closures: \(f1(1)(2)(3)(4))")
|
||||
|
||||
// Deep dictionary nesting
|
||||
let deepDict: [String: Any] = [
|
||||
"a": [
|
||||
"b": [
|
||||
"c": [
|
||||
"d": [
|
||||
"e": "deep"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
if let a = deepDict["a"] as? [String: Any],
|
||||
let b = a["b"] as? [String: Any],
|
||||
let c = b["c"] as? [String: Any],
|
||||
let d = c["d"] as? [String: Any],
|
||||
let e = d["e"] as? String {
|
||||
print("Deep dict: \(e)")
|
||||
}
|
||||
|
||||
// Deep error nesting
|
||||
enum AppError: Error {
|
||||
case inner(String)
|
||||
case middle(String, Error)
|
||||
case outer(String, Error)
|
||||
}
|
||||
|
||||
do {
|
||||
do {
|
||||
do {
|
||||
throw AppError.inner("oops")
|
||||
} catch {
|
||||
throw AppError.middle("wrapped", error)
|
||||
}
|
||||
} catch {
|
||||
throw AppError.outer("re-wrapped", error)
|
||||
}
|
||||
} catch {
|
||||
print("Nested error: \(error)")
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
import Foundation
|
||||
|
||||
struct Broken {
|
||||
let x: Int
|
||||
let name: String
|
||||
}
|
||||
|
||||
class BadRepo {
|
||||
private var items: [Int: String] = [:]
|
||||
|
||||
func add(key: Int, item: String) {
|
||||
items[key] = item
|
||||
}
|
||||
|
||||
func show() {
|
||||
print("hello")
|
||||
let x = 5
|
||||
print(x)
|
||||
}
|
||||
}
|
||||
|
||||
let s = "unclosed string
|
||||
print(s)
|
||||
|
||||
let x = 5
|
||||
if x > 0 {
|
||||
print("positive")
|
||||
|
||||
func missingReturn() -> Int {
|
||||
let a = 5
|
||||
}
|
||||
|
||||
let list = [1, 2, 3
|
||||
print(list)
|
||||
|
||||
func badFunction() {
|
||||
let y = 5
|
||||
print(y)
|
||||
}
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
import Foundation
|
||||
|
||||
struct Person: Codable {
|
||||
let id: Int
|
||||
let name: String
|
||||
let email: String
|
||||
let active: Bool
|
||||
|
||||
init(id: Int, name: String, email: String, active: Bool = true) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.email = email
|
||||
self.active = active
|
||||
}
|
||||
}
|
||||
|
||||
class Repository<T> {
|
||||
private var items: [Int: T] = [:]
|
||||
|
||||
func add(key: Int, item: T) {
|
||||
items[key] = item
|
||||
}
|
||||
|
||||
func find(key: Int) -> T? {
|
||||
return items[key]
|
||||
}
|
||||
|
||||
func getAll() -> [T] {
|
||||
return Array(items.values)
|
||||
}
|
||||
}
|
||||
|
||||
func add(_ a: Int, _ b: Int) -> Int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
enum Result<T> {
|
||||
case success(T)
|
||||
case failure(Error)
|
||||
}
|
||||
|
||||
let repo = Repository<Person>()
|
||||
repo.add(key: 1, item: Person(id: 1, name: "Alice", email: "alice@example.com"))
|
||||
repo.add(key: 2, item: Person(id: 2, name: "Bob", email: "bob@example.com"))
|
||||
|
||||
for person in repo.getAll() {
|
||||
print("\(person.name) <\(person.email)>")
|
||||
}
|
||||
|
||||
print("3 + 4 = \(add(3, 4))")
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
active: boolean;
|
||||
metadata: Record<string, unknown>;
|
||||
tags: string[];
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
type Result<T> = {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
type DeepPartial<T> = {
|
||||
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
||||
};
|
||||
|
||||
type Nullable<T> = T | null | undefined;
|
||||
|
||||
type UnionToIntersection<U> =
|
||||
(U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
||||
|
||||
class Container<T> {
|
||||
constructor(private value: T) {}
|
||||
|
||||
map<U>(fn: (val: T) => U): Container<U> {
|
||||
return new Container(fn(this.value));
|
||||
}
|
||||
|
||||
flatMap<U>(fn: (val: T) => Container<U>): Container<U> {
|
||||
return fn(this.value);
|
||||
}
|
||||
|
||||
unwrap(): T {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
function pipe<T>(...fns: Array<(arg: T) => T>): (arg: T) => T {
|
||||
return (arg: T) => fns.reduce((acc, fn) => fn(acc), arg);
|
||||
}
|
||||
|
||||
function memoize<T extends (...args: unknown[]) => unknown>(fn: T): T {
|
||||
const cache = new Map<string, unknown>();
|
||||
return ((...args: unknown[]) => {
|
||||
const key = JSON.stringify(args);
|
||||
if (cache.has(key)) return cache.get(key);
|
||||
const result = fn(...args);
|
||||
cache.set(key, result);
|
||||
return result;
|
||||
}) as unknown as T;
|
||||
}
|
||||
|
||||
function withDefault<T>(value: T | undefined | null, fallback: T): T {
|
||||
return value ?? fallback;
|
||||
}
|
||||
|
||||
async function* paginate<T>(url: string, pageSize: number): AsyncGenerator<T[], void, unknown> {
|
||||
let page = 0;
|
||||
while (true) {
|
||||
const response = await fetch(`${url}?page=${page}&size=${pageSize}`);
|
||||
const items: T[] = await response.json();
|
||||
if (items.length === 0) return;
|
||||
yield items;
|
||||
page++;
|
||||
}
|
||||
}
|
||||
|
||||
export { User, Result, Container, pipe, memoize, withDefault, paginate };
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
interface Broken {
|
||||
id number;
|
||||
name: ;
|
||||
email string;
|
||||
}
|
||||
|
||||
type Result<T> = {
|
||||
success: boolean
|
||||
data?: T
|
||||
error: string
|
||||
};
|
||||
|
||||
abstract class BadRepo<T extends id: number>> {
|
||||
protected items: Map<number, T> = new Map();
|
||||
|
||||
findById(id: number): T | undefined {
|
||||
return this.items.get(id);
|
||||
|
||||
validate(entity: T): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchBad(url: string): Promise<Result<User[]>> {
|
||||
const response = await fetch(url);
|
||||
const data: User[[]] = await response.json();
|
||||
return { success: true, data };
|
||||
}
|
||||
|
||||
const x: = 42;
|
||||
const y = x as;
|
||||
|
||||
function missingReturn(x: number): string {
|
||||
if (x > 0)
|
||||
return "positive"
|
||||
}
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
active: boolean;
|
||||
metadata: Record<string, unknown>;
|
||||
tags: string[];
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
type Result<T> = {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
enum LogLevel {
|
||||
Debug = 0,
|
||||
Info = 1,
|
||||
Warn = 2,
|
||||
Error = 3,
|
||||
}
|
||||
|
||||
abstract class BaseRepository<T extends { id: number }> {
|
||||
protected items: Map<number, T> = new Map();
|
||||
|
||||
findById(id: number): T | undefined {
|
||||
return this.items.get(id);
|
||||
}
|
||||
|
||||
abstract validate(entity: T): boolean;
|
||||
|
||||
save(entity: T): Result<T> {
|
||||
if (!this.validate(entity)) {
|
||||
return { success: false, error: "Validation failed", timestamp: Date.now() };
|
||||
}
|
||||
this.items.set(entity.id, entity);
|
||||
return { success: true, data: entity, timestamp: Date.now() };
|
||||
}
|
||||
}
|
||||
|
||||
class UserRepository extends BaseRepository<User> {
|
||||
validate(user: User): boolean {
|
||||
return user.name.length > 0 && user.email.includes("@");
|
||||
}
|
||||
|
||||
findByEmail(email: string): User | undefined {
|
||||
for (const user of this.items.values()) {
|
||||
if (user.email === email) return user;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchUsers(url: string): Promise<Result<User[]>> {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const data: User[] = await response.json();
|
||||
return { success: true, data, timestamp: Date.now() };
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : "Unknown error";
|
||||
return { success: false, error: message, timestamp: Date.now() };
|
||||
}
|
||||
}
|
||||
|
||||
function processItems<T, U>(items: T[], transform: (item: T) => U): U[] {
|
||||
return items.map(transform);
|
||||
}
|
||||
|
||||
const repo = new UserRepository();
|
||||
const user: User = {
|
||||
id: 1,
|
||||
name: "Alice",
|
||||
email: "alice@example.com",
|
||||
active: true,
|
||||
metadata: { role: "admin", level: 42 },
|
||||
tags: ["typescript", "backend"],
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
repo.save(user);
|
||||
|
||||
const names = processItems(repo.items.size > 0 ? Array.from(repo.items.values()) : [], (u) => u.name.toUpperCase());
|
||||
|
||||
export { User, UserRepository, fetchUsers, processItems, LogLevel };
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Unicode comment: 你好世界 🌍🚀 -->
|
||||
<edge-cases>
|
||||
<!-- Unicode content -->
|
||||
<unicode>こんにちは世界🌍🚀</unicode>
|
||||
|
||||
<!-- Null byte in content (will be truncated by XML parser) -->
|
||||
<null-bytes>hello�world</null-bytes>
|
||||
|
||||
<!-- Deeply nested elements -->
|
||||
<level1>
|
||||
<level2>
|
||||
<level3>
|
||||
<level4>
|
||||
<level5>
|
||||
<level6>
|
||||
<level7>
|
||||
<level8>
|
||||
<value>deep</value>
|
||||
</level8>
|
||||
</level7>
|
||||
</level6>
|
||||
</level5>
|
||||
</level4>
|
||||
</level3>
|
||||
</level2>
|
||||
</level1>
|
||||
|
||||
<!-- Very long string content -->
|
||||
<long-string>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</long-string>
|
||||
|
||||
<!-- BOM prefix (encoded) -->
|
||||
<bom>Hi</bom>
|
||||
|
||||
<!-- Special characters -->
|
||||
<special-chars>&<>"'</special-chars>
|
||||
|
||||
<!-- CDATA with special content -->
|
||||
<cdata-example><![CDATA[<script>alert("xss")</script>]]></cdata-example>
|
||||
|
||||
<!-- Deep attribute nesting (flat but many attributes) -->
|
||||
<multi-attrs a="1" b="2" c="3" d="4" e="5" f="6" g="7" h="8" i="9" j="10" k="11" l="12" m="13" n="14" o="15" p="16" q="17" r="18" s="19" t="20">many-attrs</multi-attrs>
|
||||
|
||||
<!-- Mixed content -->
|
||||
<mixed>Some text <child>with child</child> and more text <another/> trailing.</mixed>
|
||||
</edge-cases>
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<fixtures>
|
||||
<person id=1>
|
||||
<name>Alice</name>
|
||||
<email>alice@example.com
|
||||
</person>
|
||||
<person id="2">
|
||||
<name>Bob</name>
|
||||
<email>bob@example.com</email>
|
||||
<active>true</active>
|
||||
</persn>
|
||||
<unclosed>
|
||||
<inner>value
|
||||
<bad>&unknown;</bad>
|
||||
<nested>
|
||||
<unclosedTag>
|
||||
</metadata>
|
||||
</fixtures>
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<fixtures>
|
||||
<person id="1">
|
||||
<name>Alice</name>
|
||||
<email>alice@example.com</email>
|
||||
<active>true</active>
|
||||
<tags>
|
||||
<tag>admin</tag>
|
||||
<tag>backend</tag>
|
||||
</tags>
|
||||
</person>
|
||||
<person id="2">
|
||||
<name>Bob</name>
|
||||
<email>bob@example.com</email>
|
||||
<active>false</active>
|
||||
<tags>
|
||||
<tag>user</tag>
|
||||
</tags>
|
||||
</person>
|
||||
<metadata>
|
||||
<source>test-fixture</source>
|
||||
<version>1.0</version>
|
||||
<generated>2026-07-13</generated>
|
||||
</metadata>
|
||||
</fixtures>
|
||||
@@ -9,10 +9,14 @@ import ./test_python
|
||||
import ./test_python_exhaustive
|
||||
import ./test_javascript
|
||||
import ./test_javascript_exhaustive
|
||||
import ./test_typescript
|
||||
import ./test_typescript_exhaustive
|
||||
import ./test_php
|
||||
import ./test_php_exhaustive
|
||||
import ./test_html
|
||||
import ./test_html_exhaustive
|
||||
import ./test_xml
|
||||
import ./test_xml_exhaustive
|
||||
import ./test_jinja
|
||||
import ./test_jinja_exhaustive
|
||||
import ./test_json_exhaustive
|
||||
@@ -20,4 +24,34 @@ import ./test_yaml_exhaustive
|
||||
import ./test_toml_exhaustive
|
||||
import ./test_config
|
||||
import ./test_mixed
|
||||
import ./test_c
|
||||
import ./test_c_exhaustive
|
||||
import ./test_cpp
|
||||
import ./test_cpp_exhaustive
|
||||
import ./test_java
|
||||
import ./test_java_exhaustive
|
||||
import ./test_csharp
|
||||
import ./test_csharp_exhaustive
|
||||
import ./test_kotlin
|
||||
import ./test_kotlin_exhaustive
|
||||
import ./test_lua
|
||||
import ./test_lua_exhaustive
|
||||
import ./test_swift
|
||||
import ./test_swift_exhaustive
|
||||
import ./test_go
|
||||
import ./test_go_exhaustive
|
||||
import ./test_rust
|
||||
import ./test_rust_exhaustive
|
||||
import ./test_ruby
|
||||
import ./test_ruby_exhaustive
|
||||
import ./test_css
|
||||
import ./test_css_exhaustive
|
||||
import ./test_sql
|
||||
import ./test_sql_exhaustive
|
||||
import ./test_markdown
|
||||
import ./test_markdown_exhaustive
|
||||
import ./test_dockerfile
|
||||
import ./test_dockerfile_exhaustive
|
||||
import ./test_makefile
|
||||
import ./test_makefile_exhaustive
|
||||
import ./test_fuzz
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
## C language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "C Validator":
|
||||
test "valid C code":
|
||||
let result = validateSource(CGoodCode, flavor = lfC)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid C code returns errors":
|
||||
let result = validateSource(CBadCode, flavor = lfC)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .c file":
|
||||
let result = validateFile("tests/fixtures/c/valid.c")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .c file returns errors":
|
||||
let result = validateFile("tests/fixtures/c/invalid.c")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects C":
|
||||
check $detectFlavor(CGoodCode) == "c"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(CGoodCode, flavor = lfC)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,116 @@
|
||||
## C validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "C Exhaustive Tests":
|
||||
|
||||
test "basic valid C with includes":
|
||||
let src = "#include <stdio.h>\nint main(void) { return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "#include <stdio.h>\nint main(void) { char *s = \"hello; return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "mismatched brackets":
|
||||
let src = "#include <stdio.h>\nint main(void) { int a[3) = {1,2,3}; return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "#include <stdio.h>\nint main(void) { /* unclosed comment return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "preprocessor directives":
|
||||
let src = "#include <stdio.h>\n#define MAX 100\nint main(void) { return MAX; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "/* just a comment */\n// another comment\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "nested function calls":
|
||||
let src = "#include <stdio.h>\nint main(void) { printf(\"%d\\n\", abs(-5)); return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "binary null byte injection":
|
||||
let src = "int x = 5;\x00int y = 10;\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very deep bracket nesting":
|
||||
let src = "int x = ((((((((((((((((((((42))))))))))))))))))));\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode in string":
|
||||
let src = "#include <stdio.h>\nint main(void) { char *s = \"caf\u00e9\"; return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "typedef and struct":
|
||||
let src = "typedef struct { int x; int y; } Point;\nint main(void) { Point p = {1,2}; return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple function definitions":
|
||||
let src = "int add(int a, int b) { return a + b; }\nint sub(int a, int b) { return a - b; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "pointer declarations":
|
||||
let src = "int main(void) { int x = 42; int *ptr = &x; return *ptr; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "missing semicolon":
|
||||
let src = "#include <stdio.h>\nint main(void) { int x = 5 return x; }\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long single line":
|
||||
let src = "int x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;\n"
|
||||
let result = validateSource(src, flavor = lfC)
|
||||
check result.errors.len >= 0
|
||||
+293
-33
@@ -5,35 +5,30 @@
|
||||
import std/[strutils, json]
|
||||
|
||||
const
|
||||
NimGoodCode* = """
|
||||
proc add*(a, b: int): int =
|
||||
NimGoodCode* = """proc add*(a, b: int): int =
|
||||
## Add two numbers.
|
||||
result = a + b
|
||||
"""
|
||||
|
||||
NimBadCode* = """
|
||||
proc add*(a, b: int): int =
|
||||
NimBadCode* = """proc add*(a, b: int): int =
|
||||
## Add two numbers.
|
||||
result = a + b
|
||||
let x = "
|
||||
|
||||
"""
|
||||
|
||||
BashGoodCode* = """
|
||||
#!/usr/bin/env bash
|
||||
BashGoodCode* = """#!/usr/bin/env bash
|
||||
echo "Hello, world!"
|
||||
for i in 1 2 3; do
|
||||
echo $i
|
||||
done
|
||||
"""
|
||||
|
||||
BashBadCode* = """
|
||||
#!/usr/bin/env bash
|
||||
BashBadCode* = """#!/usr/bin/env bash
|
||||
echo ${unclosed
|
||||
"""
|
||||
|
||||
PythonGoodCode* = """
|
||||
def hello(name: str) -> str:
|
||||
PythonGoodCode* = """def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
class Greeter:
|
||||
@@ -41,50 +36,44 @@ class Greeter:
|
||||
self.prefix = prefix
|
||||
"""
|
||||
|
||||
PythonBadCode* = """
|
||||
def broken():
|
||||
PythonBadCode* = """def broken():
|
||||
x = "
|
||||
"""
|
||||
|
||||
JsGoodCode* = """
|
||||
function greet(name) {
|
||||
JsGoodCode* = """function greet(name) {
|
||||
return `Hello, ${name}!`;
|
||||
}
|
||||
|
||||
const nums = [1, 2, 3];
|
||||
"""
|
||||
|
||||
JsBadCode* = """
|
||||
function broken() {
|
||||
JsBadCode* = """function broken() {
|
||||
const x = `
|
||||
"""
|
||||
|
||||
PhpGoodCode* = """
|
||||
<?php
|
||||
PhpGoodCode* = """<?php
|
||||
function greet(string $name): string {
|
||||
return "Hello, $name!";
|
||||
}
|
||||
"""
|
||||
|
||||
PhpBadCode* = """<?php
|
||||
$a = "unclosed_end"""
|
||||
$a = "unclosed_end
|
||||
"""
|
||||
|
||||
HtmlGoodCode* = """
|
||||
<!DOCTYPE html>
|
||||
HtmlGoodCode* = """<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Test</title></head>
|
||||
<body><p>Hello</p></body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
HtmlBadCode* = """
|
||||
<!DOCTYPE html>
|
||||
HtmlBadCode* = """<!DOCTYPE html>
|
||||
<html>
|
||||
<body><p>Unclosed tag
|
||||
"""
|
||||
|
||||
JinjaGoodCode* = """
|
||||
{% extends "base.html" %}
|
||||
JinjaGoodCode* = """{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>{{ title }}</h1>
|
||||
<p>{{ content }}</p>
|
||||
@@ -94,21 +83,18 @@ $a = "unclosed_end"""
|
||||
JsonGoodCode* = """{"name": "test", "value": 42}"""
|
||||
JsonBadCode* = """{"name": "test", "value": }"""
|
||||
|
||||
YamlGoodCode* = """
|
||||
name: test
|
||||
YamlGoodCode* = """name: test
|
||||
version: 1.0
|
||||
dependencies:
|
||||
- lib1
|
||||
- lib2
|
||||
"""
|
||||
|
||||
YamlBadCode* = """
|
||||
name: test
|
||||
YamlBadCode* = """name: test
|
||||
: invalid
|
||||
"""
|
||||
|
||||
TomlGoodCode* = """
|
||||
[package]
|
||||
TomlGoodCode* = """[package]
|
||||
name = "test"
|
||||
version = "1.0.0"
|
||||
|
||||
@@ -117,8 +103,282 @@ name = "lib"
|
||||
version = "2.0"
|
||||
"""
|
||||
|
||||
TomlBadCode* = """
|
||||
[package]
|
||||
TomlBadCode* = """[package]
|
||||
name = "test"
|
||||
invalid line
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# C
|
||||
# ---------------------------------------------------------------------------
|
||||
CGoodCode* = """#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello, world!\\n");
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
CBadCode* = """#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello, world!\\n")
|
||||
return 0
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# C++
|
||||
# ---------------------------------------------------------------------------
|
||||
CppGoodCode* = """#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
std::vector<int> nums = {1, 2, 3};
|
||||
for (auto n : nums) {
|
||||
std::cout << n << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
CppBadCode* = """#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "unclosed
|
||||
return 0;
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# C#
|
||||
# ---------------------------------------------------------------------------
|
||||
CSharpGoodCode* = """using System;
|
||||
|
||||
class Program {
|
||||
static void Main(string[] args) {
|
||||
Console.WriteLine("Hello, world!");
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
CSharpBadCode* = """using System;
|
||||
|
||||
class Program {
|
||||
static void Main(string[] args) {
|
||||
Console.WriteLine("unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Java
|
||||
# ---------------------------------------------------------------------------
|
||||
JavaGoodCode* = """public class Hello {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, world!");
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
JavaBadCode* = """public class Hello {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Kotlin
|
||||
# ---------------------------------------------------------------------------
|
||||
KotlinGoodCode* = """fun main() {
|
||||
println("Hello, world!")
|
||||
}
|
||||
"""
|
||||
|
||||
KotlinBadCode* = """fun main() {
|
||||
println("unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Lua
|
||||
# ---------------------------------------------------------------------------
|
||||
LuaGoodCode* = """function hello(name)
|
||||
print("Hello, " .. name .. "!")
|
||||
end
|
||||
"""
|
||||
|
||||
LuaBadCode* = """function hello(name)
|
||||
print("unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Swift
|
||||
# ---------------------------------------------------------------------------
|
||||
SwiftGoodCode* = """import Foundation
|
||||
|
||||
func hello(name: String) -> String {
|
||||
return "Hello, \\(name)!"
|
||||
}
|
||||
print(hello(name: "world"))
|
||||
"""
|
||||
|
||||
SwiftBadCode* = """import Foundation
|
||||
|
||||
func hello(name: String) -> String {
|
||||
return "unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TypeScript
|
||||
# ---------------------------------------------------------------------------
|
||||
TsGoodCode* = """function greet(name: string): string {
|
||||
return `Hello, ${name}!`;
|
||||
}
|
||||
"""
|
||||
|
||||
TsBadCode* = """function greet(name: string): string {
|
||||
return `unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# XML
|
||||
# ---------------------------------------------------------------------------
|
||||
XmlGoodCode* = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<element attr="value">Content</element>
|
||||
</root>
|
||||
"""
|
||||
|
||||
XmlBadCode* = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<element>Unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Go
|
||||
# ---------------------------------------------------------------------------
|
||||
GoGoodCode* = """package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, world!")
|
||||
}
|
||||
"""
|
||||
|
||||
GoBadCode* = """package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Rust
|
||||
# ---------------------------------------------------------------------------
|
||||
RustGoodCode* = """fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
"""
|
||||
|
||||
RustBadCode* = """fn main() {
|
||||
println!("unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ruby
|
||||
# ---------------------------------------------------------------------------
|
||||
RubyGoodCode* = """def hello
|
||||
puts "Hello, world!"
|
||||
end
|
||||
"""
|
||||
|
||||
RubyBadCode* = """def hello
|
||||
puts "unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CSS
|
||||
# ---------------------------------------------------------------------------
|
||||
CssGoodCode* = """body {
|
||||
color: red;
|
||||
background: blue;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
"""
|
||||
|
||||
CssBadCode* = """body {
|
||||
color: red;
|
||||
background: "unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SQL
|
||||
# ---------------------------------------------------------------------------
|
||||
SqlGoodCode* = """SELECT * FROM users
|
||||
WHERE id = 1
|
||||
ORDER BY name;
|
||||
"""
|
||||
|
||||
SqlBadCode* = """SELECT * FROM users
|
||||
WHERE name = 'unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Markdown
|
||||
# ---------------------------------------------------------------------------
|
||||
MdGoodCode* = """# Hello World
|
||||
|
||||
This is a **markdown** file.
|
||||
|
||||
- Item 1
|
||||
- Item 2
|
||||
|
||||
```python
|
||||
print("hi")
|
||||
```
|
||||
"""
|
||||
|
||||
MdBadCode* = """# Hello World
|
||||
|
||||
```python
|
||||
print("unclosed
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Dockerfile
|
||||
# ---------------------------------------------------------------------------
|
||||
DockerGoodCode* = """FROM ubuntu:latest
|
||||
RUN apt-get update
|
||||
CMD ["echo", "hello"]
|
||||
"""
|
||||
|
||||
DockerBadCode* = """FROM ubuntu:latest
|
||||
RUN echo "
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Makefile
|
||||
# ---------------------------------------------------------------------------
|
||||
MakefileGoodCode* = """CC = gcc
|
||||
CFLAGS = -Wall -O2
|
||||
|
||||
all: hello
|
||||
|
||||
hello: hello.c
|
||||
""" & "\t" & """$(CC) $(CFLAGS) -o hello hello.c
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
""" & "\t" & "rm -f hello\n"
|
||||
|
||||
MakefileBadCode* = """CC = gcc
|
||||
all:
|
||||
""" & "\t" & "echo \"\n"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Garbage/false positive check patterns
|
||||
# ---------------------------------------------------------------------------
|
||||
FalsePositiveDetectionSamples* = """abc def ghi
|
||||
hello
|
||||
42 123 456
|
||||
... --- !!!
|
||||
"""
|
||||
|
||||
+29
-13
@@ -1,4 +1,4 @@
|
||||
## Config file (JSON, YAML, TOML) validator tests.
|
||||
## Config file format validator tests (JSON, YAML, TOML).
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
@@ -11,48 +11,64 @@ proc checkValid(result: ValidationResult, context: string = "") =
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Config Validators":
|
||||
test "JSON valid":
|
||||
checkValid(validateSource(JsonGoodCode, flavor = lfJSON))
|
||||
let result = validateSource(JsonGoodCode, flavor = lfJSON)
|
||||
checkValid(result)
|
||||
|
||||
test "JSON invalid returns errors":
|
||||
check validateSource(JsonBadCode, flavor = lfJSON).errors.len > 0
|
||||
let result = validateSource(JsonBadCode, flavor = lfJSON)
|
||||
checkInvalid(result)
|
||||
|
||||
test "JSON valid file":
|
||||
checkValid(validateFile("tests/fixtures/json/valid.json"))
|
||||
let result = validateFile("tests/fixtures/json/valid.json")
|
||||
checkValid(result)
|
||||
|
||||
test "JSON invalid file returns errors":
|
||||
check validateFile("tests/fixtures/json/invalid.json").errors.len > 0
|
||||
let result = validateFile("tests/fixtures/json/invalid.json")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "JSON detect":
|
||||
check $detectFlavor(JsonGoodCode) == "json"
|
||||
|
||||
test "YAML valid":
|
||||
checkValid(validateSource(YamlGoodCode, flavor = lfYAML))
|
||||
let result = validateSource(YamlGoodCode, flavor = lfYAML)
|
||||
checkValid(result)
|
||||
|
||||
test "YAML invalid returns errors":
|
||||
check validateSource(YamlBadCode, flavor = lfYAML).errors.len > 0
|
||||
let result = validateSource(YamlBadCode, flavor = lfYAML)
|
||||
checkInvalid(result)
|
||||
|
||||
test "YAML valid file":
|
||||
checkValid(validateFile("tests/fixtures/yaml/valid.yaml"))
|
||||
let result = validateFile("tests/fixtures/yaml/valid.yaml")
|
||||
checkValid(result)
|
||||
|
||||
test "YAML invalid file returns errors":
|
||||
check validateFile("tests/fixtures/yaml/invalid.yaml").errors.len > 0
|
||||
let result = validateFile("tests/fixtures/yaml/invalid.yaml")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "YAML detect":
|
||||
check $detectFlavor(YamlGoodCode) == "yaml"
|
||||
|
||||
test "TOML valid":
|
||||
checkValid(validateSource(TomlGoodCode, flavor = lfTOML))
|
||||
let result = validateSource(TomlGoodCode, flavor = lfTOML)
|
||||
checkValid(result)
|
||||
|
||||
test "TOML invalid returns errors":
|
||||
check validateSource(TomlBadCode, flavor = lfTOML).errors.len > 0
|
||||
let result = validateSource(TomlBadCode, flavor = lfTOML)
|
||||
checkInvalid(result)
|
||||
|
||||
test "TOML valid file":
|
||||
checkValid(validateFile("tests/fixtures/toml/valid.toml"))
|
||||
let result = validateFile("tests/fixtures/toml/valid.toml")
|
||||
checkValid(result)
|
||||
|
||||
test "TOML invalid file returns errors":
|
||||
check validateFile("tests/fixtures/toml/invalid.toml").errors.len > 0
|
||||
let result = validateFile("tests/fixtures/toml/invalid.toml")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "TOML detect":
|
||||
check $detectFlavor(TomlGoodCode) == "toml"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
## C++ language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "C++ Validator":
|
||||
test "valid C++ code":
|
||||
let result = validateSource(CppGoodCode, flavor = lfCpp)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid C++ code returns errors":
|
||||
let result = validateSource(CppBadCode, flavor = lfCpp)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .cpp file":
|
||||
let result = validateFile("tests/fixtures/cpp/valid.cpp")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .cpp file returns errors":
|
||||
let result = validateFile("tests/fixtures/cpp/invalid.cpp")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects C++":
|
||||
check $detectFlavor(CppGoodCode) == "cpp"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(CppGoodCode, flavor = lfCpp)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,96 @@
|
||||
## C++ validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "C++ Exhaustive Tests":
|
||||
|
||||
test "basic valid C++ with iostream":
|
||||
let src = "#include <iostream>\nint main() { std::cout << \"hello\"; return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "#include <iostream>\nint main() { std::cout << \"unclosed; return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "class with template":
|
||||
let src = "template<typename T>\nclass Box { T value; public: Box(T v) : value(v) {} };\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "#include <iostream>\nint main() { /* unclosed return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "int x = 5;\x00int y = 10;\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "namespace and using":
|
||||
let src = "#include <iostream>\nusing namespace std;\nint main() { cout << \"hi\"; return 0; }\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "lambda expression":
|
||||
let src = "#include <vector>\nint main() { auto f = [](int x) { return x * 2; }; return f(21); }\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "deep bracket nesting":
|
||||
let src = "int x = ((((((((((((((((((((42))))))))))))))))))));\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple classes":
|
||||
let src = "class A {}; class B {}; class C : public A, public B {};\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "pointer and reference":
|
||||
let src = "int main() { int x = 42; int& r = x; int* p = &x; return *p; }\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long single line":
|
||||
let src = "int x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;\n"
|
||||
let result = validateSource(src, flavor = lfCpp)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## C# language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "C# Validator":
|
||||
test "valid C# code":
|
||||
let result = validateSource(CSharpGoodCode, flavor = lfCSharp)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid C# code returns errors":
|
||||
let result = validateSource(CSharpBadCode, flavor = lfCSharp)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .cs file":
|
||||
let result = validateFile("tests/fixtures/csharp/valid.cs")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .cs file returns errors":
|
||||
let result = validateFile("tests/fixtures/csharp/invalid.cs")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects C#":
|
||||
check $detectFlavor(CSharpGoodCode) == "csharp"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(CSharpGoodCode, flavor = lfCSharp)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,96 @@
|
||||
## C# validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "C# Exhaustive Tests":
|
||||
|
||||
test "basic valid C#":
|
||||
let src = "using System;\nclass Program { static void Main() { Console.WriteLine(\"hi\"); } }\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string":
|
||||
let src = "using System;\nclass Program { static void Main() { string s = \"unclosed; } }\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "class with properties":
|
||||
let src = "class Person { public string Name { get; set; } }\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "int x = 5;\x00int y = 10;\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "generic class":
|
||||
let src = "using System.Collections.Generic;\nclass Box<T> { public T Value { get; set; } }\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "linq expression":
|
||||
let src = "using System.Linq;\nvar result = from x in items where x > 5 select x;\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "async method":
|
||||
let src = "using System.Threading.Tasks;\nclass Test { async Task<int> GetAsync() { return await Task.Run(() => 42); } }\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "deep bracket nesting":
|
||||
let src = "int x = ((((((((((((((((((((42))))))))))))))))))));\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "interface and implementation":
|
||||
let src = "interface IFoo { void Bar(); }\nclass Foo : IFoo { public void Bar() {} }\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "struct definition":
|
||||
let src = "struct Point { public int X; public int Y; }\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long single line":
|
||||
let src = "int x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;\n"
|
||||
let result = validateSource(src, flavor = lfCSharp)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## CSS language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "CSS Validator":
|
||||
test "valid CSS code":
|
||||
let result = validateSource(CssGoodCode, flavor = lfCSS)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid CSS code returns errors":
|
||||
let result = validateSource(CssBadCode, flavor = lfCSS)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .css file":
|
||||
let result = validateFile("tests/fixtures/css/valid.css")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .css file returns errors":
|
||||
let result = validateFile("tests/fixtures/css/invalid.css")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects CSS":
|
||||
check $detectFlavor(CssGoodCode) == "css"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(CssGoodCode, flavor = lfCSS)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,86 @@
|
||||
## CSS validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "CSS Exhaustive Tests":
|
||||
|
||||
test "basic valid CSS":
|
||||
let src = "body { color: red; background: blue; }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string":
|
||||
let src = "body { content: \"hello; }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "media query":
|
||||
let src = "@media (max-width: 600px) { .class { display: none; } }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "body { color: red; /* unclosed }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "keyframes":
|
||||
let src = "@keyframes slide { from { left: 0; } to { left: 100px; } }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode in string":
|
||||
let src = "body::before { content: \"caf\u00e9\"; }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "nested rules":
|
||||
let src = ".parent { .child { color: red; } }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple selectors":
|
||||
let src = "h1, h2, h3 { font-weight: bold; }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long line":
|
||||
let src = ".class { color: " & repeat("very", 100) & "; }\n"
|
||||
let result = validateSource(src, flavor = lfCSS)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,43 @@
|
||||
## Dockerfile language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Dockerfile Validator":
|
||||
test "valid Dockerfile code":
|
||||
let result = validateSource(DockerGoodCode, flavor = lfDockerfile)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Dockerfile code returns errors":
|
||||
let result = validateSource(DockerBadCode, flavor = lfDockerfile)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .Dockerfile file":
|
||||
let result = validateFile("tests/fixtures/dockerfile/valid.Dockerfile")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .Dockerfile file returns errors":
|
||||
let result = validateFile("tests/fixtures/dockerfile/invalid.Dockerfile")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Dockerfile":
|
||||
check $detectFlavor(DockerGoodCode) == "dockerfile"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(DockerGoodCode, flavor = lfDockerfile)
|
||||
check json.kind == JObject
|
||||
|
||||
test "supportedFlavors contains dockerfile":
|
||||
check "dockerfile" in supportedFlavors()
|
||||
@@ -0,0 +1,111 @@
|
||||
## Dockerfile validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Dockerfile Exhaustive Tests":
|
||||
|
||||
test "basic FROM":
|
||||
let src = "FROM ubuntu:latest\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multi-stage build":
|
||||
let src = "FROM node:18 AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\n\nFROM nginx:alpine\nCOPY --from=builder /app/dist /usr/share/nginx/html\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "ARG and ENV":
|
||||
let src = "ARG VERSION=latest\nFROM ubuntu:$VERSION\nENV DEBIAN_FRONTEND=noninteractive\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "COPY with chown":
|
||||
let src = "FROM alpine\nCOPY --chown=app:app . /app\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "RUN with shell form":
|
||||
let src = "FROM ubuntu\nRUN apt-get update && apt-get install -y curl\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "EXPOSE and VOLUME":
|
||||
let src = "FROM nginx\nEXPOSE 80 443\nVOLUME /var/log/nginx\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "USER and WORKDIR":
|
||||
let src = "FROM node:18\nWORKDIR /usr/src/app\nUSER node\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "HEALTHCHECK":
|
||||
let src = "FROM alpine\nHEALTHCHECK --interval=30s CMD wget -q http://localhost/ || exit 1\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "CMD vs ENTRYPOINT":
|
||||
let src = "FROM alpine\nENTRYPOINT [\"/bin/sh\"]\nCMD [\"-c\", \"echo hello\"]\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "FROM alpine\n\x00RUN echo hello\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "comment lines":
|
||||
let src = "# this is a comment\nFROM alpine\n# another comment\nRUN echo hello\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "line continuation with \\":
|
||||
let src = "FROM alpine\nRUN apt-get update && \\\n apt-get install -y curl && \\\n rm -rf /var/lib/apt/lists/*\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "LABEL with spaces":
|
||||
let src = "FROM alpine\nLABEL maintainer=\"John Doe <john@example.com>\"\nLABEL version=\"1.0.0\"\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "ONBUILD trigger":
|
||||
let src = "FROM node:18\nONBUILD COPY . /app\nONBUILD RUN npm install\n"
|
||||
let result = validateSource(src, flavor = lfDockerfile)
|
||||
check result.errors.len >= 0
|
||||
+41
-2
@@ -1,10 +1,18 @@
|
||||
## Fuzz testing module -- Nimcheck against random, binary, and malicious inputs.
|
||||
## Auto-generated. Tests framework robustness, never-crash guarantee.
|
||||
## Includes false-positive prevention tests.
|
||||
|
||||
import std/[unittest, strutils, strformat, math, sequtils]
|
||||
import ../src/nimcheck
|
||||
|
||||
const allLangs* = [lfNim, lfBash, lfPython, lfJavaScript, lfPHP, lfHTML, lfJinja, lfJSON, lfYAML, lfTOML]
|
||||
const allLangs* = [
|
||||
lfNim, lfBash, lfPython, lfJavaScript, lfTypeScript,
|
||||
lfPHP, lfHTML, lfXML, lfJinja, lfJSON, lfYAML, lfTOML,
|
||||
lfC, lfCpp, lfJava, lfCSharp, lfKotlin, lfLua, lfSwift,
|
||||
lfGo, lfRust, lfRuby, lfCSS, lfSQL, lfMarkdown,
|
||||
lfDockerfile, lfMakefile
|
||||
]
|
||||
|
||||
const threeLangs* = [lfNim, lfPython, lfJSON]
|
||||
|
||||
suite "Fuzz and Robustness Tests":
|
||||
@@ -77,7 +85,7 @@ suite "Fuzz and Robustness Tests":
|
||||
|
||||
test "version and supportedFlavors":
|
||||
check version().len > 0
|
||||
check supportedFlavors().len >= 10
|
||||
check supportedFlavors().len >= 28
|
||||
|
||||
test "detectFlavor on all good code samples":
|
||||
discard detectFlavor("proc x() = discard")
|
||||
@@ -90,6 +98,13 @@ suite "Fuzz and Robustness Tests":
|
||||
discard detectFlavor("{\"a\": 1}")
|
||||
discard detectFlavor("a: 1")
|
||||
discard detectFlavor("x = 1")
|
||||
discard detectFlavor("#include <stdio.h>")
|
||||
discard detectFlavor("package main")
|
||||
discard detectFlavor("fn main() {}")
|
||||
discard detectFlavor("fun main() {}")
|
||||
discard detectFlavor("import Foundation")
|
||||
discard detectFlavor("<?xml version=\"1.0\"?>")
|
||||
discard detectFlavor("interface Foo {}")
|
||||
|
||||
test "source with only newlines":
|
||||
let src = "\n\n\n\n\n\n\n\n\n\n"
|
||||
@@ -116,3 +131,27 @@ suite "Fuzz and Robustness Tests":
|
||||
let result = validateFile("/nonexistent/path/file.nim")
|
||||
check result.errors.len > 0
|
||||
check not result.valid
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# False Positive Prevention Tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
test "false positive: short random text returns unknown":
|
||||
let flavor = detectFlavor("abc def ghi")
|
||||
check $flavor == "unknown"
|
||||
|
||||
test "false positive: single token returns unknown":
|
||||
let flavor = detectFlavor("hello")
|
||||
check $flavor == "unknown"
|
||||
|
||||
test "false positive: numbers-only returns unknown":
|
||||
let flavor = detectFlavor("42 123 456 7890")
|
||||
check $flavor == "unknown"
|
||||
|
||||
test "false positive: punctuation-only returns unknown":
|
||||
let flavor = detectFlavor("... --- !!! ??? >>> <<<")
|
||||
check $flavor == "unknown"
|
||||
|
||||
test "false positive: binary-like data returns unknown":
|
||||
let flavor = detectFlavor("\x00\x01\x02\xFF\xFE\xFD\x00\x01\x02\x03")
|
||||
check $flavor == "unknown"
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
## Go language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Go Validator":
|
||||
test "valid Go code":
|
||||
let result = validateSource(GoGoodCode, flavor = lfGo)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Go code returns errors":
|
||||
let result = validateSource(GoBadCode, flavor = lfGo)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .go file":
|
||||
let result = validateFile("tests/fixtures/go/valid.go")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .go file returns errors":
|
||||
let result = validateFile("tests/fixtures/go/invalid.go")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Go":
|
||||
check $detectFlavor(GoGoodCode) == "go"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(GoGoodCode, flavor = lfGo)
|
||||
check json.kind == JObject
|
||||
|
||||
test "supportedFlavors contains go":
|
||||
check "go" in supportedFlavors()
|
||||
@@ -0,0 +1,111 @@
|
||||
## Go validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Go Exhaustive Tests":
|
||||
|
||||
test "basic valid Go package":
|
||||
let src = "package main\n\nfunc main() {}\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "package main\nfunc main() { s := \"hello\n}"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "raw string backtick":
|
||||
let src = "package main\nfunc main() { s := `raw\\nstring` }\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed raw string backtick":
|
||||
let src = "package main\nfunc main() { s := `unclosed\n}"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple return values":
|
||||
let src = "package main\nfunc div(a, b int) (int, error) {\n\tif b == 0 { return 0, nil }\n\treturn a / b, nil\n}\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "defer statement":
|
||||
let src = "package main\nimport \"fmt\"\nfunc main() { defer fmt.Println(\"done\") }\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "goroutine and channel":
|
||||
let src = "package main\nfunc main() {\n\tch := make(chan int)\n\tgo func() { ch <- 42 }()\n\t<-ch\n}\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "struct with methods":
|
||||
let src = "package main\ntype Point struct { X, Y float64 }\nfunc (p Point) Distance() float64 { return p.X + p.Y }\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "interface definition":
|
||||
let src = "package main\ntype Shape interface {\n\tArea() float64\n\tPerimeter() float64\n}\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "type switch":
|
||||
let src = "package main\nfunc test(v interface{}) {\n\tswitch t := v.(type) {\n\tcase int: _ = t\n\tcase string: _ = t\n\t}\n}\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "package main\nfunc main() {}\x00func other() {}\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very deep bracket nesting":
|
||||
let src = "package main\nfunc main() { x := [][][][][][][][][][]{}{}{}{}{}{}{}{}{}{} }\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode identifiers":
|
||||
let src = "package main\nfunc café() int { return 1 }\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only whitespace and comments":
|
||||
let src = "// just a comment\n \n/* another */\n"
|
||||
let result = validateSource(src, flavor = lfGo)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## Java language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Java Validator":
|
||||
test "valid Java code":
|
||||
let result = validateSource(JavaGoodCode, flavor = lfJava)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Java code returns errors":
|
||||
let result = validateSource(JavaBadCode, flavor = lfJava)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .java file":
|
||||
let result = validateFile("tests/fixtures/java/valid.java")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .java file returns errors":
|
||||
let result = validateFile("tests/fixtures/java/invalid.java")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Java":
|
||||
check $detectFlavor(JavaGoodCode) == "java"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(JavaGoodCode, flavor = lfJava)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,116 @@
|
||||
## Java validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Java Exhaustive Tests":
|
||||
|
||||
test "basic valid Java class":
|
||||
let src = "public class Hello {\n public static void main(String[] args) {\n System.out.println(\"hello\");\n }\n}\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "public class Hello {\n public static void main(String[] args) {\n String s = \"hello;\n }\n}\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "mismatched brackets":
|
||||
let src = "public class Hello {\n public static void main(String[] args) {\n int[] arr = new int[3);\n }\n}\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "public class Hello {\n /* unclosed\n public static void main(String[] args) { }\n}\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "generics syntax":
|
||||
let src = "import java.util.*;\npublic class Box<T> { private T value; public T get() { return value; } }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "// just a comment\n/* another */\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "annotation syntax":
|
||||
let src = "import java.lang.*;\n@Deprecated\npublic class Old { }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "binary null byte injection":
|
||||
let src = "public class A { int x = 5; }\x00public class B { int y = 10; }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very deep bracket nesting":
|
||||
let src = "class A { int x = ((((((((((((((((((((42)))))))))))))))))))); }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode in string":
|
||||
let src = "public class Hello {\n public static void main(String[] args) {\n String s = \"caf\u00e9\";\n }\n}\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "interface and implements":
|
||||
let src = "interface Drawable { void draw(); }\nclass Circle implements Drawable { public void draw() {} }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple classes":
|
||||
let src = "class A { int x; }\nclass B { int y; }\nclass C { int z; }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "try-catch-finally":
|
||||
let src = "public class Hello {\n public static void main(String[] args) {\n try { int x = 5; } catch(Exception e) { } finally { }\n }\n}\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "missing semicolon":
|
||||
let src = "class A { int x = 5 }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long single line":
|
||||
let src = "class A { int x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; }\n"
|
||||
let result = validateSource(src, flavor = lfJava)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## Kotlin language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Kotlin Validator":
|
||||
test "valid Kotlin code":
|
||||
let result = validateSource(KotlinGoodCode, flavor = lfKotlin)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Kotlin code returns errors":
|
||||
let result = validateSource(KotlinBadCode, flavor = lfKotlin)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .kt file":
|
||||
let result = validateFile("tests/fixtures/kotlin/valid.kt")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .kt file returns errors":
|
||||
let result = validateFile("tests/fixtures/kotlin/invalid.kt")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Kotlin":
|
||||
check $detectFlavor(KotlinGoodCode) == "kotlin"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(KotlinGoodCode, flavor = lfKotlin)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,116 @@
|
||||
## Kotlin validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Kotlin Exhaustive Tests":
|
||||
|
||||
test "basic valid Kotlin":
|
||||
let src = "fun main() { println(\"hello\") }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "fun main() { val s = \"hello }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "mismatched brackets":
|
||||
let src = "fun main() { val list = listOf(1, 2, 3) }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "fun main() { /* unclosed comment\n println(\"hi\") }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "data class":
|
||||
let src = "data class Person(val name: String, val age: Int)\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "// just a comment\n/* another */\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "lambda and higher-order functions":
|
||||
let src = "fun <T> filter(list: List<T>, pred: (T) -> Boolean): List<T> = list.filter(pred)\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "binary null byte injection":
|
||||
let src = "fun a() {}\x00fun b() {}\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very deep bracket nesting":
|
||||
let src = "val x = ((((((((((((((((((((42))))))))))))))))))))\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode in string":
|
||||
let src = "fun main() { val s = \"caf\u00e9\"; println(s) }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "extension function":
|
||||
let src = "fun String.greet(): String = \"Hello, $this!\"\nfun main() { println(\"world\".greet()) }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple class definitions":
|
||||
let src = "class A { val x = 1 }\nclass B { val y = 2 }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "when expression":
|
||||
let src = "fun describe(x: Any): String = when(x) { 1 -> \"one\"; else -> \"other\" }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null safety operators":
|
||||
let src = "fun main() { val s: String? = null; println(s?.length ?: 0) }\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long single line":
|
||||
let src = "val x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
|
||||
let result = validateSource(src, flavor = lfKotlin)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## Lua language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Lua Validator":
|
||||
test "valid Lua code":
|
||||
let result = validateSource(LuaGoodCode, flavor = lfLua)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Lua code returns errors":
|
||||
let result = validateSource(LuaBadCode, flavor = lfLua)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .lua file":
|
||||
let result = validateFile("tests/fixtures/lua/valid.lua")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .lua file returns errors":
|
||||
let result = validateFile("tests/fixtures/lua/invalid.lua")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Lua":
|
||||
check $detectFlavor(LuaGoodCode) == "lua"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(LuaGoodCode, flavor = lfLua)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,116 @@
|
||||
## Lua validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Lua Exhaustive Tests":
|
||||
|
||||
test "basic valid Lua":
|
||||
let src = "#!/usr/bin/env lua\nprint(\"hello\")\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "local s = \"hello\nprint(s)\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "local x = 5\n--[[ unclosed comment\nprint(x)\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "function definition":
|
||||
let src = "local function add(a, b) return a + b end\nprint(add(3,4))\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "-- just a comment\n--[[ multi-line ]]"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "table construction":
|
||||
let src = "local t = {name = \"test\", value = 42}\nprint(t.name)\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "binary null byte injection":
|
||||
let src = "local x = 5\x00local y = 10\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "nested functions":
|
||||
let src = "local function outer()\n local function inner()\n return 42\n end\n return inner()\nend\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode in string":
|
||||
let src = "local s = \"caf\u00e9\"\nprint(s)\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "metatable operations":
|
||||
let src = "local t = {}\nsetmetatable(t, {__index = {x = 42}})\nprint(t.x)\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple function definitions":
|
||||
let src = "local function a() return 1 end\nlocal function b() return 2 end\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "if-then-else-end":
|
||||
let src = "local x = 5\nif x > 0 then print(\"pos\") else print(\"neg\") end\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "for loop":
|
||||
let src = "for i = 1, 10 do print(i) end\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "while loop":
|
||||
let src = "local x = 0\nwhile x < 5 do x = x + 1 end\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long single line":
|
||||
let src = "local x = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n"
|
||||
let result = validateSource(src, flavor = lfLua)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,43 @@
|
||||
## Makefile language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Makefile Validator":
|
||||
test "valid Makefile code":
|
||||
let result = validateSource(MakefileGoodCode, flavor = lfMakefile)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Makefile code returns errors":
|
||||
let result = validateSource(MakefileBadCode, flavor = lfMakefile)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .Makefile file":
|
||||
let result = validateFile("tests/fixtures/makefile/valid.Makefile")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .Makefile file returns errors":
|
||||
let result = validateFile("tests/fixtures/makefile/invalid.Makefile")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Makefile":
|
||||
check $detectFlavor(MakefileGoodCode) == "makefile"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(MakefileGoodCode, flavor = lfMakefile)
|
||||
check json.kind == JObject
|
||||
|
||||
test "supportedFlavors contains makefile":
|
||||
check "makefile" in supportedFlavors()
|
||||
@@ -0,0 +1,111 @@
|
||||
## Makefile validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Makefile Exhaustive Tests":
|
||||
|
||||
test "basic target":
|
||||
let src = "all:\n\techo hello\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "variables and automatic vars":
|
||||
let src = "CC = gcc\nCFLAGS = -Wall -O2\n\n%.o: %.c\n\t$(CC) $(CFLAGS) -c $< -o $@\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "phony targets":
|
||||
let src = ".PHONY: clean all\n\nall: program\n\nprogram: main.o\n\t$(CC) -o $@ $^\n\nclean:\n\trm -f *.o program\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "conditional directives":
|
||||
let src = "ifeq ($(OS),Windows_NT)\n\tRM = del /Q\nelse\n\tRM = rm -f\nendif\n\nclean:\n\t$(RM) *.o\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "foreach and call":
|
||||
let src = "LIBS = libfoo libbar\n\ndo-thing = echo $(1)\n\nall:\n\t$(foreach lib,$(LIBS),$(call do-thing,$(lib)))\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "shell function":
|
||||
let src = "DATE := $(shell date)\n\nall:\n\techo $(DATE)\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "include directive":
|
||||
let src = "include config.mk\ninclude $(wildcard *.d)\n\nall:\n\techo done\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "export variables":
|
||||
let src = "export PATH := /custom/bin:$(PATH)\nexport VAR_NAME\n\nall:\n\techo done\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "all:\n\techo hello\x00\nclean:\n\trm -f\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "subst and patsubst":
|
||||
let src = "SRC = foo.c bar.c\nOBJ = $(SRC:.c=.o)\nOBJ2 = $(patsubst %.c,%.o,$(SRC))\n\nall:\n\techo $(OBJ) $(OBJ2)\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "vpath directive":
|
||||
let src = "vpath %.c src\nvpath %.h include\n\nall:\n\techo done\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple targets with same recipe":
|
||||
let src = "foo bar baz:\n\techo $@\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "# Makefile comment\n \n## another comment\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "order-only prerequisites":
|
||||
let src = "objdir := build/obj\n\n$(objdir)/%.o: %.c | $(objdir)\n\t$(CC) -c $< -o $@\n\n$(objdir):\n\tmkdir -p $@\n"
|
||||
let result = validateSource(src, flavor = lfMakefile)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,43 @@
|
||||
## Markdown language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Markdown Validator":
|
||||
test "valid Markdown code":
|
||||
let result = validateSource(MdGoodCode, flavor = lfMarkdown)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Markdown code returns errors":
|
||||
let result = validateSource(MdBadCode, flavor = lfMarkdown)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .md file":
|
||||
let result = validateFile("tests/fixtures/markdown/valid.md")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .md file returns errors":
|
||||
let result = validateFile("tests/fixtures/markdown/invalid.md")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Markdown":
|
||||
check $detectFlavor(MdGoodCode) == "markdown"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(MdGoodCode, flavor = lfMarkdown)
|
||||
check json.kind == JObject
|
||||
|
||||
test "supportedFlavors contains markdown":
|
||||
check "markdown" in supportedFlavors()
|
||||
@@ -0,0 +1,111 @@
|
||||
## Markdown validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Markdown Exhaustive Tests":
|
||||
|
||||
test "basic heading and paragraph":
|
||||
let src = "# Hello\n\nThis is a paragraph.\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "fenced code block":
|
||||
let src = "# Code\n\n```python\nprint(\"hello\")\n```\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed fenced code block":
|
||||
let src = "# Code\n\n```python\nprint(\"hello\")\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "table syntax":
|
||||
let src = "| Name | Age |\n|------|-----|\n| Alice | 30 |\n| Bob | 25 |\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "list nesting":
|
||||
let src = "1. First\n - Nested\n - Also nested\n2. Second\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "blockquote with nesting":
|
||||
let src = "> Quote\n> > Nested quote\n> Back to first\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "link and image syntax":
|
||||
let src = "[Google](https://google.com)\n\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "strikethrough and task list":
|
||||
let src = "~~strikethrough~~\n\n- [x] Done\n- [ ] Pending\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "footnote reference":
|
||||
let src = "Here is a footnote[^1].\n\n[^1]: The footnote content.\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "# Title\n\x00## Subtitle\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "HTML in markdown":
|
||||
let src = "<div>\n <p>HTML inside markdown</p>\n</div>\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "definition list":
|
||||
let src = "Term\n: Definition\nAnother\n: Another definition\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only whitespace":
|
||||
let src = " \n\n \n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "horizontal rule variants":
|
||||
let src = "---\n***\n___\n"
|
||||
let result = validateSource(src, flavor = lfMarkdown)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,43 @@
|
||||
## Ruby language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Ruby Validator":
|
||||
test "valid Ruby code":
|
||||
let result = validateSource(RubyGoodCode, flavor = lfRuby)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Ruby code returns errors":
|
||||
let result = validateSource(RubyBadCode, flavor = lfRuby)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .rb file":
|
||||
let result = validateFile("tests/fixtures/ruby/valid.rb")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .rb file returns errors":
|
||||
let result = validateFile("tests/fixtures/ruby/invalid.rb")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Ruby":
|
||||
check $detectFlavor(RubyGoodCode) == "ruby"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(RubyGoodCode, flavor = lfRuby)
|
||||
check json.kind == JObject
|
||||
|
||||
test "supportedFlavors contains ruby":
|
||||
check "ruby" in supportedFlavors()
|
||||
@@ -0,0 +1,106 @@
|
||||
## Ruby validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Ruby Exhaustive Tests":
|
||||
|
||||
test "basic valid method":
|
||||
let src = "def hello(name)\n \"Hello, #{name}!\"\nend\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string":
|
||||
let src = "def broken\n x = \"unclosed\nend\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "symbol and hash syntax":
|
||||
let src = "h = { foo: 1, 'bar' => 2 }\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "block with do/end":
|
||||
let src = "[1, 2, 3].each do |n|\n puts n\nend\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "class with inheritance":
|
||||
let src = "class Animal\n def speak\n \"...\"\n end\nend\nclass Dog < Animal\n def speak\n \"Woof!\"\n end\nend\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "module mixin":
|
||||
let src = "module Greetable\n def greet\n \"Hello!\"\n end\nend\nclass Person\n include Greetable\nend\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "lambda and proc":
|
||||
let src = "add = ->(a, b) { a + b }\nmul = Proc.new { |a, b| a * b }\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "rescue/ensure":
|
||||
let src = "def safe_div(a, b)\n a / b\nrescue ZeroDivisionError\n 0\nensure\n puts \"done\"\nend\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "heredoc string":
|
||||
let src = "s = <<~EOF\n hello world\nEOF\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "x = 1\x00y = 2\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "symbol interpolation":
|
||||
let src = "name = \"world\"\ns = :\"Hello, #{name}\"\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode identifiers":
|
||||
let src = "def méthode\n :ok\nend\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "# just a comment\n \n=begin\nmulti line\n=end\n"
|
||||
let result = validateSource(src, flavor = lfRuby)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,43 @@
|
||||
## Rust language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Rust Validator":
|
||||
test "valid Rust code":
|
||||
let result = validateSource(RustGoodCode, flavor = lfRust)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Rust code returns errors":
|
||||
let result = validateSource(RustBadCode, flavor = lfRust)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .rs file":
|
||||
let result = validateFile("tests/fixtures/rust/valid.rs")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .rs file returns errors":
|
||||
let result = validateFile("tests/fixtures/rust/invalid.rs")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Rust":
|
||||
check $detectFlavor(RustGoodCode) == "rust"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(RustGoodCode, flavor = lfRust)
|
||||
check json.kind == JObject
|
||||
|
||||
test "supportedFlavors contains rust":
|
||||
check "rust" in supportedFlavors()
|
||||
@@ -0,0 +1,111 @@
|
||||
## Rust validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Rust Exhaustive Tests":
|
||||
|
||||
test "basic valid main":
|
||||
let src = "fn main() { println!(\"hello\"); }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "fn main() { let s = \"hello\n}"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "raw string r#":
|
||||
let src = "fn main() { let s = r#\"raw string\"#; }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed raw string":
|
||||
let src = "fn main() { let s = r#\"unclosed\n}"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "struct with derive":
|
||||
let src = "#[derive(Debug, Clone)]\npub struct Point { pub x: f64, pub y: f64 }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "enum with match":
|
||||
let src = "enum Color { Red, Green, Blue }\nfn describe(c: Color) -> &'static str {\n\tmatch c {\n\t\tColor::Red => \"red\",\n\t\tColor::Green => \"green\",\n\t\tColor::Blue => \"blue\",\n\t}\n}\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "impl block":
|
||||
let src = "struct Square { side: f64 }\nimpl Square {\n\tfn area(&self) -> f64 { self.side * self.side }\n}\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "generic function":
|
||||
let src = "fn identity<T>(x: T) -> T { x }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "lifetime annotation":
|
||||
let src = "fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "fn main() {}\x00fn other() {}\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very deep nesting":
|
||||
let src = "fn main() { [[[[[[[[[[[[[[[[[[[[42]]]]]]]]]]]]]]]]]]]]; }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode identifiers":
|
||||
let src = "fn café() -> i32 { 1 }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments and whitespace":
|
||||
let src = "// just a comment\n \n/* block comment */\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "macro invocation":
|
||||
let src = "fn main() { let v = vec![1, 2, 3]; println!(\"{:?}\", v); }\n"
|
||||
let result = validateSource(src, flavor = lfRust)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,43 @@
|
||||
## SQL language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "SQL Validator":
|
||||
test "valid SQL code":
|
||||
let result = validateSource(SqlGoodCode, flavor = lfSQL)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid SQL code returns errors":
|
||||
let result = validateSource(SqlBadCode, flavor = lfSQL)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .sql file":
|
||||
let result = validateFile("tests/fixtures/sql/valid.sql")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .sql file returns errors":
|
||||
let result = validateFile("tests/fixtures/sql/invalid.sql")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects SQL":
|
||||
check $detectFlavor(SqlGoodCode) == "sql"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(SqlGoodCode, flavor = lfSQL)
|
||||
check json.kind == JObject
|
||||
|
||||
test "supportedFlavors contains sql":
|
||||
check "sql" in supportedFlavors()
|
||||
@@ -0,0 +1,111 @@
|
||||
## SQL validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "SQL Exhaustive Tests":
|
||||
|
||||
test "basic SELECT":
|
||||
let src = "SELECT * FROM users;\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "JOIN query":
|
||||
let src = "SELECT u.name, o.total\nFROM users u\nINNER JOIN orders o ON u.id = o.user_id\nWHERE o.total > 100\nORDER BY o.total DESC;\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "subquery":
|
||||
let src = "SELECT name FROM users WHERE id IN (SELECT user_id FROM orders);\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "CREATE TABLE":
|
||||
let src = "CREATE TABLE users (\n id INT PRIMARY KEY,\n name VARCHAR(100) NOT NULL,\n email VARCHAR(255) UNIQUE\n);\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "INSERT with values":
|
||||
let src = "INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.com');\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "UPDATE with WHERE":
|
||||
let src = "UPDATE users SET active = 1 WHERE last_login > '2024-01-01';\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "DELETE with subquery":
|
||||
let src = "DELETE FROM users WHERE id NOT IN (SELECT DISTINCT user_id FROM orders);\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "aggregate functions":
|
||||
let src = "SELECT COUNT(*), AVG(price), MAX(amount), MIN(qty), SUM(total)\nFROM items;\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "GROUP BY with HAVING":
|
||||
let src = "SELECT dept, COUNT(*) as cnt\nFROM employees\nGROUP BY dept\nHAVING cnt > 5;\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "SELECT 1;\x00SELECT 2;\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "string with escaped quote":
|
||||
let src = "SELECT 'O''Brien' AS name;\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "CTE with WITH":
|
||||
let src = "WITH regional_sales AS (\n SELECT region, SUM(amount) AS total\n FROM orders\n GROUP BY region\n)\nSELECT * FROM regional_sales;\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "CREATE INDEX":
|
||||
let src = "CREATE INDEX idx_users_email ON users(email);\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "-- just a comment\n \n/* multi\nline */\n"
|
||||
let result = validateSource(src, flavor = lfSQL)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## Swift language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "Swift Validator":
|
||||
test "valid Swift code":
|
||||
let result = validateSource(SwiftGoodCode, flavor = lfSwift)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid Swift code returns errors":
|
||||
let result = validateSource(SwiftBadCode, flavor = lfSwift)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .swift file":
|
||||
let result = validateFile("tests/fixtures/swift/valid.swift")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .swift file returns errors":
|
||||
let result = validateFile("tests/fixtures/swift/invalid.swift")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects Swift":
|
||||
check $detectFlavor(SwiftGoodCode) == "swift"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(SwiftGoodCode, flavor = lfSwift)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,116 @@
|
||||
## Swift validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "Swift Exhaustive Tests":
|
||||
|
||||
test "basic valid Swift":
|
||||
let src = "import Foundation\nprint(\"hello\")\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed string literal":
|
||||
let src = "let s = \"hello\nprint(s)\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "mismatched brackets":
|
||||
let src = "let arr = [1, 2, 3)\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "/* unclosed comment\nlet x = 5\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "struct and class definitions":
|
||||
let src = "struct Point { var x: Int; var y: Int }\nclass Person { var name: String = \"\" }\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "only comments":
|
||||
let src = "// just a comment\n/* another */\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "closure syntax":
|
||||
let src = "let add = { (a: Int, b: Int) -> Int in return a + b }\nlet result = add(3, 4)\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "binary null byte injection":
|
||||
let src = "let x = 5\x00let y = 10\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very deep bracket nesting":
|
||||
let src = "let x = [[[[[[[[[[[[[[[[[[[[42]]]]]]]]]]]]]]]]]]]]\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode in string":
|
||||
let src = "let s = \"caf\u00e9\"\nprint(s)\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "enum with associated values":
|
||||
let src = "enum Result<T> { case success(T); case failure(Error) }\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple function definitions":
|
||||
let src = "func add(_ a: Int, _ b: Int) -> Int { return a + b }\nfunc sub(_ a: Int, _ b: Int) -> Int { return a - b }\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "guard statement":
|
||||
let src = "func test(x: Int?) { guard let val = x else { return }; print(val) }\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "optionals":
|
||||
let src = "var name: String? = nil\nname = \"Alice\"\nif let n = name { print(n) }\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "very long single line":
|
||||
let src = "let x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
|
||||
let result = validateSource(src, flavor = lfSwift)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## TypeScript language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "TypeScript Validator":
|
||||
test "valid TypeScript code":
|
||||
let result = validateSource(TsGoodCode, flavor = lfTypeScript)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid TypeScript code returns errors":
|
||||
let result = validateSource(TsBadCode, flavor = lfTypeScript)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .ts file":
|
||||
let result = validateFile("tests/fixtures/typescript/valid.ts")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .ts file returns errors":
|
||||
let result = validateFile("tests/fixtures/typescript/invalid.ts")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects TypeScript":
|
||||
check $detectFlavor(TsGoodCode) == "typescript"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(TsGoodCode, flavor = lfTypeScript)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,211 @@
|
||||
## TypeScript validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "TypeScript Exhaustive Tests":
|
||||
|
||||
test "basic valid TypeScript":
|
||||
let src = "function hello(name: string): string { return `Hello, ${name}!`; }\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "arrow function":
|
||||
let src = "const add = (a: number, b: number): number => a + b;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "interface definition":
|
||||
let src = "interface Person { name: string; age: number; }\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "type alias":
|
||||
let src = "type Point = { x: number; y: number; };\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "generic function":
|
||||
let src = "function identity<T>(arg: T): T { return arg; }\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "async/await":
|
||||
let src = "async function fetchData(url: string): Promise<unknown> {\n const res = await fetch(url);\n return res.json();\n}\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "class with constructor":
|
||||
let src = "class Animal {\n constructor(public name: string) {}\n speak(): string { return this.name; }\n}\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "abstract class":
|
||||
let src = "abstract class Base {\n abstract validate(): boolean;\n save(): void {}\n}\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "enum with values":
|
||||
let src = "enum Color { Red = 1, Green = 2, Blue = 4 }\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "union and intersection types":
|
||||
let src = "type Status = 'active' | 'inactive';\ntype Name = string & { brand: never };\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "conditional type":
|
||||
let src = "type IsString<T> = T extends string ? 'yes' : 'no';\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "mapped type":
|
||||
let src = "type Readonly<T> = { readonly [K in keyof T]: T[K]; };\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "template literal string":
|
||||
let src = "const msg = `Hello, ${name}! Your age is ${age}.`;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed template literal":
|
||||
let src = "const x = `hello\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed regular string":
|
||||
let src = "const x = \"unclosed\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "nullish coalescing":
|
||||
let src = "const x = value ?? fallback;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "optional chaining":
|
||||
let src = "const x = obj?.prop?.nested ?? 'default';\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "destructuring with types":
|
||||
let src = "const { a, b }: { a: number; b: string } = obj;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "rest and spread":
|
||||
let src = "const [head, ...rest] = arr;\nconst merged = { ...obj1, ...obj2 };\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "decorator":
|
||||
let src = "function log(target: any, key: string): void {}\nclass MyClass { @log method() {} }\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "namespace":
|
||||
let src = "namespace Validation {\n export interface StringValidator { isValid(s: string): boolean; }\n}\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "module import/export":
|
||||
let src = "import { Component } from '@angular/core';\nexport class AppComponent {}\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "type assertion":
|
||||
let src = "const x = value as string;\nconst y = <string>value;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "index signature":
|
||||
let src = "interface Dict { [key: string]: unknown; }\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "overload signatures":
|
||||
let src = "function greet(person: string): string;\nfunction greet(person: string, age: number): string;\nfunction greet(person: string, age?: number): string { return ''; }\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "line comment":
|
||||
let src = "// this is a comment\nconst x = 1;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "block comment":
|
||||
let src = "const x = /* inline comment */ 42;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed block comment":
|
||||
let src = "const x = 1; /* never closed\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte injection":
|
||||
let src = "const x = 1;\x00const y = 2;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unicode in identifiers":
|
||||
let src = "const café = \"coffee\";\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "bigint literal":
|
||||
let src = "const big = 9007199254740991n;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "never and unknown types":
|
||||
let src = "function fail(msg: string): never { throw new Error(msg); }\nlet v: unknown = 42;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "keyof operator":
|
||||
let src = "type Keys = keyof { a: number; b: string };\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "satisfies operator":
|
||||
let src = "const x = { a: 1, b: 'hello' } satisfies Record<string, string | number>;\n"
|
||||
let result = validateSource(src, flavor = lfTypeScript)
|
||||
check result.errors.len >= 0
|
||||
@@ -0,0 +1,40 @@
|
||||
## XML language validator tests.
|
||||
|
||||
import std/[unittest, strutils, strformat]
|
||||
import ../src/nimcheck
|
||||
import test_common
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
suite "XML Validator":
|
||||
test "valid XML code":
|
||||
let result = validateSource(XmlGoodCode, flavor = lfXML)
|
||||
checkValid(result)
|
||||
|
||||
test "invalid XML code returns errors":
|
||||
let result = validateSource(XmlBadCode, flavor = lfXML)
|
||||
checkInvalid(result)
|
||||
|
||||
test "valid .xml file":
|
||||
let result = validateFile("tests/fixtures/xml/valid.xml")
|
||||
checkValid(result)
|
||||
|
||||
test "invalid .xml file returns errors":
|
||||
let result = validateFile("tests/fixtures/xml/invalid.xml")
|
||||
check result.errors.len > 0
|
||||
|
||||
test "detectFlavor detects XML":
|
||||
check $detectFlavor(XmlGoodCode) == "xml"
|
||||
|
||||
test "inspectSource returns JSON":
|
||||
let json = inspectSource(XmlGoodCode, flavor = lfXML)
|
||||
check json.kind == JObject
|
||||
@@ -0,0 +1,176 @@
|
||||
## XML validator -- exhaustive edge-case tests.
|
||||
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
||||
## nesting extremes, unicode bombs, binary injection, and more.
|
||||
|
||||
import std/[unittest, strutils, strformat, json]
|
||||
import ../src/nimcheck
|
||||
|
||||
proc checkValid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len > 0:
|
||||
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
||||
if context.len > 0: msg.add(&" [{context}]")
|
||||
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
||||
doAssert false, msg
|
||||
|
||||
proc checkInvalid(result: ValidationResult, context: string = "") =
|
||||
if result.errors.len == 0:
|
||||
doAssert false, &"Expected errors, got none [{context}]"
|
||||
|
||||
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if e.code == code: found = true
|
||||
if not found:
|
||||
var codes: seq[string] = @[]
|
||||
for e in result.errors: codes.add(e.code)
|
||||
let codesJoined = codes.join(", ")
|
||||
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
||||
|
||||
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
||||
var found = false
|
||||
for e in result.errors:
|
||||
if $e.severity == sev: found = true
|
||||
if not found:
|
||||
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
||||
|
||||
suite "XML Exhaustive Tests":
|
||||
|
||||
test "basic valid XML":
|
||||
let src = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><item id=\"1\">value</item></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unclosed element":
|
||||
let src = "<?xml version=\"1.0\"?><root><item>unclosed\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "mismatched tags":
|
||||
let src = "<root><a><b>text</a></b>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "self-closing element":
|
||||
let src = "<?xml version=\"1.0\"?><root><br/><hr/></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "nested elements deep":
|
||||
let src = "<root><l1><l2><l3><l4><l5><p>deep</p></l5></l4></l3></l2></l1></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "attributes with double quotes":
|
||||
let src = "<root><a href=\"https://example.com\" class=\"link\">click</a></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "attributes with single quotes":
|
||||
let src = "<root><a href='https://example.com'>click</a></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "XML comment":
|
||||
let src = "<?xml version=\"1.0\"?><root><!-- this is a comment --><p>text</p></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "CDATA section":
|
||||
let src = "<?xml version=\"1.0\"?><root><![CDATA[<script>alert('xss')</script>]]></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty source":
|
||||
let src = ""
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "XML declaration only":
|
||||
let src = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "null byte in XML":
|
||||
let src = "<root><p>hello\x00world</p></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "entity references":
|
||||
let src = "<root><p>&<>"'</p></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "numeric character references":
|
||||
let src = "<root><p>AA😀</p></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "multiple attributes":
|
||||
let src = "<root><div id=\"123\" class=\"main\" data-value=\"test\"></div></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "namespaced elements":
|
||||
let src = "<root xmlns:ns=\"http://example.com/ns\"><ns:element>value</ns:element></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "namespaced attributes":
|
||||
let src = "<root xmlns:xlink=\"http://www.w3.org/1999/xlink\"><a xlink:href=\"doc.xml\">link</a></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "mixed content":
|
||||
let src = "<root>Some text <child>with child</child> and more text.</root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "processing instruction":
|
||||
let src = "<?xml version=\"1.0\"?><?xml-stylesheet type=\"text/xsl\" href=\"style.xsl\"?><root>data</root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "duplicate attribute":
|
||||
let src = "<root><item id=\"1\" id=\"2\">dup</item></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "unquoted attribute value":
|
||||
let src = "<root><item id=1>unquoted</item></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "missing closing tag":
|
||||
let src = "<root><open>text"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "deeply nested with many children":
|
||||
let src = "<root>" & repeat("<item>", 50) & "deep" & repeat("</item>", 50) & "</root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "UTF-8 encoded content":
|
||||
let src = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><msg>\u3053\u3093\u306b\u3061\u306f</msg></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "DOCTYPE declaration":
|
||||
let src = "<?xml version=\"1.0\"?><!DOCTYPE note SYSTEM \"note.dtd\"><note><p>text</p></note>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "empty element content":
|
||||
let src = "<root><empty></empty><void/><void2/></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "whitespace-only content":
|
||||
let src = "<root> \n \t </root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
|
||||
test "long text content":
|
||||
let src = "<root><p>" & repeat("x", 10000) & "</p></root>\n"
|
||||
let result = validateSource(src, flavor = lfXML)
|
||||
check result.errors.len >= 0
|
||||
Reference in New Issue
Block a user