mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-03-12 15:30:07 -04:00
Compare commits
164 Commits
v2.0.0.515
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63853494f8 | ||
|
|
6593575850 | ||
|
|
bbf74a8835 | ||
|
|
1fc2866032 | ||
|
|
eb2e7b9c79 | ||
|
|
cab900f656 | ||
|
|
e2b91e5dc4 | ||
|
|
e52fcf843c | ||
|
|
08ba273089 | ||
|
|
faa2d632e5 | ||
|
|
1b939ebf4b | ||
|
|
aa46216117 | ||
|
|
c3c6b3d166 | ||
|
|
2c95f07cb2 | ||
|
|
4a2277b424 | ||
|
|
a1f02916d4 | ||
|
|
900dfd92d0 | ||
|
|
d6997b0588 | ||
|
|
779ab39f50 | ||
|
|
00283e3d6e | ||
|
|
2b4429f8b7 | ||
|
|
2446c4185a | ||
|
|
04900e5f90 | ||
|
|
ce59db528b | ||
|
|
31b266659e | ||
|
|
e071b0c2e0 | ||
|
|
270f04d2d2 | ||
|
|
9af57c6786 | ||
|
|
ff4a550cbb | ||
|
|
537e4d7c39 | ||
|
|
9f16d9b2fc | ||
|
|
ae6d920e2a | ||
|
|
0d22f9ec29 | ||
|
|
699076a405 | ||
|
|
df593f486f | ||
|
|
0d95873a05 | ||
|
|
b20acc9063 | ||
|
|
70d6d25178 | ||
|
|
196d165432 | ||
|
|
bb3ca998fc | ||
|
|
da73221cef | ||
|
|
36f66eed21 | ||
|
|
8e916d60f5 | ||
|
|
44048207f2 | ||
|
|
b73b99df8d | ||
|
|
ad69ecc5eb | ||
|
|
1304bc8fb9 | ||
|
|
a4f63e728c | ||
|
|
307b3536b7 | ||
|
|
24c6d3f4b3 | ||
|
|
4a052708c8 | ||
|
|
39a8d4f0d8 | ||
|
|
ca22a25842 | ||
|
|
ff9a9a5e4d | ||
|
|
3d7c59bc3b | ||
|
|
63ea1f1afd | ||
|
|
baf8f6cca6 | ||
|
|
c67c7e1b5a | ||
|
|
46d8e5830a | ||
|
|
37054673b7 | ||
|
|
86bc5c5547 | ||
|
|
fc44607c73 | ||
|
|
2a1421f488 | ||
|
|
d7a054f637 | ||
|
|
9c9ad9aec3 | ||
|
|
1467c52e03 | ||
|
|
e407145d10 | ||
|
|
476110b1de | ||
|
|
45f9f45f50 | ||
|
|
d581d997c2 | ||
|
|
633344e5bb | ||
|
|
0cce6b74f9 | ||
|
|
8b8bfb9bf0 | ||
|
|
7241ca4ae9 | ||
|
|
e9b11e55e9 | ||
|
|
48126f55ed | ||
|
|
cb549507ee | ||
|
|
a0b6cdb08e | ||
|
|
9b9597093c | ||
|
|
c92fb4d9c0 | ||
|
|
88c659059b | ||
|
|
d1ced600a1 | ||
|
|
a5977f2752 | ||
|
|
8668e8b036 | ||
|
|
783c27a584 | ||
|
|
fde0409650 | ||
|
|
2ed5abf4d3 | ||
|
|
cb372f284d | ||
|
|
5d674a07f7 | ||
|
|
59e69c1839 | ||
|
|
89a3eec6ae | ||
|
|
6517f1af14 | ||
|
|
b95c4d37d7 | ||
|
|
7388e8c2c2 | ||
|
|
b837ab41eb | ||
|
|
de6615f586 | ||
|
|
0947dfc423 | ||
|
|
002ed9e4c7 | ||
|
|
1a6a3038d6 | ||
|
|
d86beb06f7 | ||
|
|
6df61e305d | ||
|
|
47018b02a8 | ||
|
|
b6ef4d50dc | ||
|
|
2d86e44c63 | ||
|
|
69f8fc4d5e | ||
|
|
b339fcbd82 | ||
|
|
7d1b09ac1b | ||
|
|
55d01f620a | ||
|
|
39d0d08ced | ||
|
|
94f8cc9b62 | ||
|
|
17b998f01f | ||
|
|
18415ddb30 | ||
|
|
2b3d0235cf | ||
|
|
c687f45ff6 | ||
|
|
13f540f1f5 | ||
|
|
a7aff3bd9a | ||
|
|
03997e2a0d | ||
|
|
60c73df685 | ||
|
|
b5ce3b2773 | ||
|
|
6c09b7dc28 | ||
|
|
bf1fbd7fc4 | ||
|
|
401530db65 | ||
|
|
4fb7cb3a8b | ||
|
|
aba8abb176 | ||
|
|
2b3956cb60 | ||
|
|
1b0647423d | ||
|
|
5b627a8bc2 | ||
|
|
62f7e90748 | ||
|
|
fd1064cb69 | ||
|
|
c677736a8f | ||
|
|
67038ddd5e | ||
|
|
ff8eb0b67f | ||
|
|
70afacee3f | ||
|
|
5b0e959d3f | ||
|
|
b7c5639a9d | ||
|
|
740af2751a | ||
|
|
81e385bebf | ||
|
|
650d18797a | ||
|
|
49e91b8660 | ||
|
|
4fb3d99153 | ||
|
|
a29f133e5d | ||
|
|
f277b60cc9 | ||
|
|
6d8eebcd30 | ||
|
|
91c97ed232 | ||
|
|
13a259b473 | ||
|
|
d3f8e0ef4c | ||
|
|
700751715b | ||
|
|
58e939beda | ||
|
|
b19b6c93d3 | ||
|
|
17e4a8ab66 | ||
|
|
3573e631c8 | ||
|
|
52588509ed | ||
|
|
e607a67f00 | ||
|
|
96d7382a1c | ||
|
|
e15530cee1 | ||
|
|
940f59468a | ||
|
|
ff885ab3bd | ||
|
|
17cfaf170e | ||
|
|
f1b2186313 | ||
|
|
ac379e3b84 | ||
|
|
616454edb4 | ||
|
|
d284969379 | ||
|
|
f0eba619f3 | ||
|
|
448cfff769 |
1
.github/ISSUE_TEMPLATE.md
vendored
1
.github/ISSUE_TEMPLATE.md
vendored
@@ -3,6 +3,7 @@ Before opening a new issue, please ensure:
|
||||
- You use the forums for support/questions
|
||||
- You search for existing bugs/feature requests
|
||||
- Remove extraneous template details
|
||||
- Do not prefix title with type of issue (Feature Request, Bug, etc.) The appropriate labels will be added during triage.
|
||||
-->
|
||||
|
||||
## Support / Questions
|
||||
|
||||
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve Sonarr
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs**
|
||||
Link to debug or trace log files.
|
||||
|
||||
**System Information**
|
||||
|
||||
- Sonarr Version: [e.g. 2.0.0.1]
|
||||
- Operating System: [e.g. Windows 10]
|
||||
- .net Framework (Windows) or mono (macOS/Linux) Version: [e.g. 4.5 or 5.12]
|
||||
|
||||
**UI Bugs:**
|
||||
- OS: [e.g. Windows]
|
||||
- Browser: [e.g. chrome, firefox]
|
||||
- Version: [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for Sonarr
|
||||
|
||||
---
|
||||
|
||||
**Describe the problem**
|
||||
A clear and concise description of the problem you're looking to solve.
|
||||
|
||||
**Describe any solutions you think might work**
|
||||
A clear and concise description of any solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
7
.github/ISSUE_TEMPLATE/other-issues.md
vendored
Normal file
7
.github/ISSUE_TEMPLATE/other-issues.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: Other issues
|
||||
about: How to get support or ask questions
|
||||
|
||||
---
|
||||
|
||||
Please use https://forums.sonarr.tv/ for support. Support requests or questions will be redirected to the forums and the issue will be closed.
|
||||
11
build.sh
11
build.sh
@@ -52,15 +52,6 @@ CleanFolder()
|
||||
find $path -depth -empty -type d -exec rm -r "{}" \;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AddJsonNet()
|
||||
{
|
||||
rm $outputFolder/Newtonsoft.Json.*
|
||||
cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder
|
||||
cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder/NzbDrone.Update
|
||||
}
|
||||
|
||||
BuildWithMSBuild()
|
||||
{
|
||||
export PATH=$msBuild:$PATH
|
||||
@@ -91,8 +82,6 @@ Build()
|
||||
|
||||
CleanFolder $outputFolder false
|
||||
|
||||
AddJsonNet
|
||||
|
||||
echo "Removing Mono.Posix.dll"
|
||||
rm $outputFolder/Mono.Posix.dll
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@ APPNAME="Sonarr"
|
||||
|
||||
#set up environment
|
||||
if [[ -x '/opt/local/bin/mono' ]]; then
|
||||
# Macports and mono-supplied installer path
|
||||
export PATH="/opt/local/bin:$PATH"
|
||||
elif [[ -x '/usr/local/bin/mono' ]]; then
|
||||
# Homebrew-supplied path to mono
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR"
|
||||
|
||||
@@ -52,10 +52,14 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Data.Common;
|
||||
@@ -91,9 +91,11 @@ namespace Marr.Data.Mapping
|
||||
Type entType = ent.GetType();
|
||||
if (_repos.Relationships.ContainsKey(entType))
|
||||
{
|
||||
var provider = _db.ProviderFactory;
|
||||
var connectionString = _db.ConnectionString;
|
||||
Func<IDataMapper> dbCreate = () =>
|
||||
{
|
||||
var db = new DataMapper(_db.ProviderFactory, _db.ConnectionString);
|
||||
var db = new DataMapper(provider, connectionString);
|
||||
db.SqlMode = SqlModes.Text;
|
||||
return db;
|
||||
};
|
||||
|
||||
@@ -21,19 +21,32 @@ namespace NzbDrone.Api.Test.ClientSchemaTests
|
||||
public void schema_should_have_proper_fields()
|
||||
{
|
||||
var model = new TestModel
|
||||
{
|
||||
FirstName = "Bob",
|
||||
LastName = "Poop"
|
||||
};
|
||||
{
|
||||
FirstName = "Bob",
|
||||
LastName = "Poop"
|
||||
};
|
||||
|
||||
var schema = SchemaBuilder.ToSchema(model);
|
||||
|
||||
schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && (string) c.Value == "Poop");
|
||||
schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && (string) c.Value == "Bob");
|
||||
schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && (string)c.Value == "Poop");
|
||||
schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && (string)c.Value == "Bob");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void schema_should_have_nested_fields()
|
||||
{
|
||||
var model = new NestedTestModel();
|
||||
model.Name.FirstName = "Bob";
|
||||
model.Name.LastName = "Poop";
|
||||
|
||||
var schema = SchemaBuilder.ToSchema(model);
|
||||
|
||||
schema.Should().Contain(c => c.Order == 0 && c.Name == "Name.FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && (string)c.Value == "Bob");
|
||||
schema.Should().Contain(c => c.Order == 1 && c.Name == "Name.LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && (string)c.Value == "Poop");
|
||||
schema.Should().Contain(c => c.Order == 2 && c.Name == "Quote" && c.Label == "Quote" && c.HelpText == "Your Favorite Quote");
|
||||
}
|
||||
}
|
||||
|
||||
public class TestModel
|
||||
{
|
||||
@@ -45,4 +58,13 @@ namespace NzbDrone.Api.Test.ClientSchemaTests
|
||||
|
||||
public string Other { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class NestedTestModel
|
||||
{
|
||||
[FieldDefinition(0)]
|
||||
public TestModel Name { get; set; } = new TestModel();
|
||||
|
||||
[FieldDefinition(1, Label = "Quote", HelpText = "Your Favorite Quote")]
|
||||
public string Quote { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,17 @@ namespace NzbDrone.Api.ClientSchema
|
||||
public int Order { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Label { get; set; }
|
||||
public string Unit { get; set; }
|
||||
public string HelpText { get; set; }
|
||||
public string HelpLink { get; set; }
|
||||
public object Value { get; set; }
|
||||
public string Type { get; set; }
|
||||
public bool Advanced { get; set; }
|
||||
public List<SelectOption> SelectOptions { get; set; }
|
||||
|
||||
public Field Clone()
|
||||
{
|
||||
return (Field)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
src/NzbDrone.Api/ClientSchema/FieldMapping.cs
Normal file
15
src/NzbDrone.Api/ClientSchema/FieldMapping.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Api.ClientSchema
|
||||
{
|
||||
public class FieldMapping
|
||||
{
|
||||
public Field Field { get; set; }
|
||||
public Type PropertyType { get; set; }
|
||||
public Func<object, object> GetterFunc { get; set; }
|
||||
public Action<object, object> SetterFunc { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -11,45 +12,22 @@ namespace NzbDrone.Api.ClientSchema
|
||||
{
|
||||
public static class SchemaBuilder
|
||||
{
|
||||
private static Dictionary<Type, FieldMapping[]> _mappings = new Dictionary<Type, FieldMapping[]>();
|
||||
|
||||
public static List<Field> ToSchema(object model)
|
||||
{
|
||||
Ensure.That(model, () => model).IsNotNull();
|
||||
|
||||
var properties = model.GetType().GetSimpleProperties();
|
||||
var mappings = GetFieldMappings(model.GetType());
|
||||
|
||||
var result = new List<Field>(properties.Count);
|
||||
var result = new List<Field>(mappings.Length);
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
foreach (var mapping in mappings)
|
||||
{
|
||||
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
|
||||
var field = mapping.Field.Clone();
|
||||
field.Value = mapping.GetterFunc(model);
|
||||
|
||||
if (fieldAttribute != null)
|
||||
{
|
||||
|
||||
var field = new Field
|
||||
{
|
||||
Name = propertyInfo.Name,
|
||||
Label = fieldAttribute.Label,
|
||||
HelpText = fieldAttribute.HelpText,
|
||||
HelpLink = fieldAttribute.HelpLink,
|
||||
Order = fieldAttribute.Order,
|
||||
Advanced = fieldAttribute.Advanced,
|
||||
Type = fieldAttribute.Type.ToString().ToLowerInvariant()
|
||||
};
|
||||
|
||||
var value = propertyInfo.GetValue(model, null);
|
||||
if (value != null)
|
||||
{
|
||||
field.Value = value;
|
||||
}
|
||||
|
||||
if (fieldAttribute.Type == FieldType.Select)
|
||||
{
|
||||
field.SelectOptions = GetSelectOptions(fieldAttribute.SelectOptions);
|
||||
}
|
||||
|
||||
result.Add(field);
|
||||
}
|
||||
result.Add(field);
|
||||
}
|
||||
|
||||
return result.OrderBy(r => r.Order).ToList();
|
||||
@@ -59,81 +37,16 @@ namespace NzbDrone.Api.ClientSchema
|
||||
{
|
||||
Ensure.That(targetType, () => targetType).IsNotNull();
|
||||
|
||||
var properties = targetType.GetSimpleProperties();
|
||||
var mappings = GetFieldMappings(targetType);
|
||||
|
||||
var target = Activator.CreateInstance(targetType);
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
foreach (var mapping in mappings)
|
||||
{
|
||||
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
|
||||
var propertyType = mapping.PropertyType;
|
||||
var field = fields.Find(f => f.Name == mapping.Field.Name);
|
||||
|
||||
if (fieldAttribute != null)
|
||||
{
|
||||
var field = fields.Find(f => f.Name == propertyInfo.Name);
|
||||
|
||||
if (propertyInfo.PropertyType == typeof(int))
|
||||
{
|
||||
var value = field.Value.ToString().ParseInt32();
|
||||
propertyInfo.SetValue(target, value ?? 0, null);
|
||||
}
|
||||
|
||||
else if (propertyInfo.PropertyType == typeof(long))
|
||||
{
|
||||
var value = field.Value.ToString().ParseInt64();
|
||||
propertyInfo.SetValue(target, value ?? 0, null);
|
||||
}
|
||||
|
||||
else if (propertyInfo.PropertyType == typeof(int?))
|
||||
{
|
||||
var value = field.Value.ToString().ParseInt32();
|
||||
propertyInfo.SetValue(target, value, null);
|
||||
}
|
||||
|
||||
else if (propertyInfo.PropertyType == typeof(Nullable<Int64>))
|
||||
{
|
||||
var value = field.Value.ToString().ParseInt64();
|
||||
propertyInfo.SetValue(target, value, null);
|
||||
}
|
||||
|
||||
else if (propertyInfo.PropertyType == typeof(IEnumerable<int>))
|
||||
{
|
||||
IEnumerable<int> value;
|
||||
|
||||
if (field.Value.GetType() == typeof(JArray))
|
||||
{
|
||||
value = ((JArray)field.Value).Select(s => s.Value<int>());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
value = field.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => Convert.ToInt32(s));
|
||||
}
|
||||
|
||||
propertyInfo.SetValue(target, value, null);
|
||||
}
|
||||
|
||||
else if (propertyInfo.PropertyType == typeof(IEnumerable<string>))
|
||||
{
|
||||
IEnumerable<string> value;
|
||||
|
||||
if (field.Value.GetType() == typeof(JArray))
|
||||
{
|
||||
value = ((JArray)field.Value).Select(s => s.Value<string>());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
value = field.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
propertyInfo.SetValue(target, value, null);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
propertyInfo.SetValue(target, field.Value, null);
|
||||
}
|
||||
}
|
||||
mapping.SetterFunc(target, field.Value);
|
||||
}
|
||||
|
||||
return target;
|
||||
@@ -145,6 +58,84 @@ namespace NzbDrone.Api.ClientSchema
|
||||
return (T)ReadFromSchema(fields, typeof(T));
|
||||
}
|
||||
|
||||
|
||||
// Ideally this function should begin a System.Linq.Expression expression tree since it's faster.
|
||||
// But it's probably not needed till performance issues pop up.
|
||||
public static FieldMapping[] GetFieldMappings(Type type)
|
||||
{
|
||||
lock (_mappings)
|
||||
{
|
||||
FieldMapping[] result;
|
||||
if (!_mappings.TryGetValue(type, out result))
|
||||
{
|
||||
result = GetFieldMapping(type, "", v => v);
|
||||
|
||||
// Renumber al the field Orders since nested settings will have dupe Orders.
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
result[i].Field.Order = i;
|
||||
}
|
||||
|
||||
_mappings[type] = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static FieldMapping[] GetFieldMapping(Type type, string prefix, Func<object, object> targetSelector)
|
||||
{
|
||||
var result = new List<FieldMapping>();
|
||||
foreach (var property in GetProperties(type))
|
||||
{
|
||||
var propertyInfo = property.Item1;
|
||||
if (propertyInfo.PropertyType.IsSimpleType())
|
||||
{
|
||||
var fieldAttribute = property.Item2;
|
||||
var field = new Field
|
||||
{
|
||||
Name = prefix + propertyInfo.Name,
|
||||
Label = fieldAttribute.Label,
|
||||
Unit = fieldAttribute.Unit,
|
||||
HelpText = fieldAttribute.HelpText,
|
||||
HelpLink = fieldAttribute.HelpLink,
|
||||
Order = fieldAttribute.Order,
|
||||
Advanced = fieldAttribute.Advanced,
|
||||
Type = fieldAttribute.Type.ToString().ToLowerInvariant()
|
||||
};
|
||||
|
||||
if (fieldAttribute.Type == FieldType.Select)
|
||||
{
|
||||
field.SelectOptions = GetSelectOptions(fieldAttribute.SelectOptions);
|
||||
}
|
||||
|
||||
var valueConverter = GetValueConverter(propertyInfo.PropertyType);
|
||||
|
||||
result.Add(new FieldMapping
|
||||
{
|
||||
Field = field,
|
||||
PropertyType = propertyInfo.PropertyType,
|
||||
GetterFunc = t => propertyInfo.GetValue(targetSelector(t), null),
|
||||
SetterFunc = (t, v) => propertyInfo.SetValue(targetSelector(t), valueConverter(v), null)
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AddRange(GetFieldMapping(propertyInfo.PropertyType, propertyInfo.Name + ".", t => propertyInfo.GetValue(targetSelector(t), null)));
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static Tuple<PropertyInfo, FieldDefinitionAttribute>[] GetProperties(Type type)
|
||||
{
|
||||
return type.GetProperties()
|
||||
.Select(v => Tuple.Create(v, v.GetAttribute<FieldDefinitionAttribute>(false)))
|
||||
.Where(v => v.Item2 != null)
|
||||
.OrderBy(v => v.Item2.Order)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static List<SelectOption> GetSelectOptions(Type selectOptions)
|
||||
{
|
||||
var options = from Enum e in Enum.GetValues(selectOptions)
|
||||
@@ -152,5 +143,73 @@ namespace NzbDrone.Api.ClientSchema
|
||||
|
||||
return options.OrderBy(o => o.Value).ToList();
|
||||
}
|
||||
|
||||
private static Func<object, object> GetValueConverter(Type propertyType)
|
||||
{
|
||||
if (propertyType == typeof(int))
|
||||
{
|
||||
return fieldValue => fieldValue?.ToString().ParseInt32() ?? 0;
|
||||
}
|
||||
|
||||
else if (propertyType == typeof(long))
|
||||
{
|
||||
return fieldValue => fieldValue?.ToString().ParseInt64() ?? 0;
|
||||
}
|
||||
|
||||
else if (propertyType == typeof(double))
|
||||
{
|
||||
return fieldValue => fieldValue?.ToString().ParseDouble() ?? 0.0;
|
||||
}
|
||||
|
||||
else if (propertyType == typeof(int?))
|
||||
{
|
||||
return fieldValue => fieldValue?.ToString().ParseInt32();
|
||||
}
|
||||
|
||||
else if (propertyType == typeof(Int64?))
|
||||
{
|
||||
return fieldValue => fieldValue?.ToString().ParseInt64();
|
||||
}
|
||||
|
||||
else if (propertyType == typeof(double?))
|
||||
{
|
||||
return fieldValue => fieldValue?.ToString().ParseDouble();
|
||||
}
|
||||
|
||||
else if (propertyType == typeof(IEnumerable<int>))
|
||||
{
|
||||
return fieldValue =>
|
||||
{
|
||||
if (fieldValue.GetType() == typeof(JArray))
|
||||
{
|
||||
return ((JArray)fieldValue).Select(s => s.Value<int>());
|
||||
}
|
||||
else
|
||||
{
|
||||
return fieldValue.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => Convert.ToInt32(s));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
else if (propertyType == typeof(IEnumerable<string>))
|
||||
{
|
||||
return fieldValue =>
|
||||
{
|
||||
if (fieldValue.GetType() == typeof(JArray))
|
||||
{
|
||||
return ((JArray)fieldValue).Select(s => s.Value<string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
return fieldValue.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return fieldValue => fieldValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Api.Validation;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.TPL;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -17,6 +18,8 @@ namespace NzbDrone.Api.Commands
|
||||
{
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IServiceFactory _serviceFactory;
|
||||
private readonly Debouncer _debouncer;
|
||||
private readonly Dictionary<int, CommandResource> _pendingUpdates;
|
||||
|
||||
public CommandModule(IManageCommandQueue commandQueueManager,
|
||||
IBroadcastSignalRMessage signalRBroadcaster,
|
||||
@@ -31,6 +34,10 @@ namespace NzbDrone.Api.Commands
|
||||
GetResourceAll = GetStartedCommands;
|
||||
|
||||
PostValidator.RuleFor(c => c.Name).NotBlank();
|
||||
|
||||
_debouncer = new Debouncer(SendUpdates, TimeSpan.FromSeconds(0.1));
|
||||
_pendingUpdates = new Dictionary<int, CommandResource>();
|
||||
|
||||
}
|
||||
|
||||
private CommandResource GetCommand(int id)
|
||||
@@ -59,8 +66,26 @@ namespace NzbDrone.Api.Commands
|
||||
{
|
||||
if (message.Command.Body.SendUpdatesToClient)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Updated, message.Command.ToResource());
|
||||
lock (_pendingUpdates)
|
||||
{
|
||||
_pendingUpdates[message.Command.Id] = message.Command.ToResource();
|
||||
}
|
||||
_debouncer.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
private void SendUpdates()
|
||||
{
|
||||
lock (_pendingUpdates)
|
||||
{
|
||||
var pendingUpdates = _pendingUpdates.Values.ToArray();
|
||||
_pendingUpdates.Clear();
|
||||
|
||||
foreach (var pendingUpdate in pendingUpdates)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Updated, pendingUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
public string SceneName { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public MediaInfoResource MediaInfo { get; set; }
|
||||
public string OriginalFilePath { get; set; }
|
||||
|
||||
public bool QualityCutoffNotMet { get; set; }
|
||||
}
|
||||
@@ -38,8 +39,8 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
DateAdded = model.DateAdded,
|
||||
SceneName = model.SceneName,
|
||||
Quality = model.Quality,
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName)
|
||||
//QualityCutoffNotMet
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
|
||||
OriginalFilePath = model.OriginalFilePath
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
Quality = model.Quality,
|
||||
QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, model.Quality),
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
|
||||
OriginalFilePath = model.OriginalFilePath
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
@@ -30,6 +30,7 @@ namespace NzbDrone.Api.Episodes
|
||||
public bool UnverifiedSceneNumbering { get; set; }
|
||||
public string SeriesTitle { get; set; }
|
||||
public SeriesResource Series { get; set; }
|
||||
public DateTime? LastSearchTime { get; set; }
|
||||
|
||||
//Hiding this so people don't think its usable (only used to set the initial state)
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
@@ -65,6 +66,7 @@ namespace NzbDrone.Api.Episodes
|
||||
UnverifiedSceneNumbering = model.UnverifiedSceneNumbering,
|
||||
SeriesTitle = model.SeriesTitle,
|
||||
//Series = model.Series.MapToResource(),
|
||||
LastSearchTime = model.LastSearchTime
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using Nancy;
|
||||
using Nancy.Bootstrapper;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Api.Extensions.Pipelines
|
||||
@@ -15,9 +16,14 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
||||
|
||||
public int Order => 0;
|
||||
|
||||
private readonly Action<Action<Stream>, Stream> _writeGZipStream;
|
||||
|
||||
public GzipCompressionPipeline(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
|
||||
_writeGZipStream = PlatformInfo.IsMono ? WriteGZipStreamMono : (Action<Action<Stream>, Stream>)WriteGZipStream;
|
||||
}
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
@@ -43,14 +49,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
||||
var contents = response.Contents;
|
||||
|
||||
response.Headers["Content-Encoding"] = "gzip";
|
||||
response.Contents = responseStream =>
|
||||
{
|
||||
using (var gzip = new GZipStream(responseStream, CompressionMode.Compress, true))
|
||||
using (var buffered = new BufferedStream(gzip, 8192))
|
||||
{
|
||||
contents.Invoke(buffered);
|
||||
}
|
||||
};
|
||||
response.Contents = responseStream => _writeGZipStream(contents, responseStream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +60,25 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGZipStreamMono(Action<Stream> innerContent, Stream targetStream)
|
||||
{
|
||||
using (var membuffer = new MemoryStream())
|
||||
{
|
||||
WriteGZipStream(innerContent, membuffer);
|
||||
membuffer.Position = 0;
|
||||
membuffer.CopyTo(targetStream);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGZipStream(Action<Stream> innerContent, Stream targetStream)
|
||||
{
|
||||
using (var gzip = new GZipStream(targetStream, CompressionMode.Compress, true))
|
||||
using (var buffered = new BufferedStream(gzip, 8192))
|
||||
{
|
||||
innerContent.Invoke(buffered);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ContentLengthIsTooSmall(Response response)
|
||||
{
|
||||
var contentLength = response.Headers.GetValueOrDefault("Content-Length");
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using NLog;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NLog;
|
||||
|
||||
namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
@@ -15,14 +18,17 @@ namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
private readonly IMakeDownloadDecision _downloadDecisionMaker;
|
||||
private readonly IProcessDownloadDecisions _downloadDecisionProcessor;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ReleasePushModule(IMakeDownloadDecision downloadDecisionMaker,
|
||||
IProcessDownloadDecisions downloadDecisionProcessor,
|
||||
IIndexerFactory indexerFactory,
|
||||
Logger logger)
|
||||
{
|
||||
_downloadDecisionMaker = downloadDecisionMaker;
|
||||
_downloadDecisionProcessor = downloadDecisionProcessor;
|
||||
_indexerFactory = indexerFactory;
|
||||
_logger = logger;
|
||||
|
||||
Post["/push"] = x => ProcessRelease(this.Bind<ReleaseResource>());
|
||||
@@ -41,10 +47,47 @@ namespace NzbDrone.Api.Indexers
|
||||
|
||||
info.Guid = "PUSH-" + info.DownloadUrl;
|
||||
|
||||
ResolveIndexer(info);
|
||||
|
||||
var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
|
||||
_downloadDecisionProcessor.ProcessDecisions(decisions);
|
||||
|
||||
return MapDecisions(decisions).First().AsResponse();
|
||||
}
|
||||
|
||||
private void ResolveIndexer(ReleaseInfo release)
|
||||
{
|
||||
if (release.IndexerId == 0 && release.Indexer.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var indexer = _indexerFactory.All().FirstOrDefault(v => v.Name == release.Indexer);
|
||||
if (indexer != null)
|
||||
{
|
||||
release.IndexerId = indexer.Id;
|
||||
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("Push Release {0} not associated with unknown indexer {1}.", release.Title, release.Indexer);
|
||||
}
|
||||
}
|
||||
else if (release.IndexerId != 0 && release.Indexer.IsNullOrWhiteSpace())
|
||||
{
|
||||
try
|
||||
{
|
||||
var indexer = _indexerFactory.Get(release.IndexerId);
|
||||
release.Indexer = indexer.Name;
|
||||
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
|
||||
}
|
||||
catch (ModelNotFoundException)
|
||||
{
|
||||
_logger.Debug("Push Release {0} not associated with unknown indexer {0}.", release.Title, release.IndexerId);
|
||||
release.IndexerId = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("Push Release {0} not associated with an indexer.", release.Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace NzbDrone.Api.ManualImport
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string RelativePath { get; set; }
|
||||
public string FolderName { get; set; }
|
||||
public string Name { get; set; }
|
||||
public long Size { get; set; }
|
||||
public SeriesResource Series { get; set; }
|
||||
@@ -36,6 +37,7 @@ namespace NzbDrone.Api.ManualImport
|
||||
|
||||
Path = model.Path,
|
||||
RelativePath = model.RelativePath,
|
||||
FolderName = model.FolderName,
|
||||
Name = model.Name,
|
||||
Size = model.Size,
|
||||
Series = model.Series.ToResource(),
|
||||
|
||||
@@ -70,12 +70,13 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NodaTime, Version=1.3.0.0, Culture=neutral, PublicKeyToken=4226afe0d9b296d1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Ical.Net.2.2.32\lib\net40\NodaTime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@@ -83,6 +84,10 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libraries\Sqlite\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\NzbDrone.Common\Properties\SharedAssemblyInfo.cs">
|
||||
@@ -99,6 +104,7 @@
|
||||
<Compile Include="Calendar\CalendarModule.cs" />
|
||||
<Compile Include="ClientSchema\Field.cs" />
|
||||
<Compile Include="ClientSchema\FieldDefinitionAttribute.cs" />
|
||||
<Compile Include="ClientSchema\FieldMapping.cs" />
|
||||
<Compile Include="ClientSchema\SchemaBuilder.cs" />
|
||||
<Compile Include="ClientSchema\SchemaDeserializer.cs" />
|
||||
<Compile Include="ClientSchema\SelectOption.cs" />
|
||||
|
||||
@@ -210,7 +210,12 @@ namespace NzbDrone.Api
|
||||
|
||||
protected void VerifyValidationResult(ValidationResult validationResult, bool includeWarnings)
|
||||
{
|
||||
var result = new NzbDroneValidationResult(validationResult.Errors);
|
||||
var result = validationResult as NzbDroneValidationResult;
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = new NzbDroneValidationResult(validationResult.Errors);
|
||||
}
|
||||
|
||||
if (includeWarnings && (!result.IsValid || result.HasWarnings))
|
||||
{
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
<package id="Nancy.Authentication.Basic" version="1.4.1" targetFramework="net40" />
|
||||
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -47,19 +47,25 @@
|
||||
<Reference Include="FluentAssertions.Core, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Moq, Version=4.0.10827.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" targetFramework="net40" />
|
||||
<package id="NBuilder" version="4.0.0" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -45,16 +45,20 @@
|
||||
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
<package id="Selenium.Support" version="3.2.0" targetFramework="net40" />
|
||||
<package id="Selenium.WebDriver" version="3.2.0" targetFramework="net40" />
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
@@ -24,13 +25,60 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestFixture(typeof(CurlHttpDispatcher))]
|
||||
public class HttpClientFixture<TDispatcher> : TestBase<HttpClient> where TDispatcher : IHttpDispatcher
|
||||
{
|
||||
private static string[] _httpBinHosts = new[] { "eu.httpbin.org", "httpbin.org" };
|
||||
private static int _httpBinRandom;
|
||||
private string[] _httpBinHosts;
|
||||
private int _httpBinSleep;
|
||||
private int _httpBinRandom;
|
||||
private string _httpBinHost;
|
||||
private string _httpBinHost2;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void FixtureSetUp()
|
||||
{
|
||||
var candidates = new[] { "eu.httpbin.org", /*"httpbin.org",*/ "www.httpbin.org" };
|
||||
// httpbin.org is broken right now, occassionally redirecting to https if it's unavailable.
|
||||
_httpBinHosts = candidates.Where(IsTestSiteAvailable).ToArray();
|
||||
|
||||
TestLogger.Info($"{candidates.Length} TestSites available.");
|
||||
|
||||
_httpBinSleep = _httpBinHosts.Count() < 2 ? 100 : 10;
|
||||
}
|
||||
|
||||
private bool IsTestSiteAvailable(string site)
|
||||
{
|
||||
try
|
||||
{
|
||||
var req = WebRequest.Create($"http://{site}/get") as HttpWebRequest;
|
||||
var res = req.GetResponse() as HttpWebResponse;
|
||||
if (res.StatusCode != HttpStatusCode.OK) return false;
|
||||
|
||||
try
|
||||
{
|
||||
req = WebRequest.Create($"http://{site}/status/429") as HttpWebRequest;
|
||||
res = req.GetResponse() as HttpWebResponse;
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
res = ex.Response as HttpWebResponse;
|
||||
}
|
||||
|
||||
if (res == null || res.StatusCode != (HttpStatusCode)429) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
if (!_httpBinHosts.Any())
|
||||
{
|
||||
Assert.Inconclusive("No TestSites available");
|
||||
}
|
||||
|
||||
Mocker.GetMock<IPlatformInfo>().Setup(c => c.Version).Returns(new Version("1.0.0"));
|
||||
Mocker.GetMock<IOsInfo>().Setup(c => c.Name).Returns("TestOS");
|
||||
Mocker.GetMock<IOsInfo>().Setup(c => c.Version).Returns("9.0.0");
|
||||
@@ -50,6 +98,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
// Roundrobin over the two servers, to reduce the chance of hitting the ratelimiter.
|
||||
_httpBinHost = _httpBinHosts[_httpBinRandom++ % _httpBinHosts.Length];
|
||||
_httpBinHost2 = _httpBinHosts[_httpBinRandom % _httpBinHosts.Length];
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Thread.Sleep(_httpBinSleep);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,11 +130,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_execute_typed_get()
|
||||
{
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get?test=1");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Url.Should().Be(request.Url.FullUri);
|
||||
response.Resource.Url.EndsWith("/get?test=1");
|
||||
response.Resource.Args.Should().Contain("test", "1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -245,7 +301,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
public void GivenOldCookie()
|
||||
{
|
||||
var oldRequest = new HttpRequest("http://eu.httpbin.org/get");
|
||||
if (_httpBinHost == _httpBinHost2)
|
||||
{
|
||||
Assert.Inconclusive("Need both httpbin.org and eu.httpbin.org to run this test.");
|
||||
}
|
||||
|
||||
var oldRequest = new HttpRequest($"http://{_httpBinHost2}/get");
|
||||
oldRequest.Cookies["my"] = "cookie";
|
||||
|
||||
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.GetMock<IUserAgentBuilder>().Object, Mocker.Resolve<Logger>());
|
||||
@@ -262,7 +323,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest($"http://{_httpBinHost2}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -278,26 +339,103 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest("http://httpbin.org/get");
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().NotContainKey("Cookie");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_store_request_cookie()
|
||||
{
|
||||
var requestGet = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
requestGet.Cookies.Add("my", "cookie");
|
||||
requestGet.AllowAutoRedirect = false;
|
||||
requestGet.StoreRequestCookie = false;
|
||||
requestGet.StoreResponseCookie = false;
|
||||
var responseGet = Subject.Get<HttpBinResource>(requestGet);
|
||||
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_store_request_cookie()
|
||||
{
|
||||
var requestGet = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
requestGet.Cookies.Add("my", "cookie");
|
||||
requestGet.AllowAutoRedirect = false;
|
||||
requestGet.StoreRequestCookie.Should().BeTrue();
|
||||
requestGet.StoreResponseCookie = false;
|
||||
var responseGet = Subject.Get<HttpBinResource>(requestGet);
|
||||
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_request_cookie()
|
||||
{
|
||||
var requestDelete = new HttpRequest($"http://{_httpBinHost}/cookies/delete?my");
|
||||
requestDelete.Cookies.Add("my", "cookie");
|
||||
requestDelete.AllowAutoRedirect = true;
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
|
||||
// Delete and redirect since that's the only way to check the internal temporary cookie container
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestDelete);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_clear_request_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestSet.Cookies.Add("my", "cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = true;
|
||||
requestSet.StoreResponseCookie = false;
|
||||
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
|
||||
var requestClear = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestClear.Cookies.Add("my", null);
|
||||
requestClear.AllowAutoRedirect = false;
|
||||
requestClear.StoreRequestCookie = true;
|
||||
requestClear.StoreResponseCookie = false;
|
||||
|
||||
var responseClear = Subject.Get<HttpCookieResource>(requestClear);
|
||||
|
||||
responseClear.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie.Should().BeFalse();
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
response.Resource.Headers.Should().NotContainKey("Cookie");
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
@@ -307,19 +445,31 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var cookie = response.Resource.Headers["Cookie"].ToString();
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
cookie.Should().Contain("my=cookie");
|
||||
[Test]
|
||||
public void should_temp_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = true;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie.Should().BeFalse();
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
|
||||
// Set and redirect since that's the only way to check the internal temporary cookie container
|
||||
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
@@ -328,21 +478,129 @@ namespace NzbDrone.Common.Test.Http
|
||||
public void should_overwrite_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.Cookies.Add("my", "oldcookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
requestSet.Cookies["my"] = "oldcookie";
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var cookie = response.Resource.Headers["Cookie"].ToString();
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
cookie.Should().Contain("my=cookie");
|
||||
[Test]
|
||||
public void should_overwrite_temp_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.Cookies.Add("my", "oldcookie");
|
||||
requestSet.AllowAutoRedirect = true;
|
||||
requestSet.StoreRequestCookie = true;
|
||||
requestSet.StoreResponseCookie = false;
|
||||
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
|
||||
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "oldcookie");
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var requestDelete = new HttpRequest($"http://{_httpBinHost}/cookies/delete?my");
|
||||
requestDelete.AllowAutoRedirect = false;
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
|
||||
var responseDelete = Subject.Get(requestDelete);
|
||||
|
||||
requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
|
||||
responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var requestDelete = new HttpRequest($"http://{_httpBinHost}/cookies/delete?my");
|
||||
requestDelete.AllowAutoRedirect = false;
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = true;
|
||||
|
||||
var responseDelete = Subject.Get(requestDelete);
|
||||
|
||||
requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
|
||||
responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_temp_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var requestDelete = new HttpRequest($"http://{_httpBinHost}/cookies/delete?my");
|
||||
requestDelete.AllowAutoRedirect = true;
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
var responseDelete = Subject.Get<HttpCookieResource>(requestDelete);
|
||||
|
||||
responseDelete.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
@@ -449,9 +707,15 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
public class HttpBinResource
|
||||
{
|
||||
public Dictionary<string, object> Args { get; set; }
|
||||
public Dictionary<string, object> Headers { get; set; }
|
||||
public string Origin { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Data { get; set; }
|
||||
}
|
||||
|
||||
public class HttpCookieResource
|
||||
{
|
||||
public Dictionary<string, string> Cookies { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,16 +48,20 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
||||
@@ -138,18 +138,34 @@ namespace NzbDrone.Common.Test
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test\mydir", @"C:\Test")]
|
||||
[TestCase(@"C:\Test\", @"C:")]
|
||||
[TestCase(@"C:\Test\", @"C:\")]
|
||||
[TestCase(@"C:\", null)]
|
||||
public void path_should_return_parent(string path, string parentPath)
|
||||
[TestCase(@"\\server\share", null)]
|
||||
[TestCase(@"\\server\share\test", @"\\server\share")]
|
||||
public void path_should_return_parent_windows(string path, string parentPath)
|
||||
{
|
||||
WindowsOnly();
|
||||
path.GetParentPath().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[TestCase(@"/", null)]
|
||||
[TestCase(@"/test", "/")]
|
||||
public void path_should_return_parent_mono(string path, string parentPath)
|
||||
{
|
||||
MonoOnly();
|
||||
path.GetParentPath().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void path_should_return_parent_for_oversized_path()
|
||||
{
|
||||
var path = @"/media/2e168617-f2ae-43fb-b88c-3663af1c8eea/downloads/sabnzbd/nzbdrone/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories";
|
||||
var parentPath = @"/media/2e168617-f2ae-43fb-b88c-3663af1c8eea/downloads/sabnzbd/nzbdrone/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing";
|
||||
MonoOnly();
|
||||
|
||||
// This test will fail on Windows if long path support is not enabled: https://www.howtogeek.com/266621/how-to-make-windows-10-accept-file-paths-over-260-characters/
|
||||
// It will also fail if the app isn't configured to use long path (such as resharper): https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/
|
||||
|
||||
var path = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories".AsOsAgnostic();
|
||||
var parentPath = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing".AsOsAgnostic();
|
||||
|
||||
path.GetParentPath().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using NLog;
|
||||
@@ -28,7 +28,15 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
|
||||
public void Register()
|
||||
{
|
||||
_diskProvider.EnsureFolder(_appFolderInfo.AppDataFolder);
|
||||
try
|
||||
{
|
||||
_diskProvider.EnsureFolder(_appFolderInfo.AppDataFolder);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
throw new SonarrStartupException("Cannot create AppFolder, Access to the path {0} is denied", _appFolderInfo.AppDataFolder);
|
||||
}
|
||||
|
||||
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Common.EnvironmentInfo
|
||||
{
|
||||
public interface IRuntimeInfo
|
||||
{
|
||||
DateTime StartTime { get; }
|
||||
bool IsUserInteractive { get; }
|
||||
bool IsAdmin { get; }
|
||||
bool IsWindowsService { get; }
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
public class RuntimeInfo : IRuntimeInfo
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly DateTime _startTime = DateTime.UtcNow;
|
||||
|
||||
public RuntimeInfo(IServiceProvider serviceProvider, Logger logger)
|
||||
{
|
||||
@@ -37,6 +38,14 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
IsProduction = InternalIsProduction();
|
||||
}
|
||||
|
||||
public DateTime StartTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsUserInteractive => Environment.UserInteractive;
|
||||
|
||||
bool IRuntimeInfo.IsUserInteractive => IsUserInteractive;
|
||||
|
||||
@@ -32,7 +32,19 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
if (response == null || response.Content == null) return ex;
|
||||
|
||||
var contentSample = response.Content.Substring(0, Math.Min(response.Content.Length, 512));
|
||||
var contentSample = response.Content.Substring(0, Math.Min(response.Content.Length, maxSampleLength));
|
||||
|
||||
if (response.Request != null)
|
||||
{
|
||||
ex.AddData("RequestUri", response.Request.Url.ToString());
|
||||
|
||||
if (response.Request.ContentSummary != null)
|
||||
{
|
||||
ex.AddData("RequestSummary", response.Request.ContentSummary);
|
||||
}
|
||||
}
|
||||
|
||||
ex.AddData("StatusCode", response.StatusCode.ToString());
|
||||
|
||||
if (response.Headers != null)
|
||||
{
|
||||
|
||||
@@ -51,6 +51,34 @@ namespace NzbDrone.Common.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<TKey, TItem> ToDictionaryIgnoreDuplicates<TItem, TKey>(this IEnumerable<TItem> src, Func<TItem, TKey> keySelector)
|
||||
{
|
||||
var result = new Dictionary<TKey, TItem>();
|
||||
foreach (var item in src)
|
||||
{
|
||||
var key = keySelector(item);
|
||||
if (!result.ContainsKey(key))
|
||||
{
|
||||
result[key] = item;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Dictionary<TKey, TValue> ToDictionaryIgnoreDuplicates<TItem, TKey, TValue>(this IEnumerable<TItem> src, Func<TItem, TKey> keySelector, Func<TItem, TValue> valueSelector)
|
||||
{
|
||||
var result = new Dictionary<TKey, TValue>();
|
||||
foreach (var item in src)
|
||||
{
|
||||
var key = keySelector(item);
|
||||
if (!result.ContainsKey(key))
|
||||
{
|
||||
result[key] = valueSelector(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void AddIfNotNull<TSource>(this List<TSource> source, TSource item)
|
||||
{
|
||||
if (item == null)
|
||||
@@ -81,4 +109,4 @@ namespace NzbDrone.Common.Extensions
|
||||
return source.Select(predicate).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace NzbDrone.Common.Extensions
|
||||
private static readonly string UPDATE_CLIENT_FOLDER_NAME = "NzbDrone.Update" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_LOG_FOLDER_NAME = "UpdateLogs" + Path.DirectorySeparatorChar;
|
||||
|
||||
private static readonly Regex PARENT_PATH_END_SLASH_REGEX = new Regex(@"(?<!:)\\$", RegexOptions.Compiled);
|
||||
|
||||
public static string CleanFilePath(this string path)
|
||||
{
|
||||
Ensure.That(path, () => path).IsNotNullOrWhiteSpace();
|
||||
@@ -67,15 +69,16 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static string GetParentPath(this string childPath)
|
||||
{
|
||||
var parentPath = childPath.TrimEnd('\\', '/');
|
||||
var cleanPath = OsInfo.IsWindows
|
||||
? PARENT_PATH_END_SLASH_REGEX.Replace(childPath, "")
|
||||
: childPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
|
||||
var index = parentPath.LastIndexOfAny(new[] { '\\', '/' });
|
||||
|
||||
if (index != -1)
|
||||
if (cleanPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return parentPath.Substring(0, index);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
||||
return Directory.GetParent(cleanPath)?.FullName;
|
||||
}
|
||||
|
||||
public static bool IsParentPath(this string parentPath, string childPath)
|
||||
@@ -191,6 +194,24 @@ namespace NzbDrone.Common.Extensions
|
||||
return directories;
|
||||
}
|
||||
|
||||
public static string GetAncestorPath(this string path, string ancestorName)
|
||||
{
|
||||
var parent = Path.GetDirectoryName(path);
|
||||
|
||||
while (parent != null)
|
||||
{
|
||||
var currentPath = parent;
|
||||
parent = Path.GetDirectoryName(parent);
|
||||
|
||||
if (Path.GetFileName(currentPath) == ancestorName)
|
||||
{
|
||||
return currentPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetAppDataPath(this IAppFolderInfo appFolderInfo)
|
||||
{
|
||||
return appFolderInfo.AppDataFolder;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
@@ -6,7 +7,7 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
public static int? ParseInt32(this string source)
|
||||
{
|
||||
int result = 0;
|
||||
int result;
|
||||
|
||||
if (int.TryParse(source, out result))
|
||||
{
|
||||
@@ -16,9 +17,9 @@ namespace NzbDrone.Common.Extensions
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Nullable<long> ParseInt64(this string source)
|
||||
public static long? ParseInt64(this string source)
|
||||
{
|
||||
long result = 0;
|
||||
long result;
|
||||
|
||||
if (long.TryParse(source, out result))
|
||||
{
|
||||
@@ -27,5 +28,17 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static double? ParseDouble(this string source)
|
||||
{
|
||||
double result;
|
||||
|
||||
if (double.TryParse(source.Replace(',', '.'), NumberStyles.Number, CultureInfo.InvariantCulture, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Common.Security;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
@@ -12,22 +18,35 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
|
||||
private readonly ICreateManagedWebProxy _createManagedWebProxy;
|
||||
private readonly IUserAgentBuilder _userAgentBuilder;
|
||||
private readonly IPlatformInfo _platformInfo;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy, IUserAgentBuilder userAgentBuilder)
|
||||
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy, IUserAgentBuilder userAgentBuilder, IPlatformInfo platformInfo, Logger logger)
|
||||
{
|
||||
_proxySettingsProvider = proxySettingsProvider;
|
||||
_createManagedWebProxy = createManagedWebProxy;
|
||||
_userAgentBuilder = userAgentBuilder;
|
||||
_platformInfo = platformInfo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url);
|
||||
|
||||
// Deflate is not a standard and could break depending on implementation.
|
||||
// we should just stick with the more compatible Gzip
|
||||
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
if (PlatformInfo.IsMono)
|
||||
{
|
||||
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.None;
|
||||
webRequest.Headers.Add("Accept-Encoding", "gzip");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Deflate is not a standard and could break depending on implementation.
|
||||
// we should just stick with the more compatible Gzip
|
||||
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
}
|
||||
|
||||
webRequest.Method = request.Method.ToString();
|
||||
webRequest.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
|
||||
@@ -73,6 +92,9 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
if (httpWebResponse == null)
|
||||
{
|
||||
// Workaround for mono not closing connections properly in certain situations.
|
||||
AbortWebRequest(webRequest);
|
||||
|
||||
// The default messages for WebException on mono are pretty horrible.
|
||||
if (e.Status == WebExceptionStatus.NameResolutionFailure)
|
||||
{
|
||||
@@ -101,11 +123,24 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
using (var responseStream = httpWebResponse.GetResponseStream())
|
||||
{
|
||||
if (responseStream != null)
|
||||
if (responseStream != null && responseStream != Stream.Null)
|
||||
{
|
||||
try
|
||||
{
|
||||
data = responseStream.ToBytes();
|
||||
|
||||
if (PlatformInfo.IsMono && httpWebResponse.ContentEncoding == "gzip")
|
||||
{
|
||||
using (var compressedStream = new MemoryStream(data))
|
||||
using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress))
|
||||
using (var decompressedStream = new MemoryStream())
|
||||
{
|
||||
gzip.CopyTo(decompressedStream);
|
||||
data = decompressedStream.ToArray();
|
||||
}
|
||||
|
||||
httpWebResponse.Headers.Remove("Content-Encoding");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -174,5 +209,36 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for mono not closing connections properly on timeouts
|
||||
private void AbortWebRequest(HttpWebRequest webRequest)
|
||||
{
|
||||
// First affected version was mono 5.16
|
||||
if (OsInfo.IsNotWindows && _platformInfo.Version >= new Version(5, 16))
|
||||
{
|
||||
try
|
||||
{
|
||||
var currentOperationInfo = webRequest.GetType().GetField("currentOperation", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var currentOperation = currentOperationInfo.GetValue(webRequest);
|
||||
|
||||
if (currentOperation != null)
|
||||
{
|
||||
var responseStreamInfo = currentOperation.GetType().GetField("responseStream", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var responseStream = responseStreamInfo.GetValue(currentOperation) as Stream;
|
||||
// Note that responseStream will likely be null once mono fixes it.
|
||||
responseStream?.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// This can fail randomly on future mono versions that have been changed/fixed. Log to sentry and ignore.
|
||||
_logger.Trace()
|
||||
.Exception(ex)
|
||||
.Message("Unable to dispose responseStream on mono {0}", _platformInfo.Version)
|
||||
.WriteSentryWarn("MonoCloseWaitPatchFailed", ex.Message)
|
||||
.Write();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,9 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public HttpResponse Execute(HttpRequest request)
|
||||
{
|
||||
var response = ExecuteRequest(request);
|
||||
var cookieContainer = InitializeRequestCookies(request);
|
||||
|
||||
var response = ExecuteRequest(request, cookieContainer);
|
||||
|
||||
if (request.AllowAutoRedirect && response.HasHttpRedirect)
|
||||
{
|
||||
@@ -71,7 +73,7 @@ namespace NzbDrone.Common.Http
|
||||
throw new WebException($"Too many automatic redirections were attempted for {autoRedirectChain.Join(" -> ")}", WebExceptionStatus.ProtocolError);
|
||||
}
|
||||
|
||||
response = ExecuteRequest(request);
|
||||
response = ExecuteRequest(request, cookieContainer);
|
||||
}
|
||||
while (response.HasHttpRedirect);
|
||||
}
|
||||
@@ -98,7 +100,7 @@ namespace NzbDrone.Common.Http
|
||||
return response;
|
||||
}
|
||||
|
||||
private HttpResponse ExecuteRequest(HttpRequest request)
|
||||
private HttpResponse ExecuteRequest(HttpRequest request, CookieContainer cookieContainer)
|
||||
{
|
||||
foreach (var interceptor in _requestInterceptors)
|
||||
{
|
||||
@@ -114,11 +116,11 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
|
||||
var cookies = PrepareRequestCookies(request);
|
||||
PrepareRequestCookies(request, cookieContainer);
|
||||
|
||||
var response = _httpDispatcher.GetResponse(request, cookies);
|
||||
var response = _httpDispatcher.GetResponse(request, cookieContainer);
|
||||
|
||||
HandleResponseCookies(request, cookies);
|
||||
HandleResponseCookies(response, cookieContainer);
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
@@ -137,49 +139,91 @@ namespace NzbDrone.Common.Http
|
||||
return response;
|
||||
}
|
||||
|
||||
private CookieContainer PrepareRequestCookies(HttpRequest request)
|
||||
private CookieContainer InitializeRequestCookies(HttpRequest request)
|
||||
{
|
||||
lock (_cookieContainerCache)
|
||||
{
|
||||
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
|
||||
var sourceContainer = new CookieContainer();
|
||||
|
||||
var presistentContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
|
||||
var persistentCookies = presistentContainer.GetCookies((Uri)request.Url);
|
||||
sourceContainer.Add(persistentCookies);
|
||||
|
||||
if (request.Cookies.Count != 0)
|
||||
{
|
||||
foreach (var pair in request.Cookies)
|
||||
{
|
||||
persistentCookieContainer.Add(new Cookie(pair.Key, pair.Value, "/", request.Url.Host)
|
||||
Cookie cookie;
|
||||
if (pair.Value == null)
|
||||
{
|
||||
// Use Now rather than UtcNow to work around Mono cookie expiry bug.
|
||||
// See https://gist.github.com/ta264/7822b1424f72e5b4c961
|
||||
Expires = DateTime.Now.AddHours(1)
|
||||
});
|
||||
cookie = new Cookie(pair.Key, "", "/")
|
||||
{
|
||||
Expires = DateTime.Now.AddDays(-1)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
cookie = new Cookie(pair.Key, pair.Value, "/")
|
||||
{
|
||||
// Use Now rather than UtcNow to work around Mono cookie expiry bug.
|
||||
// See https://gist.github.com/ta264/7822b1424f72e5b4c961
|
||||
Expires = DateTime.Now.AddHours(1)
|
||||
};
|
||||
}
|
||||
|
||||
sourceContainer.Add((Uri)request.Url, cookie);
|
||||
|
||||
if (request.StoreRequestCookie)
|
||||
{
|
||||
presistentContainer.Add((Uri)request.Url, cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var requestCookies = persistentCookieContainer.GetCookies((Uri)request.Url);
|
||||
|
||||
var cookieContainer = new CookieContainer();
|
||||
|
||||
cookieContainer.Add(requestCookies);
|
||||
|
||||
return cookieContainer;
|
||||
return sourceContainer;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleResponseCookies(HttpRequest request, CookieContainer cookieContainer)
|
||||
private void PrepareRequestCookies(HttpRequest request, CookieContainer cookieContainer)
|
||||
{
|
||||
if (!request.StoreResponseCookie)
|
||||
// Don't collect persistnet cookies for intermediate/redirected urls.
|
||||
/*lock (_cookieContainerCache)
|
||||
{
|
||||
var presistentContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
|
||||
var persistentCookies = presistentContainer.GetCookies((Uri)request.Url);
|
||||
var existingCookies = cookieContainer.GetCookies((Uri)request.Url);
|
||||
|
||||
cookieContainer.Add(persistentCookies);
|
||||
cookieContainer.Add(existingCookies);
|
||||
}*/
|
||||
}
|
||||
|
||||
private void HandleResponseCookies(HttpResponse response, CookieContainer cookieContainer)
|
||||
{
|
||||
var cookieHeaders = response.GetCookieHeaders();
|
||||
if (cookieHeaders.Empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_cookieContainerCache)
|
||||
if (response.Request.StoreResponseCookie)
|
||||
{
|
||||
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
|
||||
lock (_cookieContainerCache)
|
||||
{
|
||||
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
|
||||
|
||||
var cookies = cookieContainer.GetCookies((Uri)request.Url);
|
||||
|
||||
persistentCookieContainer.Add(cookies);
|
||||
foreach (var cookieHeader in cookieHeaders)
|
||||
{
|
||||
try
|
||||
{
|
||||
persistentCookieContainer.SetCookies((Uri)response.Request.Url, cookieHeader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Debug(ex, "Invalid cookie in {0}", response.Request.Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +267,7 @@ namespace NzbDrone.Common.Http
|
||||
public HttpResponse<T> Get<T>(HttpRequest request) where T : new()
|
||||
{
|
||||
var response = Get(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
}
|
||||
|
||||
@@ -241,7 +286,16 @@ namespace NzbDrone.Common.Http
|
||||
public HttpResponse<T> Post<T>(HttpRequest request) where T : new()
|
||||
{
|
||||
var response = Post(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
}
|
||||
|
||||
private void CheckResponseContentType(HttpResponse response)
|
||||
{
|
||||
if (response.Headers.ContentType != null && response.Headers.ContentType.Contains("text/html"))
|
||||
{
|
||||
throw new UnexpectedHtmlContentException(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,19 @@ namespace NzbDrone.Common.Http
|
||||
public HttpRequest Request { get; private set; }
|
||||
public HttpResponse Response { get; private set; }
|
||||
|
||||
public HttpException(HttpRequest request, HttpResponse response)
|
||||
: base(string.Format("HTTP request failed: [{0}:{1}] [{2}] at [{3}]", (int)response.StatusCode, response.StatusCode, request.Method, request.Url))
|
||||
public HttpException(HttpRequest request, HttpResponse response, string message)
|
||||
: base(message)
|
||||
{
|
||||
Request = request;
|
||||
Response = response;
|
||||
}
|
||||
|
||||
public HttpException(HttpRequest request, HttpResponse response)
|
||||
: this(request, response, string.Format("HTTP request failed: [{0}:{1}] [{2}] at [{3}]", (int)response.StatusCode, response.StatusCode, request.Method, request.Url))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public HttpException(HttpResponse response)
|
||||
: this(response.Request, response)
|
||||
{
|
||||
@@ -30,4 +36,4 @@ namespace NzbDrone.Common.Http
|
||||
return base.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,10 @@ namespace NzbDrone.Common.Http
|
||||
Url = new HttpUri(url);
|
||||
Headers = new HttpHeader();
|
||||
AllowAutoRedirect = true;
|
||||
StoreRequestCookie = true;
|
||||
Cookies = new Dictionary<string, string>();
|
||||
|
||||
|
||||
|
||||
if (!RuntimeInfo.IsProduction)
|
||||
{
|
||||
AllowAutoRedirect = false;
|
||||
@@ -37,6 +39,7 @@ namespace NzbDrone.Common.Http
|
||||
public bool ConnectionKeepAlive { get; set; }
|
||||
public bool LogResponseContent { get; set; }
|
||||
public Dictionary<string, string> Cookies { get; private set; }
|
||||
public bool StoreRequestCookie { get; set; }
|
||||
public bool StoreResponseCookie { get; set; }
|
||||
public TimeSpan RequestTimeout { get; set; }
|
||||
public TimeSpan RateLimit { get; set; }
|
||||
|
||||
@@ -355,7 +355,7 @@ namespace NzbDrone.Common.Http
|
||||
FormData.Add(new HttpFormData
|
||||
{
|
||||
Name = key,
|
||||
ContentData = Encoding.UTF8.GetBytes(value.ToString())
|
||||
ContentData = Encoding.UTF8.GetBytes(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture))
|
||||
});
|
||||
|
||||
return this;
|
||||
|
||||
@@ -55,20 +55,22 @@ namespace NzbDrone.Common.Http
|
||||
StatusCode == HttpStatusCode.MovedPermanently ||
|
||||
StatusCode == HttpStatusCode.Found;
|
||||
|
||||
public string[] GetCookieHeaders()
|
||||
{
|
||||
return Headers.GetValues("Set-Cookie") ?? new string[0];
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetCookies()
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
|
||||
var setCookieHeaders = Headers.GetValues("Set-Cookie");
|
||||
if (setCookieHeaders != null)
|
||||
var setCookieHeaders = GetCookieHeaders();
|
||||
foreach (var cookie in setCookieHeaders)
|
||||
{
|
||||
foreach (var cookie in setCookieHeaders)
|
||||
var match = RegexSetCookie.Match(cookie);
|
||||
if (match.Success)
|
||||
{
|
||||
var match = RegexSetCookie.Match(cookie);
|
||||
if (match.Success)
|
||||
{
|
||||
result[match.Groups[1].Value] = match.Groups[2].Value;
|
||||
}
|
||||
result[match.Groups[1].Value] = match.Groups[2].Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace NzbDrone.Common.Http
|
||||
return new HttpUri(Scheme, Host, Port, CombinePath(Path, path), Query, Fragment);
|
||||
}
|
||||
|
||||
private static string CombinePath(string basePath, string relativePath)
|
||||
public static string CombinePath(string basePath, string relativePath)
|
||||
{
|
||||
if (relativePath.IsNullOrWhiteSpace())
|
||||
{
|
||||
|
||||
13
src/NzbDrone.Common/Http/UnexpectedHtmlContentException.cs
Normal file
13
src/NzbDrone.Common/Http/UnexpectedHtmlContentException.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class UnexpectedHtmlContentException : HttpException
|
||||
{
|
||||
public UnexpectedHtmlContentException(HttpResponse response)
|
||||
: base(response.Request, response, $"Site responded with browser content instead of api data. This disruption may be temporary, please try again later. [{response.Request.Url}]")
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Org.Mentalis, Version=1.0.0.1, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNet4.SocksProxy.1.3.4.0\lib\net40\Org.Mentalis.dll</HintPath>
|
||||
@@ -60,11 +60,14 @@
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="ICSharpCode.SharpZipLib">
|
||||
<HintPath>..\packages\ICSharpCode.SharpZipLib.Patched.0.86.5\lib\net20\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
@@ -174,6 +177,7 @@
|
||||
<Compile Include="Http\HttpRequestBuilderFactory.cs" />
|
||||
<Compile Include="Http\Proxy\ProxyType.cs" />
|
||||
<Compile Include="Http\TlsFailureException.cs" />
|
||||
<Compile Include="Http\UnexpectedHtmlContentException.cs" />
|
||||
<Compile Include="Http\TooManyRequestsException.cs" />
|
||||
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
||||
<Compile Include="Http\UserAgentBuilder.cs" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
@@ -14,13 +15,13 @@ namespace NzbDrone.Common.Serializer
|
||||
static Json()
|
||||
{
|
||||
SerializerSetting = new JsonSerializerSettings
|
||||
{
|
||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Formatting = Formatting.Indented,
|
||||
DefaultValueHandling = DefaultValueHandling.Include,
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
{
|
||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Formatting = Formatting.Indented,
|
||||
DefaultValueHandling = DefaultValueHandling.Include,
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
|
||||
SerializerSetting.Converters.Add(new StringEnumConverter { CamelCaseText = true });
|
||||
@@ -34,12 +35,61 @@ namespace NzbDrone.Common.Serializer
|
||||
|
||||
public static T Deserialize<T>(string json) where T : new()
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json, SerializerSetting);
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json, SerializerSetting);
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
throw DetailedJsonReaderException(ex, json);
|
||||
}
|
||||
}
|
||||
|
||||
public static object Deserialize(string json, Type type)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type, SerializerSetting);
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type, SerializerSetting);
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
throw DetailedJsonReaderException(ex, json);
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonReaderException DetailedJsonReaderException(JsonReaderException ex, string json)
|
||||
{
|
||||
var lineNumber = ex.LineNumber == 0 ? 0 : (ex.LineNumber - 1);
|
||||
var linePosition = ex.LinePosition;
|
||||
|
||||
var lines = json.Split('\n');
|
||||
if (lineNumber >= 0 && lineNumber < lines.Length &&
|
||||
linePosition >= 0 && linePosition < lines[lineNumber].Length)
|
||||
{
|
||||
var line = lines[lineNumber];
|
||||
var start = Math.Max(0, linePosition - 20);
|
||||
var end = Math.Min(line.Length, linePosition + 20);
|
||||
|
||||
var snippetBefore = line.Substring(start, linePosition - start);
|
||||
var snippetAfter = line.Substring(linePosition, end - linePosition);
|
||||
var message = ex.Message + " (Json snippet '" + snippetBefore + "<--error-->" + snippetAfter + "')";
|
||||
|
||||
// Not risking updating JSON.net from 9.x to 10.x just to get this as public ctor.
|
||||
var ctor = typeof(JsonReaderException).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(Exception), typeof(string), typeof(int), typeof(int) }, null);
|
||||
if (ctor != null)
|
||||
{
|
||||
return (JsonReaderException)ctor.Invoke(new object[] { message, ex, ex.Path, ex.LineNumber, linePosition });
|
||||
}
|
||||
|
||||
// JSON.net 10.x ctor in case we update later.
|
||||
ctor = typeof(JsonReaderException).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string), typeof(int), typeof(int), typeof(Exception) }, null);
|
||||
if (ctor != null)
|
||||
{
|
||||
return (JsonReaderException)ctor.Invoke(new object[] { message, ex.Path, ex.LineNumber, linePosition, ex });
|
||||
}
|
||||
}
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
public static bool TryDeserialize<T>(string json, out T result) where T : new()
|
||||
@@ -78,4 +128,4 @@ namespace NzbDrone.Common.Serializer
|
||||
Serialize(model, new StreamWriter(outputStream));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="DotNet4.SocksProxy" version="1.3.4.0" targetFramework="net40" />
|
||||
<package id="ICSharpCode.SharpZipLib.Patched" version="0.86.5" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
<package id="SharpRaven" version="2.2.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -66,6 +66,7 @@
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Owin, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Microsoft.Owin.2.1.0\lib\net40\Microsoft.Owin.dll</HintPath>
|
||||
@@ -79,13 +80,19 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Owin">
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\NzbDrone.Common\Properties\SharedAssemblyInfo.cs">
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="Microsoft.Owin" version="2.1.0" targetFramework="net40" />
|
||||
<package id="Microsoft.Owin.Hosting" version="2.1.0" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
<package id="Owin" version="1.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class update_animetosho_urlFixture : MigrationTest<update_animetosho_url>
|
||||
{
|
||||
[TestCase("Newznab", "https://animetosho.org")]
|
||||
[TestCase("Newznab", "http://animetosho.org")]
|
||||
[TestCase("Torznab", "https://animetosho.org")]
|
||||
[TestCase("Torznab", "http://animetosho.org")]
|
||||
public void should_replace_old_url(string impl, string baseUrl)
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Indexers").Row(new
|
||||
{
|
||||
Name = "AnimeTosho",
|
||||
Implementation = impl,
|
||||
Settings = new NewznabSettings121
|
||||
{
|
||||
BaseUrl = baseUrl,
|
||||
ApiPath = "/feed/nabapi"
|
||||
|
||||
}.ToJson(),
|
||||
ConfigContract = impl + "Settings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<IndexerDefinition90>("SELECT * FROM Indexers");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().Settings.ToObject<NewznabSettings121>().BaseUrl.Should().Be(baseUrl.Replace("animetosho", "feed.animetosho"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AlreadyImportedSpecificationFixture : CoreTest<AlreadyImportedSpecification>
|
||||
{
|
||||
private const int FIRST_EPISODE_ID = 1;
|
||||
private const string TITLE = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
|
||||
private Series _series;
|
||||
private QualityModel _hdtv720p;
|
||||
private QualityModel _hdtv1080p;
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
private List<History.History> _history;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var singleEpisodeList = new List<Episode>
|
||||
{
|
||||
new Episode
|
||||
{
|
||||
Id = FIRST_EPISODE_ID,
|
||||
SeasonNumber = 12,
|
||||
EpisodeNumber = 3,
|
||||
EpisodeFileId = 1
|
||||
}
|
||||
};
|
||||
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.Build();
|
||||
|
||||
_hdtv720p = new QualityModel(Quality.HDTV720p, new Revision(version: 1));
|
||||
_hdtv1080p = new QualityModel(Quality.HDTV1080p, new Revision(version: 1));
|
||||
|
||||
_remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
Series = _series,
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = _hdtv720p },
|
||||
Episodes = singleEpisodeList,
|
||||
Release = Builder<ReleaseInfo>.CreateNew()
|
||||
.Build()
|
||||
};
|
||||
|
||||
_history = new List<History.History>();
|
||||
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.EnableCompletedDownloadHandling)
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IHistoryService>()
|
||||
.Setup(s => s.FindByEpisodeId(It.IsAny<int>()))
|
||||
.Returns(_history);
|
||||
}
|
||||
|
||||
private void GivenCdhDisabled()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.EnableCompletedDownloadHandling)
|
||||
.Returns(false);
|
||||
}
|
||||
|
||||
private void GivenHistoryItem(string downloadId, string sourceTitle, QualityModel quality, HistoryEventType eventType)
|
||||
{
|
||||
_history.Add(new History.History
|
||||
{
|
||||
DownloadId = downloadId,
|
||||
SourceTitle = sourceTitle,
|
||||
Quality = quality,
|
||||
Date = DateTime.UtcNow,
|
||||
EventType = eventType
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_CDH_is_disabled()
|
||||
{
|
||||
GivenCdhDisabled();
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_episode_does_not_have_a_file()
|
||||
{
|
||||
_remoteEpisode.Episodes.First().EpisodeFileId = 0;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_episode_does_not_have_grabbed_event()
|
||||
{
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_episode_does_not_have_imported_event()
|
||||
{
|
||||
GivenHistoryItem(Guid.NewGuid().ToString().ToUpper(), TITLE, _hdtv720p, HistoryEventType.Grabbed);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_grabbed_and_imported_quality_is_the_same()
|
||||
{
|
||||
var downloadId = Guid.NewGuid().ToString().ToUpper();
|
||||
|
||||
GivenHistoryItem(downloadId, TITLE, _hdtv720p, HistoryEventType.Grabbed);
|
||||
GivenHistoryItem(downloadId, TITLE, _hdtv720p, HistoryEventType.DownloadFolderImported);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_grabbed_download_id_matches_release_torrent_hash()
|
||||
{
|
||||
var downloadId = Guid.NewGuid().ToString().ToUpper();
|
||||
|
||||
GivenHistoryItem(downloadId, TITLE, _hdtv720p, HistoryEventType.Grabbed);
|
||||
GivenHistoryItem(downloadId, TITLE, _hdtv1080p, HistoryEventType.DownloadFolderImported);
|
||||
|
||||
_remoteEpisode.Release = Builder<TorrentInfo>.CreateNew()
|
||||
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent)
|
||||
.With(t => t.InfoHash = downloadId)
|
||||
.Build();
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_release_title_matches_grabbed_event_source_title()
|
||||
{
|
||||
var downloadId = Guid.NewGuid().ToString().ToUpper();
|
||||
|
||||
GivenHistoryItem(downloadId, TITLE, _hdtv720p, HistoryEventType.Grabbed);
|
||||
GivenHistoryItem(downloadId, TITLE, _hdtv1080p, HistoryEventType.DownloadFolderImported);
|
||||
|
||||
_remoteEpisode.Release = Builder<TorrentInfo>.CreateNew()
|
||||
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent)
|
||||
.With(t => t.InfoHash = downloadId)
|
||||
.Build();
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
Title = "Dexter.S08E01.EDITED.WEBRip.x264-KYR"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.SetConstant<ITermMatcher>(Mocker.Resolve<TermMatcher>());
|
||||
}
|
||||
|
||||
private void GivenRestictions(string required, string ignored)
|
||||
@@ -123,5 +125,16 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("/WEB/", true)]
|
||||
[TestCase("/WEB\b/", false)]
|
||||
[TestCase("/WEb/", false)]
|
||||
[TestCase(@"/\.WEB/", true)]
|
||||
public void should_match_perl_regex(string pattern, bool expected)
|
||||
{
|
||||
GivenRestictions(pattern, null);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
@@ -16,7 +16,7 @@ using NzbDrone.Core.DecisionEngine;
|
||||
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||
{
|
||||
[TestFixture]
|
||||
public class HistorySpecificationFixture : CoreTest<HistorySpecification>
|
||||
@@ -137,6 +137,7 @@ namespace NzbDrone.Core.Test.DiskSpace
|
||||
[TestCase("/var/lib/kubelet")]
|
||||
[TestCase("/var/lib/docker")]
|
||||
[TestCase("/some/place/docker/aufs")]
|
||||
[TestCase("/etc/network")]
|
||||
public void should_not_check_diskspace_for_irrelevant_mounts(string path)
|
||||
{
|
||||
var mount = new Mock<IMount>();
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Never());
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -226,7 +226,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Exactly(2));
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
@@ -16,6 +17,10 @@ namespace NzbDrone.Core.Test.Download
|
||||
public void SetUp()
|
||||
{
|
||||
_epoch = DateTime.UtcNow;
|
||||
|
||||
Mocker.GetMock<IRuntimeInfo>()
|
||||
.SetupGet(v => v.StartTime)
|
||||
.Returns(_epoch - TimeSpan.FromHours(1));
|
||||
}
|
||||
|
||||
private DownloadClientStatus WithStatus(DownloadClientStatus status)
|
||||
|
||||
@@ -290,6 +290,24 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DelugeTests
|
||||
item.CanBeRemoved.Should().Be(canBeRemoved);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetItems_should_ignore_items_without_hash()
|
||||
{
|
||||
_downloading.Hash = null;
|
||||
|
||||
GivenTorrents(new List<DelugeTorrent>
|
||||
{
|
||||
_downloading,
|
||||
_queued
|
||||
});
|
||||
|
||||
var items = Subject.GetItems();
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().Status.Should().Be(DownloadItemStatus.Queued);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_status_with_outputdirs()
|
||||
{
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "0"},
|
||||
{ "size_uploaded", "0"},
|
||||
{ "speed_download", "0" }
|
||||
}
|
||||
}
|
||||
@@ -96,6 +97,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "1000"},
|
||||
{ "size_uploaded", "100"},
|
||||
{ "speed_download", "0" }
|
||||
},
|
||||
}
|
||||
@@ -119,6 +121,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "1000"},
|
||||
{ "size_uploaded", "100"},
|
||||
{ "speed_download", "0" }
|
||||
}
|
||||
}
|
||||
@@ -142,6 +145,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "100"},
|
||||
{ "size_uploaded", "10"},
|
||||
{ "speed_download", "50" }
|
||||
}
|
||||
}
|
||||
@@ -165,6 +169,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "10"},
|
||||
{ "size_uploaded", "1"},
|
||||
{ "speed_download", "0" }
|
||||
}
|
||||
}
|
||||
@@ -188,6 +193,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "1000"},
|
||||
{ "size_uploaded", "100"},
|
||||
{ "speed_download", "0" }
|
||||
}
|
||||
}
|
||||
@@ -211,6 +217,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "1000"},
|
||||
{ "size_uploaded", "100"},
|
||||
{ "speed_download", "0" }
|
||||
}
|
||||
}
|
||||
@@ -234,6 +241,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "1000"},
|
||||
{ "size_uploaded", "100"},
|
||||
{ "speed_download", "0" }
|
||||
}
|
||||
}
|
||||
@@ -257,6 +265,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
Transfer = new Dictionary<string, string>
|
||||
{
|
||||
{ "size_downloaded", "1000"},
|
||||
{ "size_uploaded", "100"},
|
||||
{ "speed_download", "0" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients.QBittorrent;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
{
|
||||
@@ -20,13 +21,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
{
|
||||
Subject.Definition = new DownloadClientDefinition();
|
||||
Subject.Definition.Settings = new QBittorrentSettings
|
||||
{
|
||||
Host = "127.0.0.1",
|
||||
Port = 2222,
|
||||
Username = "admin",
|
||||
Password = "pass",
|
||||
TvCategory = "tv"
|
||||
};
|
||||
{
|
||||
Host = "127.0.0.1",
|
||||
Port = 2222,
|
||||
Username = "admin",
|
||||
Password = "pass",
|
||||
TvCategory = "tv"
|
||||
};
|
||||
|
||||
Mocker.GetMock<ITorrentFileInfoReader>()
|
||||
.Setup(s => s.GetHashFromTorrentFile(It.IsAny<Byte[]>()))
|
||||
@@ -37,8 +38,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new Byte[0]));
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new QBittorrentPreferences());
|
||||
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new QBittorrentPreferences() { DhtEnabled = true });
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxySelector>()
|
||||
.Setup(s => s.GetProxy(It.IsAny<QBittorrentSettings>(), It.IsAny<bool>()))
|
||||
.Returns(Mocker.GetMock<IQBittorrentProxy>().Object);
|
||||
}
|
||||
|
||||
protected void GivenRedirectToMagnet()
|
||||
@@ -95,15 +100,18 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
Subject.Definition.Settings.As<QBittorrentSettings>().RecentTvPriority = (int)QBittorrentPriority.First;
|
||||
}
|
||||
|
||||
protected void GivenMaxRatio(float maxRatio, bool removeOnMaxRatio = true)
|
||||
protected void GivenGlobalSeedLimits(float maxRatio, int maxSeedingTime = -1, bool removeOnMaxRatio = false)
|
||||
{
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new QBittorrentPreferences
|
||||
{
|
||||
RemoveOnMaxRatio = removeOnMaxRatio,
|
||||
MaxRatio = maxRatio
|
||||
});
|
||||
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new QBittorrentPreferences
|
||||
{
|
||||
RemoveOnMaxRatio = removeOnMaxRatio,
|
||||
MaxRatio = maxRatio,
|
||||
MaxRatioEnabled = maxRatio >= 0,
|
||||
MaxSeedingTime = maxSeedingTime,
|
||||
MaxSeedingTimeEnabled = maxSeedingTime >= 0
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void GivenTorrents(List<QBittorrentTorrent> torrents)
|
||||
@@ -154,7 +162,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
VerifyPaused(item);
|
||||
item.RemainingTime.Should().NotBe(TimeSpan.Zero);
|
||||
item.RemainingTime.Should().NotHaveValue();
|
||||
}
|
||||
|
||||
[TestCase("pausedUP")]
|
||||
@@ -162,6 +170,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
[TestCase("uploading")]
|
||||
[TestCase("stalledUP")]
|
||||
[TestCase("checkingUP")]
|
||||
[TestCase("forcedUP")]
|
||||
public void completed_item_should_have_required_properties(string state)
|
||||
{
|
||||
var torrent = new QBittorrentTorrent
|
||||
@@ -184,6 +193,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
|
||||
[TestCase("queuedDL")]
|
||||
[TestCase("checkingDL")]
|
||||
[TestCase("metaDL")]
|
||||
public void queued_item_should_have_required_properties(string state)
|
||||
{
|
||||
var torrent = new QBittorrentTorrent
|
||||
@@ -201,7 +211,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
VerifyQueued(item);
|
||||
item.RemainingTime.Should().NotBe(TimeSpan.Zero);
|
||||
item.RemainingTime.Should().NotHaveValue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -243,7 +253,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
VerifyWarning(item);
|
||||
item.RemainingTime.Should().NotBe(TimeSpan.Zero);
|
||||
item.RemainingTime.Should().NotHaveValue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -271,6 +281,35 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_refuse_magnet_if_no_trackers_provided_and_dht_is_disabled()
|
||||
{
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new QBittorrentPreferences() { DhtEnabled = false });
|
||||
|
||||
var remoteEpisode = CreateRemoteEpisode();
|
||||
remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR";
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteEpisode));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_accept_magnet_if_trackers_provided_and_dht_is_disabled()
|
||||
{
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new QBittorrentPreferences() { DhtEnabled = false });
|
||||
|
||||
var remoteEpisode = CreateRemoteEpisode();
|
||||
remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp://abc";
|
||||
|
||||
Assert.DoesNotThrow(() => Subject.Download(remoteEpisode));
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Verify(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_set_top_priority()
|
||||
{
|
||||
@@ -352,7 +391,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
[Test]
|
||||
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_not_reached()
|
||||
{
|
||||
GivenMaxRatio(1.0f);
|
||||
GivenGlobalSeedLimits(1.0f);
|
||||
|
||||
var torrent = new QBittorrentTorrent
|
||||
{
|
||||
@@ -373,11 +412,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
item.CanMoveFiles.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_paused()
|
||||
protected virtual QBittorrentTorrent GivenCompletedTorrent(
|
||||
string state = "pausedUP",
|
||||
float ratio = 0.1f, float ratioLimit = -2,
|
||||
int seedingTime = 1, int seedingTimeLimit = -2)
|
||||
{
|
||||
GivenMaxRatio(1.0f);
|
||||
|
||||
var torrent = new QBittorrentTorrent
|
||||
{
|
||||
Hash = "HASH",
|
||||
@@ -385,12 +424,32 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
Size = 1000,
|
||||
Progress = 1.0,
|
||||
Eta = 8640000,
|
||||
State = "uploading",
|
||||
State = state,
|
||||
Label = "",
|
||||
SavePath = "",
|
||||
Ratio = 1.0f
|
||||
Ratio = ratio,
|
||||
RatioLimit = ratioLimit,
|
||||
SeedingTimeLimit = seedingTimeLimit
|
||||
};
|
||||
GivenTorrents(new List<QBittorrentTorrent> { torrent });
|
||||
|
||||
GivenTorrents(new List<QBittorrentTorrent>() { torrent });
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Setup(s => s.GetTorrentProperties("HASH", It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new QBittorrentTorrentProperties
|
||||
{
|
||||
Hash = "HASH",
|
||||
SeedingTime = seedingTime
|
||||
});
|
||||
|
||||
return torrent;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(1.0f);
|
||||
GivenCompletedTorrent("uploading", ratio: 1.0f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeFalse();
|
||||
@@ -400,21 +459,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
[Test]
|
||||
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_is_not_set()
|
||||
{
|
||||
GivenMaxRatio(1.0f, false);
|
||||
|
||||
var torrent = new QBittorrentTorrent
|
||||
{
|
||||
Hash = "HASH",
|
||||
Name = _title,
|
||||
Size = 1000,
|
||||
Progress = 1.0,
|
||||
Eta = 8640000,
|
||||
State = "uploading",
|
||||
Label = "",
|
||||
SavePath = "",
|
||||
Ratio = 1.0f
|
||||
};
|
||||
GivenTorrents(new List<QBittorrentTorrent> { torrent });
|
||||
GivenGlobalSeedLimits(-1);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 1.0f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeFalse();
|
||||
@@ -424,21 +470,86 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
[Test]
|
||||
public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached_and_paused()
|
||||
{
|
||||
GivenMaxRatio(1.0f);
|
||||
GivenGlobalSeedLimits(1.0f);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 1.0f);
|
||||
|
||||
var torrent = new QBittorrentTorrent
|
||||
{
|
||||
Hash = "HASH",
|
||||
Name = _title,
|
||||
Size = 1000,
|
||||
Progress = 1.0,
|
||||
Eta = 8640000,
|
||||
State = "pausedUP",
|
||||
Label = "",
|
||||
SavePath = "",
|
||||
Ratio = 1.0f
|
||||
};
|
||||
GivenTorrents(new List<QBittorrentTorrent> { torrent });
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_and_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(2.0f);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 1.0f, ratioLimit: 0.8f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_removable_if_overridden_max_ratio_not_reached_and_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(0.2f);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 0.5f, ratioLimit: 0.8f);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeFalse();
|
||||
item.CanMoveFiles.Should().BeFalse();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_not_be_removable_and_should_not_allow_move_files_if_max_seedingtime_reached_and_not_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(-1, 20);
|
||||
GivenCompletedTorrent("uploading", ratio: 2.0f, seedingTime: 30);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeFalse();
|
||||
item.CanMoveFiles.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_removable_and_should_allow_move_files_if_max_seedingtime_reached_and_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(-1, 20);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 2.0f, seedingTime: 20);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_removable_and_should_allow_move_files_if_overridden_max_seedingtime_reached_and_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(-1, 40);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 2.0f, seedingTime: 20, seedingTimeLimit: 10);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
item.CanMoveFiles.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_removable_if_overridden_max_seedingtime_not_reached_and_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(-1, 20);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 2.0f, seedingTime: 30, seedingTimeLimit: 40);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeFalse();
|
||||
item.CanMoveFiles.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_removable_and_should_allow_move_files_if_max_seedingtime_reached_but_ratio_not_and_paused()
|
||||
{
|
||||
GivenGlobalSeedLimits(2.0f, 20);
|
||||
GivenCompletedTorrent("pausedUP", ratio: 1.0f, seedingTime: 30);
|
||||
|
||||
var item = Subject.GetItems().Single();
|
||||
item.CanBeRemoved.Should().BeTrue();
|
||||
@@ -449,7 +560,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
public void should_get_category_from_the_category_if_set()
|
||||
{
|
||||
const string category = "tv-sonarr";
|
||||
GivenMaxRatio(1.0f);
|
||||
GivenGlobalSeedLimits(1.0f);
|
||||
|
||||
var torrent = new QBittorrentTorrent
|
||||
{
|
||||
@@ -474,7 +585,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
public void should_get_category_from_the_label_if_the_category_is_not_available()
|
||||
{
|
||||
const string category = "tv-sonarr";
|
||||
GivenMaxRatio(1.0f);
|
||||
GivenGlobalSeedLimits(1.0f);
|
||||
|
||||
var torrent = new QBittorrentTorrent
|
||||
{
|
||||
@@ -494,5 +605,32 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
var item = Subject.GetItems().Single();
|
||||
item.Category.Should().Be(category);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_handle_eta_biginteger()
|
||||
{
|
||||
// Let this stand as a lesson to never write temporary unit tests on your dev machine and claim it works.
|
||||
// Commit the tests and let it run with the official build on the official build agents.
|
||||
// (Also don't replace library versions in your build script)
|
||||
|
||||
var json = "{ \"eta\": 18446744073709335000 }";
|
||||
var torrent = Newtonsoft.Json.JsonConvert.DeserializeObject<QBittorrentTorrent>(json);
|
||||
torrent.Eta.ToString().Should().Be("18446744073709335000");
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_should_force_api_version_check()
|
||||
{
|
||||
// Set TestConnection up to fail quick
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Setup(v => v.GetApiVersion(It.IsAny<QBittorrentSettings>()))
|
||||
.Returns(new Version(1, 0));
|
||||
|
||||
Subject.Test();
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxySelector>()
|
||||
.Verify(v => v.GetProxy(It.IsAny<QBittorrentSettings>(), true), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
@@ -54,8 +54,10 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
|
||||
_release = Builder<ReleaseInfo>.CreateNew().Build();
|
||||
|
||||
_parsedEpisodeInfo = Builder<ParsedEpisodeInfo>.CreateNew().Build();
|
||||
_parsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p);
|
||||
_parsedEpisodeInfo = Builder<ParsedEpisodeInfo>.CreateNew()
|
||||
.With(h => h.Quality = new QualityModel(Quality.HDTV720p))
|
||||
.With(h => h.AirDate = null)
|
||||
.Build();
|
||||
|
||||
_remoteEpisode = new RemoteEpisode();
|
||||
_remoteEpisode.Episodes = new List<Episode>{ _episode };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
@@ -98,6 +98,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
.With(h => h.SeriesId = _series.Id)
|
||||
.With(h => h.Title = title)
|
||||
.With(h => h.Release = release)
|
||||
.With(h => h.ParsedEpisodeInfo = new ParsedEpisodeInfo())
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<IPendingReleaseRepository>()
|
||||
|
||||
@@ -8,7 +8,7 @@ using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Metadata.Consumers.Roksbox
|
||||
namespace NzbDrone.Core.Test.Extras.Metadata.Consumers.Roksbox
|
||||
{
|
||||
[TestFixture]
|
||||
public class FindMetadataFileFixture : CoreTest<RoksboxMetadata>
|
||||
@@ -8,7 +8,7 @@ using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Metadata.Consumers.Wdtv
|
||||
namespace NzbDrone.Core.Test.Extras.Metadata.Consumers.Wdtv
|
||||
{
|
||||
[TestFixture]
|
||||
public class FindMetadataFileFixture : CoreTest<WdtvMetadata>
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Extras.Metadata;
|
||||
using NzbDrone.Core.Extras.Metadata.Consumers.Xbmc;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Extras.Metadata.Consumers.Xbmc
|
||||
{
|
||||
[TestFixture]
|
||||
public class FindMetadataFileFixture : CoreTest<XbmcMetadata>
|
||||
{
|
||||
private Series _series;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic())
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_if_filename_is_not_handled()
|
||||
{
|
||||
var path = Path.Combine(_series.Path, "file.jpg");
|
||||
|
||||
Subject.FindMetadataFile(_series, path).Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_metadata_for_xbmc_nfo()
|
||||
{
|
||||
var path = Path.Combine(_series.Path, "the.series.s01e01.episode.nfo");
|
||||
|
||||
Mocker.GetMock<IDetectXbmcNfo>()
|
||||
.Setup(v => v.IsXbmcNfoFile(path))
|
||||
.Returns(true);
|
||||
|
||||
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.EpisodeMetadata);
|
||||
|
||||
Mocker.GetMock<IDetectXbmcNfo>()
|
||||
.Verify(v => v.IsXbmcNfoFile(It.IsAny<string>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_for_scene_nfo()
|
||||
{
|
||||
var path = Path.Combine(_series.Path, "the.series.s01e01.episode.nfo");
|
||||
|
||||
Mocker.GetMock<IDetectXbmcNfo>()
|
||||
.Setup(v => v.IsXbmcNfoFile(path))
|
||||
.Returns(false);
|
||||
|
||||
Subject.FindMetadataFile(_series, path).Should().BeNull();
|
||||
|
||||
Mocker.GetMock<IDetectXbmcNfo>()
|
||||
.Verify(v => v.IsXbmcNfoFile(It.IsAny<string>()), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ namespace NzbDrone.Core.Test.Framework
|
||||
|
||||
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
||||
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
||||
Mocker.SetConstant<ManagedHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>()));
|
||||
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<NLog.Logger>()));
|
||||
Mocker.SetConstant<ManagedHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<IPlatformInfo>(), TestLogger));
|
||||
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
||||
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
|
||||
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
||||
Mocker.SetConstant<ISonarrCloudRequestBuilder>(new SonarrCloudRequestBuilder());
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
using static NzbDrone.Core.HealthCheck.Checks.MonoDebugCheck;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class MonoDebugFixture : CoreTest<MonoDebugCheck>
|
||||
{
|
||||
private void GivenHasStackFrame(bool hasStackFrame)
|
||||
{
|
||||
Mocker.GetMock<StackFrameHelper>()
|
||||
.Setup(f => f.HasStackFrameInfo())
|
||||
.Returns(hasStackFrame);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_if_windows()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_if_not_debug()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
GivenHasStackFrame(false);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_warning_if_not_debug()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
GivenHasStackFrame(false);
|
||||
|
||||
Subject.Check();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_if_debug()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
GivenHasStackFrame(true);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
Mocker.GetMock<IDownloadClientStatusRepository>()
|
||||
.Verify(v => v.UpdateMany(
|
||||
It.Is<List<DownloadClientStatus>>(i => i.All(
|
||||
s => s.DisabledTill.Value < DateTime.UtcNow.AddMinutes(disabledTillTime)))
|
||||
s => s.DisabledTill.Value <= DateTime.UtcNow.AddMinutes(disabledTillTime)))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using FizzWare.NBuilder;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
{
|
||||
@@ -55,7 +56,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
.Returns(new List<string>());
|
||||
}
|
||||
|
||||
private void WithEpisode(int seasonNumber, int episodeNumber, int? sceneSeasonNumber, int? sceneEpisodeNumber)
|
||||
private void WithEpisode(int seasonNumber, int episodeNumber, int? sceneSeasonNumber, int? sceneEpisodeNumber, string airDate = null)
|
||||
{
|
||||
var episode = Builder<Episode>.CreateNew()
|
||||
.With(v => v.SeriesId == _xemSeries.Id)
|
||||
@@ -64,6 +65,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
.With(v => v.EpisodeNumber, episodeNumber)
|
||||
.With(v => v.SceneSeasonNumber, sceneSeasonNumber)
|
||||
.With(v => v.SceneEpisodeNumber, sceneEpisodeNumber)
|
||||
.With(v => v.AirDate = (airDate ?? $"{2000 + seasonNumber}-{episodeNumber:00}-05"))
|
||||
.With(v => v.Monitored = true)
|
||||
.Build();
|
||||
|
||||
@@ -108,10 +110,22 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
.Callback<SeasonSearchCriteria>(s => result.Add(s))
|
||||
.Returns(new List<Parser.Model.ReleaseInfo>());
|
||||
|
||||
_mockIndexer.Setup(v => v.Fetch(It.IsAny<DailyEpisodeSearchCriteria>()))
|
||||
.Callback<DailyEpisodeSearchCriteria>(s => result.Add(s))
|
||||
.Returns(new List<Parser.Model.ReleaseInfo>());
|
||||
|
||||
_mockIndexer.Setup(v => v.Fetch(It.IsAny<DailySeasonSearchCriteria>()))
|
||||
.Callback<DailySeasonSearchCriteria>(s => result.Add(s))
|
||||
.Returns(new List<Parser.Model.ReleaseInfo>());
|
||||
|
||||
_mockIndexer.Setup(v => v.Fetch(It.IsAny<AnimeEpisodeSearchCriteria>()))
|
||||
.Callback<AnimeEpisodeSearchCriteria>(s => result.Add(s))
|
||||
.Returns(new List<Parser.Model.ReleaseInfo>());
|
||||
|
||||
_mockIndexer.Setup(v => v.Fetch(It.IsAny<SpecialEpisodeSearchCriteria>()))
|
||||
.Callback<SpecialEpisodeSearchCriteria>(s => result.Add(s))
|
||||
.Returns(new List<Parser.Model.ReleaseInfo>());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -249,6 +263,68 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
criteria.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void season_search_for_daily_should_search_multiple_years()
|
||||
{
|
||||
WithEpisode(1, 1, null, null, "2005-12-30");
|
||||
WithEpisode(1, 2, null, null, "2005-12-31");
|
||||
WithEpisode(1, 3, null, null, "2006-01-01");
|
||||
WithEpisode(1, 4, null, null, "2006-01-02");
|
||||
_xemSeries.SeriesType = SeriesTypes.Daily;
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.SeasonSearch(_xemSeries.Id, 1, false, true);
|
||||
|
||||
var criteria = allCriteria.OfType<DailySeasonSearchCriteria>().ToList();
|
||||
|
||||
criteria.Count.Should().Be(2);
|
||||
criteria[0].Year.Should().Be(2005);
|
||||
criteria[1].Year.Should().Be(2006);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void season_search_for_daily_should_search_single_episode_if_possible()
|
||||
{
|
||||
WithEpisode(1, 1, null, null, "2005-12-30");
|
||||
WithEpisode(1, 2, null, null, "2005-12-31");
|
||||
WithEpisode(1, 3, null, null, "2006-01-01");
|
||||
_xemSeries.SeriesType = SeriesTypes.Daily;
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.SeasonSearch(_xemSeries.Id, 1, false, true);
|
||||
|
||||
var criteria1 = allCriteria.OfType<DailySeasonSearchCriteria>().ToList();
|
||||
var criteria2 = allCriteria.OfType<DailyEpisodeSearchCriteria>().ToList();
|
||||
|
||||
criteria1.Count.Should().Be(1);
|
||||
criteria1[0].Year.Should().Be(2005);
|
||||
|
||||
criteria2.Count.Should().Be(1);
|
||||
criteria2[0].AirDate.Should().Be(new DateTime(2006, 1, 1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void season_search_for_daily_should_not_search_for_unmonitored_episodes()
|
||||
{
|
||||
WithEpisode(1, 1, null, null, "2005-12-30");
|
||||
WithEpisode(1, 2, null, null, "2005-12-31");
|
||||
WithEpisode(1, 3, null, null, "2006-01-01");
|
||||
_xemSeries.SeriesType = SeriesTypes.Daily;
|
||||
_xemEpisodes[0].Monitored = false;
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.SeasonSearch(_xemSeries.Id, 1, false, true);
|
||||
|
||||
var criteria1 = allCriteria.OfType<DailySeasonSearchCriteria>().ToList();
|
||||
var criteria2 = allCriteria.OfType<DailyEpisodeSearchCriteria>().ToList();
|
||||
|
||||
criteria1.Should().HaveCount(0);
|
||||
criteria2.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void getscenenames_should_use_seasonnumber_if_no_scene_seasonnumber_is_available()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
@@ -16,6 +17,10 @@ namespace NzbDrone.Core.Test.IndexerTests
|
||||
public void SetUp()
|
||||
{
|
||||
_epoch = DateTime.UtcNow;
|
||||
|
||||
Mocker.GetMock<IRuntimeInfo>()
|
||||
.SetupGet(v => v.StartTime)
|
||||
.Returns(_epoch - TimeSpan.FromHours(1));
|
||||
}
|
||||
|
||||
private void WithStatus(IndexerStatus status)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Torznab;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SeedConfigProviderFixture : CoreTest<SeedConfigProvider>
|
||||
{
|
||||
[Test]
|
||||
public void should_not_return_config_for_non_existent_indexer()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(v => v.Get(It.IsAny<int>()))
|
||||
.Throws(new ModelNotFoundException(typeof(IndexerDefinition), 0));
|
||||
|
||||
var result = Subject.GetSeedConfiguration(new RemoteEpisode
|
||||
{
|
||||
Release = new ReleaseInfo()
|
||||
{
|
||||
DownloadProtocol = DownloadProtocol.Torrent,
|
||||
IndexerId = 0
|
||||
}
|
||||
});
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_season_time_for_season_packs()
|
||||
{
|
||||
var settings = new TorznabSettings();
|
||||
settings.SeedCriteria.SeasonPackSeedTime = 10;
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(v => v.Get(It.IsAny<int>()))
|
||||
.Returns(new IndexerDefinition
|
||||
{
|
||||
Settings = settings
|
||||
});
|
||||
|
||||
var result = Subject.GetSeedConfiguration(new RemoteEpisode
|
||||
{
|
||||
Release = new ReleaseInfo()
|
||||
{
|
||||
DownloadProtocol = DownloadProtocol.Torrent,
|
||||
IndexerId = 1
|
||||
},
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
FullSeason = true
|
||||
}
|
||||
});
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result.SeedTime.Should().Be(TimeSpan.FromMinutes(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AugmentEpisodesFixture : CoreTest<AggregateEpisodes>
|
||||
{
|
||||
private Series _series;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew().Build();
|
||||
|
||||
var augmenters = new List<Mock<IAggregateLocalEpisode>>
|
||||
{
|
||||
new Mock<IAggregateLocalEpisode>()
|
||||
};
|
||||
|
||||
Mocker.SetConstant(augmenters.Select(c => c.Object));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_for_full_season()
|
||||
{
|
||||
var fileEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
var folderEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01");
|
||||
var localEpisode = new LocalEpisode
|
||||
{
|
||||
FileEpisodeInfo = fileEpisodeInfo,
|
||||
FolderEpisodeInfo = folderEpisodeInfo,
|
||||
Path = @"C:\Test\Unsorted TV\Series.Title.S01\Series.Title.S01E01.mkv".AsOsAgnostic(),
|
||||
Series = _series
|
||||
};
|
||||
|
||||
Subject.Aggregate(localEpisode, false);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_when_it_contains_more_than_one_valid_video_file()
|
||||
{
|
||||
var fileEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
var folderEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01");
|
||||
var localEpisode = new LocalEpisode
|
||||
{
|
||||
FileEpisodeInfo = fileEpisodeInfo,
|
||||
FolderEpisodeInfo = folderEpisodeInfo,
|
||||
Path = @"C:\Test\Unsorted TV\Series.Title.S01\Series.Title.S01E01.mkv".AsOsAgnostic(),
|
||||
Series = _series
|
||||
};
|
||||
|
||||
Subject.Aggregate(localEpisode, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_name_if_file_name_is_scene_name()
|
||||
{
|
||||
var fileEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
var folderEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
var localEpisode = new LocalEpisode
|
||||
{
|
||||
FileEpisodeInfo = fileEpisodeInfo,
|
||||
FolderEpisodeInfo = folderEpisodeInfo,
|
||||
Path = @"C:\Test\Unsorted TV\Series.Title.S01E01\Series.Title.S01E01.720p.HDTV-Sonarr.mkv".AsOsAgnostic(),
|
||||
Series = _series
|
||||
};
|
||||
|
||||
Subject.Aggregate(localEpisode, false);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_when_only_one_video_file()
|
||||
{
|
||||
var fileEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
var folderEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
var localEpisode = new LocalEpisode
|
||||
{
|
||||
FileEpisodeInfo = fileEpisodeInfo,
|
||||
FolderEpisodeInfo = folderEpisodeInfo,
|
||||
Path = @"C:\Test\Unsorted TV\Series.Title.S01E01\Series.Title.S01E01.mkv".AsOsAgnostic(),
|
||||
Series = _series
|
||||
};
|
||||
|
||||
Subject.Aggregate(localEpisode, false);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetEpisodes(folderEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_file_when_folder_is_absolute_and_file_is_not()
|
||||
{
|
||||
var fileEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
var folderEpisodeInfo = Parser.Parser.ParseTitle("Series.Title.01");
|
||||
var localEpisode = new LocalEpisode
|
||||
{
|
||||
FileEpisodeInfo = fileEpisodeInfo,
|
||||
FolderEpisodeInfo = folderEpisodeInfo,
|
||||
Path = @"C:\Test\Unsorted TV\Series.Title.101\Series.Title.S01E01.mkv".AsOsAgnostic(),
|
||||
Series = _series
|
||||
};
|
||||
|
||||
Subject.Aggregate(localEpisode, false);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_special_info_when_not_null()
|
||||
{
|
||||
var fileEpisodeInfo = Parser.Parser.ParseTitle("S00E01");
|
||||
var specialEpisodeInfo = fileEpisodeInfo.JsonClone();
|
||||
|
||||
var localEpisode = new LocalEpisode
|
||||
{
|
||||
FileEpisodeInfo = fileEpisodeInfo,
|
||||
Path = @"C:\Test\TV\Series\Specials\S00E01.mkv".AsOsAgnostic(),
|
||||
Series = _series
|
||||
};
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.ParseSpecialEpisodeTitle(fileEpisodeInfo, It.IsAny<string>(), _series))
|
||||
.Returns(specialEpisodeInfo);
|
||||
|
||||
Subject.Aggregate(localEpisode, false);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetEpisodes(specialEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AugmentQualityFixture : CoreTest<AggregateQuality>
|
||||
{
|
||||
private Mock<IAugmentQuality> _mediaInfoAugmenter;
|
||||
private Mock<IAugmentQuality> _fileExtensionAugmenter;
|
||||
private Mock<IAugmentQuality> _nameAugmenter;
|
||||
|
||||
private IEnumerable<IAugmentQuality> _qualityAugmenters;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_mediaInfoAugmenter = new Mock<IAugmentQuality>();
|
||||
_fileExtensionAugmenter = new Mock<IAugmentQuality>();
|
||||
_nameAugmenter = new Mock<IAugmentQuality>();
|
||||
|
||||
_mediaInfoAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalEpisode>()))
|
||||
.Returns(AugmentQualityResult.ResolutionOnly(1080, Confidence.MediaInfo));
|
||||
|
||||
_fileExtensionAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalEpisode>()))
|
||||
.Returns(new AugmentQualityResult(QualitySource.Television, Confidence.Fallback, 720, Confidence.Fallback, new Revision()));
|
||||
|
||||
_nameAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalEpisode>()))
|
||||
.Returns(new AugmentQualityResult(QualitySource.Television, Confidence.Default, 480, Confidence.Default, new Revision()));
|
||||
}
|
||||
|
||||
private void GivenAugmenters(params Mock<IAugmentQuality>[] mocks)
|
||||
{
|
||||
Mocker.SetConstant<IEnumerable<IAugmentQuality>>(mocks.Select(c => c.Object));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_HDTV720_from_extension_when_other_augments_are_null()
|
||||
{
|
||||
var nullMock = new Mock<IAugmentQuality>();
|
||||
nullMock.Setup(s => s.AugmentQuality(It.IsAny<LocalEpisode>()))
|
||||
.Returns<LocalEpisode>(l => null);
|
||||
|
||||
GivenAugmenters(_fileExtensionAugmenter, nullMock);
|
||||
|
||||
var result = Subject.Aggregate(new LocalEpisode(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Extension);
|
||||
result.Quality.Quality.Should().Be(Quality.HDTV720p);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_SDTV_when_HDTV720_came_from_extension()
|
||||
{
|
||||
GivenAugmenters(_fileExtensionAugmenter, _nameAugmenter);
|
||||
|
||||
var result = Subject.Aggregate(new LocalEpisode(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Name);
|
||||
result.Quality.Quality.Should().Be(Quality.SDTV);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_HDTV1080p_when_HDTV720_came_from_extension_and_mediainfo_indicates_1080()
|
||||
{
|
||||
GivenAugmenters(_fileExtensionAugmenter, _mediaInfoAugmenter);
|
||||
|
||||
var result = Subject.Aggregate(new LocalEpisode(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.MediaInfo);
|
||||
result.Quality.Quality.Should().Be(Quality.HDTV1080p);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_HDTV1080p_when_SDTV_came_from_name_and_mediainfo_indicates_1080()
|
||||
{
|
||||
GivenAugmenters(_nameAugmenter, _mediaInfoAugmenter);
|
||||
|
||||
var result = Subject.Aggregate(new LocalEpisode(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.MediaInfo);
|
||||
result.Quality.Quality.Should().Be(Quality.HDTV1080p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
[TestFixture]
|
||||
public class AugmentQualityFromMediaInfoFixture : CoreTest<AugmentQualityFromMediaInfo>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_null_if_media_info_is_null()
|
||||
{
|
||||
var localEpisode = Builder<LocalEpisode>.CreateNew()
|
||||
.With(l => l.MediaInfo = null)
|
||||
.Build();
|
||||
|
||||
Subject.AugmentQuality(localEpisode).Should().Be(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_if_media_info_width_is_zero()
|
||||
{
|
||||
var mediaInfo = Builder<MediaInfoModel>.CreateNew()
|
||||
.With(m => m.Width = 0)
|
||||
.Build();
|
||||
|
||||
var localEpisode = Builder<LocalEpisode>.CreateNew()
|
||||
.With(l => l.MediaInfo = mediaInfo)
|
||||
.Build();
|
||||
|
||||
Subject.AugmentQuality(localEpisode).Should().Be(null);
|
||||
}
|
||||
|
||||
[TestCase(4096, 2160)] // True 4K
|
||||
[TestCase(4000, 2160)]
|
||||
[TestCase(3840, 2160)] // 4K UHD
|
||||
[TestCase(3200, 2160)]
|
||||
[TestCase(2000, 1080)]
|
||||
[TestCase(1920, 1080)] // Full HD
|
||||
[TestCase(1800, 1080)]
|
||||
[TestCase(1490, 720)]
|
||||
[TestCase(1280, 720)] // HD
|
||||
[TestCase(1200, 720)]
|
||||
[TestCase(800, 480)]
|
||||
[TestCase(720, 480)] // SDTV
|
||||
[TestCase(600, 480)]
|
||||
[TestCase(100, 480)]
|
||||
public void should_return_closest_resolution(int mediaInfoWidth, int expectedResolution)
|
||||
{
|
||||
var mediaInfo = Builder<MediaInfoModel>.CreateNew()
|
||||
.With(m => m.Width = mediaInfoWidth)
|
||||
.Build();
|
||||
|
||||
var localEpisode = Builder<LocalEpisode>.CreateNew()
|
||||
.With(l => l.MediaInfo = mediaInfo)
|
||||
.Build();
|
||||
|
||||
var result = Subject.AugmentQuality(localEpisode);
|
||||
|
||||
result.Should().NotBe(null);
|
||||
result.Resolution.Should().Be(expectedResolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@@ -15,6 +14,7 @@ using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
using FizzWare.NBuilder;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
@@ -54,6 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), It.IsAny<DownloadClientItem>())).Returns(Decision.Reject("_fail3"));
|
||||
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(e => e.Path = @"C:\Test\Series".AsOsAgnostic())
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
@@ -67,10 +68,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi"
|
||||
};
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()))
|
||||
.Returns(_localEpisode);
|
||||
|
||||
GivenVideoFiles(new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi".AsOsAgnostic() });
|
||||
}
|
||||
|
||||
@@ -88,20 +85,31 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
.Returns(_videoFiles);
|
||||
}
|
||||
|
||||
private void GivenAugmentationSuccess()
|
||||
{
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Setup(s => s.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()))
|
||||
.Callback<LocalEpisode, bool>((localEpisode, otherFiles) =>
|
||||
{
|
||||
localEpisode.Episodes = _localEpisode.Episodes;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_call_all_specifications()
|
||||
{
|
||||
var downloadClientItem = Builder<DownloadClientItem>.CreateNew().Build();
|
||||
GivenAugmentationSuccess();
|
||||
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, new Series(), downloadClientItem, null, false);
|
||||
Subject.GetImportDecisions(_videoFiles, _series, downloadClientItem, null, false);
|
||||
|
||||
_fail1.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
|
||||
_fail2.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
|
||||
_fail3.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
|
||||
_pass1.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
|
||||
_pass2.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
|
||||
_pass3.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
|
||||
_fail1.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), downloadClientItem), Times.Once());
|
||||
_fail2.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), downloadClientItem), Times.Once());
|
||||
_fail3.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), downloadClientItem), Times.Once());
|
||||
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), downloadClientItem), Times.Once());
|
||||
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), downloadClientItem), Times.Once());
|
||||
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), downloadClientItem), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -109,7 +117,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
GivenSpecifications(_fail1);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
result.Single().Approved.Should().BeFalse();
|
||||
}
|
||||
@@ -119,17 +127,18 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
GivenSpecifications(_pass1, _fail1, _pass2, _pass3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
result.Single().Approved.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_pass_if_all_specs_pass()
|
||||
public void should_return_approved_if_all_specs_pass()
|
||||
{
|
||||
GivenAugmentationSuccess();
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
result.Single().Approved.Should().BeTrue();
|
||||
}
|
||||
@@ -137,9 +146,10 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
[Test]
|
||||
public void should_have_same_number_of_rejections_as_specs_that_failed()
|
||||
{
|
||||
GivenAugmentationSuccess();
|
||||
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series);
|
||||
result.Single().Rejections.Should().HaveCount(3);
|
||||
}
|
||||
|
||||
@@ -148,8 +158,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
GivenSpecifications(_pass1);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()))
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Setup(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_videoFiles = new List<string>
|
||||
@@ -163,76 +173,17 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Verify(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_file_quality_if_folder_quality_is_null()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_file_quality_if_file_quality_was_determined_by_name()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo{Quality = new QualityModel(Quality.SDTV)}, true);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_quality_when_file_quality_was_determined_by_the_extension()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
GivenVideoFiles(new string[] { @"C:\Test\Unsorted\The.Office.S03E115.mkv".AsOsAgnostic() });
|
||||
|
||||
_localEpisode.Path = _videoFiles.Single();
|
||||
_localEpisode.Quality.QualitySource = QualitySource.Extension;
|
||||
_localEpisode.Quality.Quality = Quality.HDTV720p;
|
||||
|
||||
var expectedQuality = new QualityModel(Quality.SDTV);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo { Quality = expectedQuality }, true);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_quality_when_greater_than_file_quality()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
GivenVideoFiles(new string[] { @"C:\Test\Unsorted\The.Office.S03E115.mkv".AsOsAgnostic() });
|
||||
|
||||
_localEpisode.Path = _videoFiles.Single();
|
||||
_localEpisode.Quality.Quality = Quality.HDTV720p;
|
||||
|
||||
var expectedQuality = new QualityModel(Quality.Bluray720p);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo { Quality = expectedQuality }, true);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_throw_if_episodes_are_not_found()
|
||||
{
|
||||
GivenSpecifications(_pass1);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()))
|
||||
.Returns(new LocalEpisode() { Path = "test", ParsedEpisodeInfo = new ParsedEpisodeInfo { } });
|
||||
|
||||
_videoFiles = new List<string>
|
||||
{
|
||||
"The.Office.S03E115.DVDRip.XviD-OSiTV",
|
||||
@@ -244,162 +195,18 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
|
||||
var decisions = Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Verify(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
|
||||
|
||||
decisions.Should().HaveCount(3);
|
||||
decisions.First().Rejections.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_for_full_season()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01\S01E01.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01\S01E02.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01\S01E03.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(3));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(p => p != null), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_when_it_contains_more_than_one_valid_video_file()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\1x01.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(2));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(p => p != null), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_when_only_one_video_file()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), true), Times.Exactly(1));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_when_only_one_video_file_and_a_sample()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.sample.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles.ToList());
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(_series, It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns((Series s, string path, bool special) =>
|
||||
{
|
||||
if (path.Contains("sample"))
|
||||
{
|
||||
return DetectSampleResult.Sample;
|
||||
}
|
||||
|
||||
return DetectSampleResult.NotSample;
|
||||
});
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), true), Times.Exactly(2));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_name_if_file_name_is_scene_name()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-LOL\Series.Title.S01E01.720p.HDTV-LOL.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01.720p.HDTV-LOL");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(1));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(p => p != null), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_quality_when_it_is_unknown()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
_series.Profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.Unknown)
|
||||
};
|
||||
|
||||
|
||||
var folderQuality = new QualityModel(Quality.Unknown);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo { Quality = folderQuality}, true);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(_quality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_a_decision_when_exception_is_caught()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()))
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Setup(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_videoFiles = new List<string>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
@@ -22,17 +22,24 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi".AsOsAgnostic(),
|
||||
Size = 100,
|
||||
Series = Builder<Series>.CreateNew().Build(),
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
FileEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
FullSeason = false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_no_fileinfo_available()
|
||||
{
|
||||
_localEpisode.FileEpisodeInfo = null;
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_when_file_contains_the_full_season()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.FullSeason = true;
|
||||
_localEpisode.FileEpisodeInfo.FullSeason = true;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
[TestFixture]
|
||||
public class GrabbedReleaseQualityFixture : CoreTest<GrabbedReleaseQualitySpecification>
|
||||
{
|
||||
private LocalEpisode _localEpisode;
|
||||
private DownloadClientItem _downloadClientItem;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_localEpisode = Builder<LocalEpisode>.CreateNew()
|
||||
.With(l => l.Quality = new QualityModel(Quality.Bluray720p))
|
||||
.Build();
|
||||
|
||||
_downloadClientItem = Builder<DownloadClientItem>.CreateNew()
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void GivenHistory(List<History.History> history)
|
||||
{
|
||||
Mocker.GetMock<IHistoryService>()
|
||||
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
|
||||
.Returns(history);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_when_downloadClientItem_is_null()
|
||||
{
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_no_history_for_downloadId()
|
||||
{
|
||||
GivenHistory(new List<History.History>());
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_no_grabbed_history_for_downloadId()
|
||||
{
|
||||
var history = Builder<History.History>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.EventType = HistoryEventType.Unknown)
|
||||
.BuildList();
|
||||
|
||||
GivenHistory(history);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_grabbed_history_is_for_a_season_pack()
|
||||
{
|
||||
var history = Builder<History.History>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.EventType = HistoryEventType.Grabbed)
|
||||
.With(h => h.Quality = _localEpisode.Quality)
|
||||
.With(h => h.SourceTitle = "Series.Title.S01.720p.HDTV.x264-RlsGroup")
|
||||
.BuildList();
|
||||
|
||||
GivenHistory(history);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_grabbed_history_quality_is_unknown()
|
||||
{
|
||||
var history = Builder<History.History>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.EventType = HistoryEventType.Grabbed)
|
||||
.With(h => h.Quality = new QualityModel(Quality.Unknown))
|
||||
.BuildList();
|
||||
|
||||
GivenHistory(history);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_grabbed_history_quality_matches()
|
||||
{
|
||||
var history = Builder<History.History>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.EventType = HistoryEventType.Grabbed)
|
||||
.With(h => h.Quality = _localEpisode.Quality)
|
||||
.BuildList();
|
||||
|
||||
GivenHistory(history);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_grabbed_history_quality_does_not_match()
|
||||
{
|
||||
var history = Builder<History.History>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.EventType = HistoryEventType.Grabbed)
|
||||
.With(h => h.Quality = new QualityModel(Quality.HDTV720p))
|
||||
.BuildList();
|
||||
|
||||
GivenHistory(history);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
@@ -18,9 +18,16 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
_localEpisode = Builder<LocalEpisode>.CreateNew()
|
||||
.With(l => l.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic())
|
||||
.With(l => l.ParsedEpisodeInfo =
|
||||
.With(l => l.FileEpisodeInfo =
|
||||
Builder<ParsedEpisodeInfo>.CreateNew()
|
||||
.With(p => p.EpisodeNumbers = new[] {5})
|
||||
.With(p => p.EpisodeNumbers = new[] { 5 })
|
||||
.With(p => p.SeasonNumber == 1)
|
||||
.With(p => p.FullSeason = false)
|
||||
.Build())
|
||||
.With(l => l.FolderEpisodeInfo =
|
||||
Builder<ParsedEpisodeInfo>.CreateNew()
|
||||
.With(p => p.EpisodeNumbers = new[] { 1 })
|
||||
.With(p => p.SeasonNumber == 1)
|
||||
.With(p => p.FullSeason = false)
|
||||
.Build())
|
||||
.Build();
|
||||
@@ -38,6 +45,16 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
public void should_be_accepted_if_folder_name_is_not_parseable()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title\S01E01.mkv".AsOsAgnostic();
|
||||
_localEpisode.FolderEpisodeInfo = null;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_file_name_is_not_parseable()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01\AFDAFD.mkv".AsOsAgnostic();
|
||||
_localEpisode.FileEpisodeInfo = null;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
@@ -46,6 +63,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
public void should_should_be_accepted_for_full_season()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01\S01E01.mkv".AsOsAgnostic();
|
||||
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new int[0];
|
||||
_localEpisode.FolderEpisodeInfo.FullSeason = true;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
@@ -53,32 +72,64 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
[Test]
|
||||
public void should_be_accepted_if_file_and_folder_have_the_same_episode()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.FileEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E01.mkv".AsOsAgnostic();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_file_is_one_episode_in_folder()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.FileEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E01.mkv".AsOsAgnostic();
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_disregard_subfolder()
|
||||
{
|
||||
_localEpisode.FileEpisodeInfo.EpisodeNumbers = new[] { 5, 6 };
|
||||
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1, 2 };
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E05E06.mkv".AsOsAgnostic();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_file_and_folder_do_not_have_same_episode()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic();
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_file_and_folder_do_not_have_same_episodes()
|
||||
public void should_be_rejected_if_file_and_folder_do_not_have_the_same_episodes()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 5, 6 };
|
||||
_localEpisode.FileEpisodeInfo.EpisodeNumbers = new[] { 5, 6 };
|
||||
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1, 2 };
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E05E06.mkv".AsOsAgnostic();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_file_and_folder_do_not_have_episodes_from_the_same_season()
|
||||
{
|
||||
_localEpisode.FileEpisodeInfo.SeasonNumber = 2;
|
||||
_localEpisode.FileEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
|
||||
_localEpisode.FolderEpisodeInfo.FullSeason = true;
|
||||
_localEpisode.FolderEpisodeInfo.SeasonNumber = 1;
|
||||
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1, 2 };
|
||||
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01.720p.HDTV-Sonarr\S02E01.mkv".AsOsAgnostic();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
@@ -34,6 +35,8 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
_rejectedDecisions = new List<ImportDecision>();
|
||||
_approvedDecisions = new List<ImportDecision>();
|
||||
|
||||
var outputPath = @"C:\Test\Unsorted\TV\30.Rock.S01E01".AsOsAgnostic();
|
||||
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic())
|
||||
@@ -58,10 +61,7 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
Episodes = new List<Episode> { episode },
|
||||
Path = Path.Combine(series.Path, "30 Rock - S01E01 - Pilot.avi"),
|
||||
Quality = new QualityModel(Quality.Bluray720p),
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
ReleaseGroup = "DRONE"
|
||||
}
|
||||
ReleaseGroup = "DRONE"
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -69,7 +69,14 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>(), It.IsAny<bool>()))
|
||||
.Returns(new EpisodeFileMoveResult());
|
||||
|
||||
_downloadClientItem = Builder<DownloadClientItem>.CreateNew().Build();
|
||||
_downloadClientItem = Builder<DownloadClientItem>.CreateNew()
|
||||
.With(d => d.OutputPath = new OsPath(outputPath))
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void GivenNewDownload()
|
||||
{
|
||||
_approvedDecisions.ForEach(a => a.LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), Path.GetFileName(a.LocalEpisode.Path)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -143,6 +150,7 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[Test]
|
||||
public void should_use_nzb_title_as_scene_name()
|
||||
{
|
||||
GivenNewDownload();
|
||||
_downloadClientItem.Title = "malcolm.in.the.middle.s02e05.dvdrip.xvid-ingot";
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
@@ -155,6 +163,7 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[TestCase(".nzb")]
|
||||
public void should_remove_extension_from_nzb_title_for_scene_name(string extension)
|
||||
{
|
||||
GivenNewDownload();
|
||||
var title = "malcolm.in.the.middle.s02e05.dvdrip.xvid-ingot";
|
||||
|
||||
_downloadClientItem.Title = title + extension;
|
||||
@@ -167,7 +176,8 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[Test]
|
||||
public void should_not_use_nzb_title_as_scene_name_if_full_season()
|
||||
{
|
||||
_approvedDecisions.First().LocalEpisode.Path = "c:\\tv\\season1\\malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv".AsOsAgnostic();
|
||||
GivenNewDownload();
|
||||
_approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv");
|
||||
_downloadClientItem.Title = "malcolm.in.the.middle.s02.dvdrip.xvid-ingot";
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
@@ -178,7 +188,8 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[Test]
|
||||
public void should_use_file_name_as_scenename_only_if_it_looks_like_scenename()
|
||||
{
|
||||
_approvedDecisions.First().LocalEpisode.Path = "c:\\tv\\malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv".AsOsAgnostic();
|
||||
GivenNewDownload();
|
||||
_approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
|
||||
|
||||
@@ -188,7 +199,8 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[Test]
|
||||
public void should_not_use_file_name_as_scenename_if_it_doesnt_looks_like_scenename()
|
||||
{
|
||||
_approvedDecisions.First().LocalEpisode.Path = "c:\\tv\\aaaaa.mkv".AsOsAgnostic();
|
||||
GivenNewDownload();
|
||||
_approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
|
||||
|
||||
@@ -226,7 +238,11 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[Test]
|
||||
public void should_copy_when_cannot_move_files_downloads()
|
||||
{
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, new DownloadClientItem { Title = "30.Rock.S01E01", CanMoveFiles = false});
|
||||
GivenNewDownload();
|
||||
_downloadClientItem.Title = "30.Rock.S01E01";
|
||||
_downloadClientItem.CanMoveFiles = false;
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
|
||||
Mocker.GetMock<IUpgradeMediaFiles>()
|
||||
.Verify(v => v.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), _approvedDecisions.First().LocalEpisode, true), Times.Once());
|
||||
@@ -235,10 +251,155 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[Test]
|
||||
public void should_use_override_importmode()
|
||||
{
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, new DownloadClientItem { Title = "30.Rock.S01E01", CanMoveFiles = false }, ImportMode.Move);
|
||||
GivenNewDownload();
|
||||
_downloadClientItem.Title = "30.Rock.S01E01";
|
||||
_downloadClientItem.CanMoveFiles = false;
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem, ImportMode.Move);
|
||||
|
||||
Mocker.GetMock<IUpgradeMediaFiles>()
|
||||
.Verify(v => v.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), _approvedDecisions.First().LocalEpisode, false), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_file_name_only_for_download_client_item_without_a_job_folder()
|
||||
{
|
||||
var fileName = "Series.Title.S01E01.720p.HDTV.x264-Sonarr.mkv";
|
||||
var path = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), fileName);
|
||||
|
||||
_downloadClientItem.OutputPath = new OsPath(path);
|
||||
_approvedDecisions.First().LocalEpisode.Path = path;
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == fileName)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_and_file_name_only_for_download_client_item_with_a_job_folder()
|
||||
{
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name);
|
||||
|
||||
_downloadClientItem.OutputPath = new OsPath(outputPath);
|
||||
_approvedDecisions.First().LocalEpisode.Path = Path.Combine(outputPath, name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}\\{name}.mkv".AsOsAgnostic())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_include_intermediate_folders_for_download_client_item_with_a_job_folder()
|
||||
{
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name);
|
||||
|
||||
_downloadClientItem.OutputPath = new OsPath(outputPath);
|
||||
_approvedDecisions.First().LocalEpisode.Path = Path.Combine(outputPath, "subfolder", name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}\\subfolder\\{name}.mkv".AsOsAgnostic())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_info_release_title_to_find_relative_path()
|
||||
{
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name);
|
||||
var localEpisode = _approvedDecisions.First().LocalEpisode;
|
||||
|
||||
localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name };
|
||||
localEpisode.Path = Path.Combine(outputPath, "subfolder", name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, null);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}\\subfolder\\{name}.mkv".AsOsAgnostic())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_get_relative_path_when_there_is_no_grandparent_windows()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = @"C:\";
|
||||
var localEpisode = _approvedDecisions.First().LocalEpisode;
|
||||
|
||||
localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name };
|
||||
localEpisode.Path = Path.Combine(outputPath, name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, null);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}.mkv".AsOsAgnostic())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_get_relative_path_when_there_is_no_grandparent_mono()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = "/";
|
||||
var localEpisode = _approvedDecisions.First().LocalEpisode;
|
||||
|
||||
localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name };
|
||||
localEpisode.Path = Path.Combine(outputPath, name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, null);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}.mkv".AsOsAgnostic())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_get_relative_path_when_there_is_no_grandparent_for_UNC_path()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = @"\\server\share";
|
||||
var localEpisode = _approvedDecisions.First().LocalEpisode;
|
||||
|
||||
localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name };
|
||||
localEpisode.Path = Path.Combine(outputPath, name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, null);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}.mkv")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_info_release_title_to_find_relative_path_when_file_is_not_in_download_client_item_output_directory()
|
||||
{
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name);
|
||||
var localEpisode = _approvedDecisions.First().LocalEpisode;
|
||||
|
||||
_downloadClientItem.OutputPath = new OsPath(Path.Combine(@"C:\Test\Unsorted\TV-Other\".AsOsAgnostic(), name));
|
||||
localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name };
|
||||
localEpisode.Path = Path.Combine(outputPath, "subfolder", name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}\\subfolder\\{name}.mkv".AsOsAgnostic())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_info_release_title_to_find_relative_path_when_download_client_item_has_an_empty_output_path()
|
||||
{
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name);
|
||||
var localEpisode = _approvedDecisions.First().LocalEpisode;
|
||||
|
||||
_downloadClientItem.OutputPath = new OsPath();
|
||||
localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name };
|
||||
localEpisode.Path = Path.Combine(outputPath, "subfolder", name + ".mkv");
|
||||
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.OriginalFilePath == $"{name}\\subfolder\\{name}.mkv".AsOsAgnostic())));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
[TestCase("V_MPEGH/ISO/HEVC", "source.title.x265.720p-Sonarr", "x265")]
|
||||
[TestCase("V_MPEGH/ISO/HEVC", "source.title.h265.720p-Sonarr", "h265")]
|
||||
[TestCase("MPEG-2 Video", null, "MPEG2")]
|
||||
public void should_format_video_codec_with_source_title(string videoCodec, string sceneName, string expectedFormat)
|
||||
public void should_format_video_codec_with_source_title_legacy(string videoCodec, string sceneName, string expectedFormat)
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
@@ -39,6 +39,11 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
[TestCase("VP7, VP70, General, ", "Sweet Seymour.avi", "VP7")]
|
||||
[TestCase("VP8, V_VP8, , ", "Dick.mkv", "VP8")]
|
||||
[TestCase("VP9, V_VP9, , ", "Roadkill Ep3x11 - YouTube.webm", "VP9")]
|
||||
[TestCase("x264, x264, , ", "Ghost Advent - S04E05 - Stanley Hotel SDTV.avi", "x264")]
|
||||
[TestCase("V_MPEGH/ISO/HEVC, V_MPEGH/ISO/HEVC, , ", "The BBT S11E12 The Matrimonial Metric 1080p 10bit AMZN WEB-DL", "h265")]
|
||||
[TestCase("MPEG-4 Visual, 20, Simple@L1, Lavc52.29.0", "Will.And.Grace.S08E14.WS.DVDrip.XviD.I.Love.L.Gay-Obfuscated", "XviD")]
|
||||
[TestCase("MPEG-4 Visual, 20, Advanced Simple@L5, XviD0046", "", "XviD")]
|
||||
[TestCase("mp4v, mp4v, , ", "American.Chopper.S06E07.Mountain.Creek.Bike.DSR.XviD-KRS", "XviD")]
|
||||
public void should_format_video_format(string videoFormatPack, string sceneName, string expectedFormat)
|
||||
{
|
||||
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||
@@ -53,6 +58,42 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
}
|
||||
|
||||
[TestCase("AVC, AVC, , x264", "Some.Video.S01E01.h264", "x264")] // Force mediainfo tag
|
||||
[TestCase("HEVC, HEVC, , x265", "Some.Video.S01E01.h265", "x265")] // Force mediainfo tag
|
||||
[TestCase("AVC, AVC, , ", "Some.Video.S01E01.x264", "x264")] // Not seen in practice, but honor tag if otherwise unknown
|
||||
[TestCase("HEVC, HEVC, , ", "Some.Video.S01E01.x265", "x265")] // Not seen in practice, but honor tag if otherwise unknown
|
||||
[TestCase("AVC, AVC, , ", "Some.Video.S01E01", "h264")] // Default value
|
||||
[TestCase("HEVC, HEVC, , ", "Some.Video.S01E01", "h265")] // Default value
|
||||
public void should_format_video_format_fallbacks(string videoFormatPack, string sceneName, string expectedFormat)
|
||||
{
|
||||
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
VideoFormat = split[0],
|
||||
VideoCodecID = split[1],
|
||||
VideoProfile = split[2],
|
||||
VideoCodecLibrary = split[3]
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
}
|
||||
|
||||
[TestCase("MPEG-4 Visual, 20, , Intel(R) MPEG-4 encoder based on Intel(R) IPP 6.1 build 137.20[6.1.137.763]", "", "")]
|
||||
public void should_warn_on_unknown_video_format(string videoFormatPack, string sceneName, string expectedFormat)
|
||||
{
|
||||
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
VideoFormat = split[0],
|
||||
VideoCodecID = split[1],
|
||||
VideoProfile = split[2],
|
||||
VideoCodecLibrary = split[3]
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_VideoFormat_by_default()
|
||||
{
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Manual;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Update.Commands;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Messaging.Commands
|
||||
{
|
||||
[TestFixture]
|
||||
public class CommandEqualityComparerFixture
|
||||
{
|
||||
private string GivenRandomPath()
|
||||
{
|
||||
return Path.Combine(@"C:\Tesst\", Guid.NewGuid().ToString()).AsOsAgnostic();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_there_are_no_properties()
|
||||
{
|
||||
@@ -107,5 +119,43 @@ namespace NzbDrone.Core.Test.Messaging.Commands
|
||||
|
||||
CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_commands_list_for_non_primitive_type_match()
|
||||
{
|
||||
var files1 = Builder<ManualImportFile>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(m => m.Path = GivenRandomPath())
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
var files2 = files1.JsonClone();
|
||||
|
||||
var command1 = new ManualImportCommand { Files = files1 };
|
||||
var command2 = new ManualImportCommand { Files = files2 };
|
||||
|
||||
CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_when_commands_list_for_non_primitive_type_dont_match()
|
||||
{
|
||||
var files1 = Builder<ManualImportFile>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(m => m.Path = GivenRandomPath())
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
var files2 = Builder<ManualImportFile>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(m => m.Path = GivenRandomPath())
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
var command1 = new ManualImportCommand { Files = files1 };
|
||||
var command2 = new ManualImportCommand { Files = files2 };
|
||||
|
||||
CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +137,9 @@ namespace NzbDrone.Core.Test.Messaging.Commands
|
||||
QueueAndWaitForExecution(commandModel);
|
||||
|
||||
VerifyEventPublished<CommandExecutedEvent>();
|
||||
|
||||
Thread.Sleep(10);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ namespace NzbDrone.Core.Test.MetadataSource.SkyHook
|
||||
[TestCase("tvdbid: 0")]
|
||||
[TestCase("tvdbid: -12")]
|
||||
[TestCase("tvdbid:289578")]
|
||||
[TestCase("adjalkwdjkalwdjklawjdlKAJD;EF")]
|
||||
[TestCase("adjalkwdjkalwdjklawjdlKAJD")]
|
||||
public void no_search_result(string term)
|
||||
{
|
||||
var result = Subject.SearchForNewSeries(term);
|
||||
result.Should().BeEmpty();
|
||||
|
||||
|
||||
ExceptionVerification.IgnoreWarns();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,14 +90,19 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@@ -148,6 +153,7 @@
|
||||
<Compile Include="Datastore\Migration\079_dedupe_tagsFixture.cs" />
|
||||
<Compile Include="Datastore\Migration\075_force_lib_updateFixture.cs" />
|
||||
<Compile Include="Datastore\Migration\090_update_kickass_urlFixture.cs" />
|
||||
<Compile Include="Datastore\Migration\121_update_animetosho_urlFixture.cs" />
|
||||
<Compile Include="Datastore\ObjectDatabaseFixture.cs" />
|
||||
<Compile Include="Datastore\PagingSpecExtensionsTests\PagingOffsetFixture.cs" />
|
||||
<Compile Include="Datastore\PagingSpecExtensionsTests\ToSortDirectionFixture.cs" />
|
||||
@@ -160,7 +166,8 @@
|
||||
<Compile Include="DecisionEngineTests\ProtocolSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\CutoffSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\HistorySpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\AlreadyImportedSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\RssSync\HistorySpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\MonitoredEpisodeSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\QueueSpecificationFixture.cs" />
|
||||
@@ -220,6 +227,7 @@
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Compile Include="Download\TrackedDownloads\TrackedDownloadServiceFixture.cs" />
|
||||
<Compile Include="Extras\Metadata\Consumers\Xbmc\FindMetadataFileFixture.cs" />
|
||||
<Compile Include="FluentTest.cs" />
|
||||
<Compile Include="Framework\CoreTest.cs" />
|
||||
<Compile Include="Framework\DbTest.cs" />
|
||||
@@ -235,6 +243,7 @@
|
||||
<Compile Include="HealthCheck\Checks\ImportMechanismCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\IndexerSearchCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\IndexerRssCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\MonoDebugFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\MonoVersionCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\IndexerStatusCheckFixture.cs" />
|
||||
<Compile Include="HealthCheck\Checks\RootFolderCheckFixture.cs" />
|
||||
@@ -271,6 +280,7 @@
|
||||
<Compile Include="IndexerTests\IntegrationTests\IndexerIntegrationTests.cs" />
|
||||
<Compile Include="IndexerTests\NewznabTests\NewznabCapabilitiesProviderFixture.cs" />
|
||||
<Compile Include="IndexerTests\RarbgTests\RarbgFixture.cs" />
|
||||
<Compile Include="IndexerTests\SeedConfigProviderFixture.cs" />
|
||||
<Compile Include="IndexerTests\TorrentRssIndexerTests\TorrentRssParserFactoryFixture.cs" />
|
||||
<Compile Include="IndexerTests\TorrentRssIndexerTests\TorrentRssSettingsDetectorFixture.cs" />
|
||||
<Compile Include="IndexerTests\TorznabTests\TorznabFixture.cs" />
|
||||
@@ -298,13 +308,15 @@
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesCommandServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateEpisodesFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateQualityFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromMediaInfoFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\DetectSampleFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\EpisodeTitleSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameFileSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\GrabbedReleaseQualityFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\MatchesFolderSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecificationFixture.cs" />
|
||||
@@ -327,10 +339,11 @@
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\MultiEpisodeFixture.cs" />
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\TitleTheFixture.cs" />
|
||||
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
|
||||
<Compile Include="ParserTests\ValidateParsedEpisodeInfoFixture.cs" />
|
||||
<Compile Include="Qualities\RevisionComparableFixture.cs" />
|
||||
<Compile Include="QueueTests\QueueServiceFixture.cs" />
|
||||
<Compile Include="RemotePathMappingsTests\RemotePathMappingServiceFixture.cs" />
|
||||
<Compile Include="OrganizerTests\CleanFixture.cs" />
|
||||
<Compile Include="OrganizerTests\CleanFilenameFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaFileServiceTests\FilterFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaFileTableCleanupServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoServiceFixture.cs" />
|
||||
@@ -340,8 +353,8 @@
|
||||
<Compile Include="Messaging\Commands\CommandEqualityComparerFixture.cs" />
|
||||
<Compile Include="Messaging\Commands\CommandExecutorFixture.cs" />
|
||||
<Compile Include="Messaging\Events\EventAggregatorFixture.cs" />
|
||||
<Compile Include="Metadata\Consumers\Roksbox\FindMetadataFileFixture.cs" />
|
||||
<Compile Include="Metadata\Consumers\Wdtv\FindMetadataFileFixture.cs" />
|
||||
<Compile Include="Extras\Metadata\Consumers\Roksbox\FindMetadataFileFixture.cs" />
|
||||
<Compile Include="Extras\Metadata\Consumers\Wdtv\FindMetadataFileFixture.cs" />
|
||||
<Compile Include="NotificationTests\PlexClientServiceTest.cs" />
|
||||
<Compile Include="NotificationTests\ProwlProviderTest.cs" />
|
||||
<Compile Include="NotificationTests\Xbmc\Http\ActivePlayersFixture.cs" />
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.OrganizerTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CleanFilenameFixture : CoreTest
|
||||
{
|
||||
[TestCase("Law & Order: Criminal Intent - S10E07 - Icarus [HDTV-720p]", "Law & Order- Criminal Intent - S10E07 - Icarus [HDTV-720p]")]
|
||||
public void should_replaace_invalid_characters(string name, string expectedName)
|
||||
{
|
||||
FileNameBuilder.CleanFileName(name).Should().Be(expectedName);
|
||||
}
|
||||
|
||||
[TestCase(".hack s01e01", "hack s01e01")]
|
||||
public void should_remove_periods_from_start(string name, string expectedName)
|
||||
{
|
||||
FileNameBuilder.CleanFileName(name).Should().Be(expectedName);
|
||||
}
|
||||
|
||||
[TestCase(" Series Title - S01E01 - Episode Title", "Series Title - S01E01 - Episode Title")]
|
||||
[TestCase("Series Title - S01E01 - Episode Title ", "Series Title - S01E01 - Episode Title")]
|
||||
public void should_remove_spaces_from_start_and_end(string name, string expectedName)
|
||||
{
|
||||
FileNameBuilder.CleanFileName(name).Should().Be(expectedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.OrganizerTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CleanFixture : CoreTest
|
||||
{
|
||||
[TestCase("Law & Order: Criminal Intent - S10E07 - Icarus [HDTV-720p]",
|
||||
"Law & Order- Criminal Intent - S10E07 - Icarus [HDTV-720p]")]
|
||||
public void CleanFileName(string name, string expectedName)
|
||||
{
|
||||
FileNameBuilder.CleanFileName(name).Should().Be(expectedName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,15 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("[Cthuyuu] Taimadou Gakuen 35 Shiken Shoutai - 03 [720p H264 AAC][8AD82C3A]", "Taimadou Gakuen 35 Shiken Shoutai", 3, 0, 0)]
|
||||
[TestCase("Dragon Ball Super Episode 56 [VOSTFR V2][720p][AAC]-Mystic Z-Team", "Dragon Ball Super", 56, 0, 0)]
|
||||
[TestCase("[Mystic Z-Team] Dragon Ball Super Episode 69 [VOSTFR_Finale][1080p][AAC].mp4", "Dragon Ball Super", 69, 0, 0)]
|
||||
[TestCase("[Shark-Raws] Crayon Shin-chan #957 (NBN 1280x720 x264 AAC).mp4", "Crayon Shin-chan", 957, 0, 0)]
|
||||
[TestCase("Love Rerun EP06 720p x265 AOZ.mp4", "Love Rerun", 6, 0, 0)]
|
||||
[TestCase("Love Rerun 2018 EP06 720p x265 AOZ.mp4", "Love Rerun 2018", 6, 0, 0)]
|
||||
[TestCase("Love Rerun 2018 06 720p x265 AOZ.mp4", "Love Rerun 2018", 6, 0, 0)]
|
||||
[TestCase("Boku No Hero Academia S03 - EP14 VOSTFR [1080p] [HardSub] Yass'Kun", "Boku No Hero Academia S03", 14, 0, 0)]
|
||||
[TestCase("Boku No Hero Academia S3 - 15 VOSTFR [720p]", "Boku No Hero Academia S3", 15, 0, 0)]
|
||||
[TestCase("Tokyo Ghoul: RE S2 - Episode 4 VOSTFR (1080p)", "Tokyo Ghoul RE S2", 4, 0, 0)]
|
||||
[TestCase("To Aru Majutsu no Index III - Episode 5 VOSTFR (1080p)", "To Aru Majutsu no Index III", 5, 0, 0)]
|
||||
[TestCase("[Prout] Steins;Gate 0 - Episode 5 VOSTFR (BDRip 1920x1080 x264 FLAC)", "Steins;Gate 0", 5, 0, 0)]
|
||||
//[TestCase("", "", 0, 0, 0)]
|
||||
public void should_parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
@@ -127,5 +136,29 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.FullSeason.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("[Vivid] Living Sky Saga S01 [Web][MKV][h264 10-bit][1080p][AAC 2.0]", "Living Sky Saga", 1)]
|
||||
public void should_parse_anime_season_packs(string postTitle, string title, int seasonNumber)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
result.Should().NotBeNull();
|
||||
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.FullSeason.Should().BeTrue();
|
||||
result.SeasonNumber.Should().Be(seasonNumber);
|
||||
}
|
||||
|
||||
[TestCase("[HorribleSubs] Goblin Slayer - 10.5 [1080p].mkv", "Goblin Slayer", 10.5)]
|
||||
public void should_handle_anime_recap_numbering(string postTitle, string title, double specialEpisodeNumber)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
result.Should().NotBeNull();
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
|
||||
result.SpecialAbsoluteEpisodeNumbers.Should().NotBeEmpty();
|
||||
result.SpecialAbsoluteEpisodeNumbers.Should().BeEquivalentTo(new[] { (decimal)specialEpisodeNumber });
|
||||
result.FullSeason.Should().BeFalse();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("At_Midnight_140722_720p_HDTV_x264-YesTV", "At Midnight", 2014, 07, 22)]
|
||||
//[TestCase("Corrie.07.01.15", "Corrie", 2015, 1, 7)]
|
||||
[TestCase("The Nightly Show with Larry Wilmore 2015 02 09 WEBRIP s01e13", "The Nightly Show with Larry Wilmore", 2015, 2, 9)]
|
||||
[TestCase("Jimmy_Fallon_2018_06_22_Seth_Meyers_720p_HEVC_x265-MeGusta", "Jimmy Fallon", 2018, 6, 22)]
|
||||
[TestCase("20161024- Exotic Payback.21x41_720.mkv", "", 2016, 10, 24)]
|
||||
[TestCase("2018-11-14.1080.all.mp4", "", 2018, 11, 14)]
|
||||
//[TestCase("", "", 0, 0, 0)]
|
||||
public void should_parse_daily_episode(string postTitle, string title, int year, int month, int day)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentAssertions;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@@ -80,6 +80,20 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
"The Good Wife",
|
||||
Quality.HDTV720p,
|
||||
"NZBgeek"
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
@"C:\Test\Fargo.S03E04.1080p.WEB-DL.DD5.1.H264-RARBG\170424_26.mkv".AsOsAgnostic(),
|
||||
"Fargo",
|
||||
Quality.WEBDL1080p,
|
||||
"RARBG"
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
@"C:\Test\XxQVHK4GJMP3n2dLpmhW\XxQVHK4GJMP3n2dLpmhW\MKV\010E70S.yhcranA.fo.snoS.mkv".AsOsAgnostic(),
|
||||
"Sons of Anarchy",
|
||||
Quality.HDTV720p,
|
||||
null
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Mad.Men.S05E01-02.720p.5.1Ch.BluRay", "Mad Men", 5, new[] { 1, 2 })]
|
||||
[TestCase("S01E01-E03 - Episode Title.HDTV-720p", "", 1, new [] { 1, 2, 3 })]
|
||||
[TestCase("1x01-x03 - Episode Title.HDTV-720p", "", 1, new [] { 1, 2, 3 })]
|
||||
[TestCase("Are.You.Human.Too.E07-E08.180612.1080p-NEXT", "Are You Human Too", 1, new[] { 7, 8 })]
|
||||
[TestCase("Are You Human Too? E11-E12 1080p HDTV AAC H.264-NEXT", "Are You Human Too", 1, new[] { 11, 12 })]
|
||||
[TestCase("The Series Title (2010) - [S01E01-02-03] - Episode Title", "The Series Title (2010)", 1, new [] { 1, 2, 3 })]
|
||||
//[TestCase("", "", , new [] { })]
|
||||
public void should_parse_multiple_episodes(string postTitle, string title, int season, int[] episodes)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Match of the Day", "matchday")]
|
||||
[TestCase("Match of the Day 2", "matchday2")]
|
||||
[TestCase("[ www.Torrenting.com ] - Revenge.S03E14.720p.HDTV.X264-DIMENSION", "Revenge")]
|
||||
[TestCase("www.Torrenting.com - Revenge.S03E14.720p.HDTV.X264-DIMENSION", "Revenge")]
|
||||
[TestCase("Seed S02E09 HDTV x264-2HD [eztv]-[rarbg.com]", "Seed")]
|
||||
[TestCase("Reno.911.S01.DVDRip.DD2.0.x264-DEEP", "Reno 911")]
|
||||
public void should_parse_series_name(string postTitle, string title)
|
||||
@@ -70,7 +71,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
public void should_parse_quality_from_extension(string title)
|
||||
{
|
||||
Parser.Parser.ParseTitle(title).Quality.Quality.Should().NotBe(Quality.Unknown);
|
||||
Parser.Parser.ParseTitle(title).Quality.QualitySource.Should().Be(QualitySource.Extension);
|
||||
Parser.Parser.ParseTitle(title).Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Extension);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
@@ -117,6 +118,10 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
{
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Setup(s => s.FindEpisodesBySceneNumbering(It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.Map(_parsedEpisodeInfo, _series.TvdbId, _series.TvRageId, _singleEpisodeSearchCriteria);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
@@ -253,7 +258,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
public void should_find_episode_by_season_and_absolute_episode_number_when_scene_absolute_episode_number_returns_multiple_results(int seasonNumber)
|
||||
public void should_return_episodes_when_scene_absolute_episode_number_returns_multiple_results(int seasonNumber)
|
||||
{
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
@@ -265,6 +270,32 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
.Setup(s => s.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()))
|
||||
.Returns(Builder<Episode>.CreateListOfSize(5).Build().ToList());
|
||||
|
||||
var result = Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
|
||||
|
||||
result.Should().HaveCount(5);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Once());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Never());
|
||||
}
|
||||
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
public void should_find_episode_by_season_and_absolute_episode_number_when_scene_absolute_episode_number_returns_no_results(int seasonNumber)
|
||||
{
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
|
||||
.Returns(seasonNumber);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Setup(s => s.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Hells.Kitchen.US.S12E17.HR.WS.PDTV.X264-DIMENSION", false)]
|
||||
[TestCase("Survivorman.The.Lost.Pilots.Summer.HR.WS.PDTV.x264-DHD", false)]
|
||||
[TestCase("Victoria S01E07 - Motor zmen (CZ)[TvRip][HEVC][720p]", false)]
|
||||
[TestCase("flashpoint.S05E06.720p.HDTV.x264-FHD", false)]
|
||||
public void should_parse_hdtv720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.HDTV720p, proper);
|
||||
@@ -130,11 +131,27 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Dexter - S01E01 - Title [HDTV-1080p]", false)]
|
||||
[TestCase("[HorribleSubs] Yowamushi Pedal - 32 [1080p]", false)]
|
||||
[TestCase("Victoria S01E07 - Motor zmen (CZ)[TvRip][HEVC][1080p]", false)]
|
||||
[TestCase("Sword Art Online Alicization 04 vostfr FHD", false)]
|
||||
[TestCase("Goblin Slayer 04 vostfr FHD.mkv", false)]
|
||||
[TestCase("[Onii-ChanSub] SSSS.Gridman - 02 vostfr (FHD 1080p 10bits).mkv", false)]
|
||||
[TestCase("[Miaou] Akanesasu Shoujo 02 VOSTFR FHD 10 bits", false)]
|
||||
[TestCase("[mhastream.com]_Episode_05_FHD.mp4", false)]
|
||||
[TestCase("[Kousei]_One_Piece_ - _609_[FHD][648A87C7].mp4", false)]
|
||||
[TestCase("Presunto culpable 1x02 Culpabilidad [HDTV 1080i AVC MP2 2.0 Sub][GrupoHDS]", false)]
|
||||
[TestCase("Cuéntame cómo pasó - 19x15 [344] Cuarenta años de baile [HDTV 1080i AVC MP2 2.0 Sub][GrupoHDS]", false)]
|
||||
public void should_parse_hdtv1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.HDTV1080p, proper);
|
||||
}
|
||||
|
||||
[TestCase("My Title - S01E01 - EpTitle [HEVC 4k DTSHD-MA-6ch]", false)]
|
||||
[TestCase("My Title - S01E01 - EpTitle [HEVC-4k DTSHD-MA-6ch]", false)]
|
||||
[TestCase("My Title - S01E01 - EpTitle [4k HEVC DTSHD-MA-6ch]", false)]
|
||||
public void should_parse_hdtv2160p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.HDTV2160p, proper);
|
||||
}
|
||||
|
||||
[TestCase("Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false)]
|
||||
[TestCase("Vanguard S01E04 Mexicos Death Train 720p WEB DL", false)]
|
||||
[TestCase("Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false)]
|
||||
@@ -177,6 +194,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("The.Simpsons.S25E21.Pay.Pal.1080p.WEB-DL.DD5.1.H.264-NTb", false)]
|
||||
[TestCase("Incorporated.S01E08.Das.geloeschte.Ich.German.DD51.Dubbed.DL.1080p.AmazonHD.x264-TVS", false)]
|
||||
[TestCase("Death.Note.2017.German.DD51.DL.1080p.NetflixHD.x264-TVS", false)]
|
||||
[TestCase("Played.S01E08.Pro.Gamer.1440p.BKPL.WEB-DL.H.264-LiGHT", false)]
|
||||
[TestCase("Good.Luck.Charlie.S04E11.Teddy's.Choice.FHD.1080p.Web-DL", false)]
|
||||
[TestCase("Outlander.S04E03.The.False.Bride.1080p.NF.WEB.DDP5.1.x264-NTb[rartv]", false)]
|
||||
public void should_parse_webdl1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.WEBDL1080p, proper);
|
||||
@@ -209,6 +229,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("[Elysium]Lucky.Star.01(BD.720p.AAC.DA)[0BB96AD8].mkv", false)]
|
||||
[TestCase("Battlestar.Galactica.S01E01.33.720p.HDDVD.x264-SiNNERS.mkv", false)]
|
||||
[TestCase("The.Expanse.S01E07.RERIP.720p.BluRay.x264-DEMAND", true)]
|
||||
[TestCase("Sans.Laisser.De.Traces.FRENCH.720p.BluRay.x264-FHD", false)]
|
||||
public void should_parse_bluray720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.Bluray720p, proper);
|
||||
@@ -223,6 +244,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("[Zurako] Log Horizon - 01 - The Apocalypse (BD 1080p AAC) [7AE12174].mkv", false)]
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.1080p.Blu-ray.AC3.-HELLYWOOD.avi", false)]
|
||||
[TestCase("[Coalgirls]_Durarara!!_01_(1920x1080_Blu-ray_FLAC)_[8370CB8F].mkv", false)]
|
||||
[TestCase("Planet.Earth.S01E11.Ocean.Deep.1080p.HD-DVD.DD.VC1-TRB", false)]
|
||||
[TestCase("Spirited Away(2001) Bluray FHD Hi10P.mkv", false)]
|
||||
public void should_parse_bluray1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.Bluray1080p, proper);
|
||||
@@ -230,6 +253,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
|
||||
[TestCase("House.of.Cards.US.s05e13.4K.UHD.Bluray", false)]
|
||||
[TestCase("House.of.Cards.US.s05e13.UHD.4K.Bluray", false)]
|
||||
[TestCase("[DameDesuYo] Backlog Bundle - Part 1 (BD 4K 8bit FLAC)", false)]
|
||||
public void should_parse_bluray2160p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.Bluray2160p, proper);
|
||||
@@ -283,7 +307,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA")]
|
||||
public void should_parse_quality_from_name(string title)
|
||||
{
|
||||
QualityParser.ParseQuality(title).QualitySource.Should().Be(QualitySource.Name);
|
||||
QualityParser.ParseQuality(title).QualityDetectionSource.Should().Be(QualityDetectionSource.Name);
|
||||
}
|
||||
|
||||
[TestCase("Revolution.S01E02.Chained.Heat.mkv")]
|
||||
@@ -292,7 +316,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("[CR] Sailor Moon - 004 [48CE2D0F].avi")]
|
||||
public void should_parse_quality_from_extension(string title)
|
||||
{
|
||||
QualityParser.ParseQuality(title).QualitySource.Should().Be(QualitySource.Extension);
|
||||
QualityParser.ParseQuality(title).QualityDetectionSource.Should().Be(QualityDetectionSource.Extension);
|
||||
}
|
||||
|
||||
private void ParseAndVerifyQuality(string title, Quality quality, bool proper)
|
||||
|
||||
@@ -130,6 +130,10 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Shortland.Street.S26E022.HDTV.x264-FiHTV", "Shortland Street", 26, 22)]
|
||||
[TestCase("Super.Potatoes.S01.Ep06.1080p.BluRay.DTS.x264-MiR", "Super Potatoes", 1, 6)]
|
||||
[TestCase("Room 104 - S01E07 The Missionaries [SDTV]", "Room 104", 1, 7)]
|
||||
[TestCase("11-02 The Retraction Reaction (HD).m4v", "", 11, 2)]
|
||||
[TestCase("Plus belle la vie - S14E3533 FRENCH WEBRIP H.264 AAC (09.05.2018)", "Plus belle la vie", 14, 3533)]
|
||||
[TestCase("The 100 - S01E02 - Earth Skills HDTV-1080p AVC DTS [EN+FR+ES+PT+DA+FI+NB+SV]", "The 100", 1, 2)]
|
||||
[TestCase("Series Title - S01E01 - Day 100 [SDTV]", "Series Title", 1, 1)]
|
||||
//[TestCase("", "", 0, 0)]
|
||||
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ValidateParsedEpisodeInfoFixture : CoreTest
|
||||
{
|
||||
private ParsedEpisodeInfo _parsedEpisodeInfo;
|
||||
private Series _series;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_parsedEpisodeInfo = Builder<ParsedEpisodeInfo>.CreateNew()
|
||||
.With(p => p.AirDate = null)
|
||||
.Build();
|
||||
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void GivenDailyParsedEpisodeInfo()
|
||||
{
|
||||
_parsedEpisodeInfo.AirDate = "2018-05-21";
|
||||
}
|
||||
|
||||
private void GivenDailySeries()
|
||||
{
|
||||
_series.SeriesType = SeriesTypes.Daily;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_episode_info_is_not_daily()
|
||||
{
|
||||
ValidateParsedEpisodeInfo.ValidateForSeriesType(_parsedEpisodeInfo, _series).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_episode_info_is_daily_for_daily_series()
|
||||
{
|
||||
GivenDailyParsedEpisodeInfo();
|
||||
GivenDailySeries();
|
||||
|
||||
ValidateParsedEpisodeInfo.ValidateForSeriesType(_parsedEpisodeInfo, _series).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_episode_info_is_daily_for_standard_series()
|
||||
{
|
||||
GivenDailyParsedEpisodeInfo();
|
||||
|
||||
ValidateParsedEpisodeInfo.ValidateForSeriesType(_parsedEpisodeInfo, _series).Should().BeFalse();
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_log_warning_if_warnIfInvalid_is_false()
|
||||
{
|
||||
GivenDailyParsedEpisodeInfo();
|
||||
|
||||
ValidateParsedEpisodeInfo.ValidateForSeriesType(_parsedEpisodeInfo, _series, false);
|
||||
ExceptionVerification.ExpectedWarns(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NLog;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
@@ -25,8 +26,8 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
|
||||
|
||||
public class MockProviderStatusService : ProviderStatusServiceBase<IMockProvider, MockProviderStatus>
|
||||
{
|
||||
public MockProviderStatusService(IMockProviderStatusRepository providerStatusRepository, IEventAggregator eventAggregator, Logger logger)
|
||||
: base(providerStatusRepository, eventAggregator, logger)
|
||||
public MockProviderStatusService(IMockProviderStatusRepository providerStatusRepository, IEventAggregator eventAggregator, IRuntimeInfo runtimeInfo, Logger logger)
|
||||
: base(providerStatusRepository, eventAggregator, runtimeInfo, logger)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -40,9 +41,20 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
|
||||
public void SetUp()
|
||||
{
|
||||
_epoch = DateTime.UtcNow;
|
||||
|
||||
Mocker.GetMock<IRuntimeInfo>()
|
||||
.SetupGet(v => v.StartTime)
|
||||
.Returns(_epoch - TimeSpan.FromHours(1));
|
||||
}
|
||||
|
||||
private void WithStatus(MockProviderStatus status)
|
||||
private void GivenRecentStartup()
|
||||
{
|
||||
Mocker.GetMock<IRuntimeInfo>()
|
||||
.SetupGet(v => v.StartTime)
|
||||
.Returns(_epoch - TimeSpan.FromMinutes(12));
|
||||
}
|
||||
|
||||
private MockProviderStatus WithStatus(MockProviderStatus status)
|
||||
{
|
||||
Mocker.GetMock<IMockProviderStatusRepository>()
|
||||
.Setup(v => v.FindByProviderId(1))
|
||||
@@ -51,6 +63,8 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
|
||||
Mocker.GetMock<IMockProviderStatusRepository>()
|
||||
.Setup(v => v.All())
|
||||
.Returns(new[] { status });
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private void VerifyUpdate()
|
||||
@@ -122,5 +136,32 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
|
||||
status.DisabledTill.Should().HaveValue();
|
||||
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(15), 500);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_escalate_further_till_after_5_minutes_since_startup()
|
||||
{
|
||||
GivenRecentStartup();
|
||||
|
||||
var origStatus = WithStatus(new MockProviderStatus
|
||||
{
|
||||
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
|
||||
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
|
||||
EscalationLevel = 3
|
||||
});
|
||||
|
||||
Subject.RecordFailure(1);
|
||||
Subject.RecordFailure(1);
|
||||
Subject.RecordFailure(1);
|
||||
Subject.RecordFailure(1);
|
||||
Subject.RecordFailure(1);
|
||||
Subject.RecordFailure(1);
|
||||
Subject.RecordFailure(1);
|
||||
|
||||
var status = Subject.GetBlockedProviders().FirstOrDefault();
|
||||
status.Should().NotBeNull();
|
||||
|
||||
origStatus.EscalationLevel.Should().Be(3);
|
||||
status.DisabledTill.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MetadataSource.SkyHook;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests
|
||||
{
|
||||
@@ -77,12 +78,14 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetSeries(), GetEpisodes());
|
||||
|
||||
_insertedEpisodes.Should().HaveSameCount(GetEpisodes());
|
||||
_updatedEpisodes.Should().BeEmpty();
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -146,6 +149,63 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
_updatedEpisodes.Should().OnlyContain(e => e.Monitored == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_set_monitored_status_for_old_episodes_to_false_if_recent_enough()
|
||||
{
|
||||
var series = GetSeries();
|
||||
series.Seasons = new List<Season>();
|
||||
series.Seasons.Add(new Season { SeasonNumber = 1, Monitored = true });
|
||||
|
||||
var episodes = GetEpisodes().OrderBy(v => v.SeasonNumber).ThenBy(v => v.EpisodeNumber).Take(5).ToList();
|
||||
|
||||
episodes[1].AirDateUtc = DateTime.UtcNow.AddDays(-15);
|
||||
episodes[2].AirDateUtc = DateTime.UtcNow.AddDays(-10);
|
||||
episodes[3].AirDateUtc = DateTime.UtcNow.AddDays(1);
|
||||
|
||||
var existingEpisodes = episodes.Skip(4).ToList();
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(existingEpisodes);
|
||||
|
||||
Subject.RefreshEpisodeInfo(series, episodes);
|
||||
|
||||
_insertedEpisodes = _insertedEpisodes.OrderBy(v => v.EpisodeNumber).ToList();
|
||||
|
||||
_insertedEpisodes.Should().HaveCount(4);
|
||||
_insertedEpisodes[0].Monitored.Should().Be(true);
|
||||
_insertedEpisodes[1].Monitored.Should().Be(true);
|
||||
_insertedEpisodes[2].Monitored.Should().Be(true);
|
||||
_insertedEpisodes[3].Monitored.Should().Be(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_monitored_status_for_old_episodes_to_false_if_no_episodes_existed()
|
||||
{
|
||||
var series = GetSeries();
|
||||
series.Seasons = new List<Season>();
|
||||
|
||||
var episodes = GetEpisodes().OrderBy(v => v.SeasonNumber).ThenBy(v => v.EpisodeNumber).Take(4).ToList();
|
||||
|
||||
episodes[1].AirDateUtc = DateTime.UtcNow.AddDays(-15);
|
||||
episodes[2].AirDateUtc = DateTime.UtcNow.AddDays(-10);
|
||||
episodes[3].AirDateUtc = DateTime.UtcNow.AddDays(1);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.RefreshEpisodeInfo(series, episodes);
|
||||
|
||||
_insertedEpisodes = _insertedEpisodes.OrderBy(v => v.EpisodeNumber).ToList();
|
||||
|
||||
_insertedEpisodes.Should().HaveSameCount(episodes);
|
||||
_insertedEpisodes[0].Monitored.Should().Be(false);
|
||||
_insertedEpisodes[1].Monitored.Should().Be(false);
|
||||
_insertedEpisodes[2].Monitored.Should().Be(false);
|
||||
_insertedEpisodes[3].Monitored.Should().Be(true);
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_duplicate_remote_episodes_before_processing()
|
||||
{
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
@@ -180,5 +183,35 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Seasons.Count == 2), It.IsAny<bool>()));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_rescan_series_if_updating_fails()
|
||||
{
|
||||
Mocker.GetMock<IProvideSeriesInfo>()
|
||||
.Setup(s => s.GetSeriesInfo(_series.Id))
|
||||
.Throws(new IOException());
|
||||
|
||||
Assert.Throws<IOException>(() => Subject.Execute(new RefreshSeriesCommand(_series.Id)));
|
||||
|
||||
Mocker.GetMock<IDiskScanService>()
|
||||
.Verify(v => v.Scan(_series), Times.Once());
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_rescan_series_if_updating_fails_with_series_not_found()
|
||||
{
|
||||
Mocker.GetMock<IProvideSeriesInfo>()
|
||||
.Setup(s => s.GetSeriesInfo(_series.Id))
|
||||
.Throws(new SeriesNotFoundException(_series.Id));
|
||||
|
||||
Subject.Execute(new RefreshSeriesCommand(_series.Id));
|
||||
|
||||
Mocker.GetMock<IDiskScanService>()
|
||||
.Verify(v => v.Scan(_series), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentAssertions;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
@@ -8,7 +8,9 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
public class SeriesTitleNormalizerFixture
|
||||
{
|
||||
[TestCase("A to Z", 281588, "a to z")]
|
||||
[TestCase("A. D. - The Trials & Triumph of the Early Church", 266757, "ad trials triumph early church")]
|
||||
[TestCase("A.D. The Bible Continues", 289260, "ad bible continues")]
|
||||
[TestCase("A.P. Bio", 328534, "ap bio")]
|
||||
[TestCase("The A-Team", 77904, "ateam")]
|
||||
public void should_use_precomputed_title(string title, int tvdbId, string expected)
|
||||
{
|
||||
SeriesTitleNormalizer.Normalize(title, tvdbId).Should().Be(expected);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<package id="Moq" version="4.0.10827" targetFramework="net40" />
|
||||
<package id="NBuilder" version="4.0.0" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
<package id="Prowlin" version="0.9.4456.26422" targetFramework="net40" />
|
||||
<package id="Unity" version="2.1.505.2" targetFramework="net40" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user