1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-03-16 16:04:33 -04:00

Compare commits

..

7 Commits

Author SHA1 Message Date
Taloth Saldono
942dddb5e4 Removed old CDH Migration checker. 2017-12-18 21:34:13 +01:00
Taloth Saldono
2f02f0a92d CustomTransfer mimicking MountTransfer.cs 2017-12-16 12:38:18 +01:00
Taloth Saldono
5c1491ca06 Removed RemotePathMapping infrastructure. OutputPath on items is now DownloadClientPath. 2017-12-16 09:55:17 +01:00
Taloth Saldono
e089bfb4d2 Added a bit more implementation for the Default TransferProvider. 2017-12-14 21:47:43 +01:00
Taloth Saldono
2c8489de43 Progress reporting infrastructure. 2017-12-14 21:47:42 +01:00
Taloth Saldono
74fbc97835 IVirtualDiskProvider abstraction, need a lot of work on the class names. 2017-12-13 21:34:32 +01:00
Taloth Saldono
2a0a4cfb6d Mockups of Transfer Providers. 2017-12-13 21:34:32 +01:00
386 changed files with 2556 additions and 7528 deletions

View File

@@ -3,7 +3,6 @@ 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

View File

@@ -1,28 +0,0 @@
---
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.

View File

@@ -1,14 +0,0 @@
---
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.

View File

@@ -1,7 +0,0 @@
---
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.

1
.gitignore vendored
View File

@@ -130,7 +130,6 @@ output/*
#OS X metadata files
._*
.DS_Store
_start
_temp_*/**/*

View File

@@ -52,6 +52,15 @@ 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
@@ -82,6 +91,8 @@ Build()
CleanFolder $outputFolder false
AddJsonNet
echo "Removing Mono.Posix.dll"
rm $outputFolder/Mono.Posix.dll

View File

@@ -9,11 +9,7 @@ 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"

View File

@@ -52,14 +52,10 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.4.12\lib\net40\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" />

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.5.3" targetFramework="net40" />
<package id="NLog" version="4.4.12" targetFramework="net40" />
</packages>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

View File

@@ -21,33 +21,20 @@ 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
{
[FieldDefinition(0, Label = "First Name", HelpText = "Your First Name")]
@@ -58,13 +45,4 @@ 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; }
}
}
}

View File

@@ -7,17 +7,11 @@ 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();
}
}
}
}

View File

@@ -1,15 +0,0 @@
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; }
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json.Linq;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions;
@@ -12,22 +11,45 @@ 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 mappings = GetFieldMappings(model.GetType());
var properties = model.GetType().GetSimpleProperties();
var result = new List<Field>(mappings.Length);
var result = new List<Field>(properties.Count);
foreach (var mapping in mappings)
foreach (var propertyInfo in properties)
{
var field = mapping.Field.Clone();
field.Value = mapping.GetterFunc(model);
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
result.Add(field);
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);
}
}
return result.OrderBy(r => r.Order).ToList();
@@ -37,16 +59,81 @@ namespace NzbDrone.Api.ClientSchema
{
Ensure.That(targetType, () => targetType).IsNotNull();
var mappings = GetFieldMappings(targetType);
var properties = targetType.GetSimpleProperties();
var target = Activator.CreateInstance(targetType);
foreach (var mapping in mappings)
foreach (var propertyInfo in properties)
{
var propertyType = mapping.PropertyType;
var field = fields.Find(f => f.Name == mapping.Field.Name);
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
mapping.SetterFunc(target, field.Value);
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);
}
}
}
return target;
@@ -58,84 +145,6 @@ 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)
@@ -143,73 +152,5 @@ 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;
}
}
}
}

View File

@@ -4,7 +4,6 @@ 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;
@@ -18,8 +17,6 @@ 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,
@@ -34,10 +31,6 @@ 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)
@@ -66,26 +59,8 @@ namespace NzbDrone.Api.Commands
{
if (message.Command.Body.SendUpdatesToClient)
{
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);
}
BroadcastResourceChange(ModelAction.Updated, message.Command.ToResource());
}
}
}
}
}

View File

@@ -13,9 +13,6 @@ namespace NzbDrone.Api.Config
SharedValidator.RuleFor(c => c.MinimumAge)
.GreaterThanOrEqualTo(0);
SharedValidator.RuleFor(c => c.MaximumSize)
.GreaterThanOrEqualTo(0);
SharedValidator.RuleFor(c => c.Retention)
.GreaterThanOrEqualTo(0);
@@ -28,4 +25,4 @@ namespace NzbDrone.Api.Config
return IndexerConfigResourceMapper.ToResource(model);
}
}
}
}

View File

@@ -6,7 +6,6 @@ namespace NzbDrone.Api.Config
public class IndexerConfigResource : RestResource
{
public int MinimumAge { get; set; }
public int MaximumSize { get; set; }
public int Retention { get; set; }
public int RssSyncInterval { get; set; }
}
@@ -18,7 +17,6 @@ namespace NzbDrone.Api.Config
return new IndexerConfigResource
{
MinimumAge = model.MinimumAge,
MaximumSize = model.MaximumSize,
Retention = model.Retention,
RssSyncInterval = model.RssSyncInterval,
};

View File

@@ -1,4 +1,4 @@
using NzbDrone.Api.REST;
using NzbDrone.Api.REST;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles;
@@ -10,7 +10,6 @@ namespace NzbDrone.Api.Config
public string RecycleBin { get; set; }
public bool AutoDownloadPropers { get; set; }
public bool CreateEmptySeriesFolders { get; set; }
public bool DeleteEmptyFolders { get; set; }
public FileDateType FileDate { get; set; }
public bool SetPermissionsLinux { get; set; }
@@ -36,7 +35,6 @@ namespace NzbDrone.Api.Config
RecycleBin = model.RecycleBin,
AutoDownloadPropers = model.AutoDownloadPropers,
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
DeleteEmptyFolders = model.DeleteEmptyFolders,
FileDate = model.FileDate,
SetPermissionsLinux = model.SetPermissionsLinux,

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
@@ -15,7 +15,6 @@ namespace NzbDrone.Api.EpisodeFiles
public DateTime DateAdded { get; set; }
public string SceneName { get; set; }
public QualityModel Quality { get; set; }
public MediaInfoResource MediaInfo { get; set; }
public bool QualityCutoffNotMet { get; set; }
}
@@ -38,7 +37,6 @@ namespace NzbDrone.Api.EpisodeFiles
DateAdded = model.DateAdded,
SceneName = model.SceneName,
Quality = model.Quality,
MediaInfo = model.MediaInfo.ToResource(model.SceneName)
//QualityCutoffNotMet
};
}
@@ -59,8 +57,7 @@ namespace NzbDrone.Api.EpisodeFiles
DateAdded = model.DateAdded,
SceneName = model.SceneName,
Quality = model.Quality,
QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, model.Quality),
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, model.Quality)
};
}
}

View File

@@ -1,30 +0,0 @@
using NzbDrone.Api.REST;
using NzbDrone.Core.MediaFiles.MediaInfo;
namespace NzbDrone.Api.EpisodeFiles
{
public class MediaInfoResource : RestResource
{
public decimal AudioChannels { get; set; }
public string AudioCodec { get; set; }
public string VideoCodec { get; set; }
}
public static class MediaInfoResourceMapper
{
public static MediaInfoResource ToResource(this MediaInfoModel model, string sceneName)
{
if (model == null)
{
return null;
}
return new MediaInfoResource
{
AudioChannels = MediaInfoFormatter.FormatAudioChannels(model),
AudioCodec = MediaInfoFormatter.FormatAudioCodec(model, sceneName),
VideoCodec = MediaInfoFormatter.FormatVideoCodec(model, sceneName)
};
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Nancy;
namespace NzbDrone.Api.Extensions
@@ -42,17 +42,5 @@ namespace NzbDrone.Api.Extensions
return request.Path.StartsWith("/MediaCover/", StringComparison.InvariantCultureIgnoreCase) ||
request.Path.StartsWith("/Content/Images/", StringComparison.InvariantCultureIgnoreCase);
}
public static bool GetBooleanQueryParameter(this Request request, string parameter, bool defaultValue = false)
{
var parameterValue = request.Query[parameter];
if (parameterValue.HasValue)
{
return bool.Parse(parameterValue.Value);
}
return defaultValue;
}
}
}

View File

@@ -1,16 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using Nancy;
using Nancy;
using Nancy.ModelBinding;
using NLog;
using NzbDrone.Api.Extensions;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore;
using FluentValidation;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Api.Extensions;
using NLog;
namespace NzbDrone.Api.Indexers
{
@@ -18,17 +15,14 @@ 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>());
@@ -47,47 +41,10 @@ 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);
}
}
}
}

View File

@@ -13,7 +13,6 @@ 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; }
@@ -37,7 +36,6 @@ namespace NzbDrone.Api.ManualImport
Path = model.Path,
RelativePath = model.RelativePath,
FolderName = model.FolderName,
Name = model.Name,
Size = model.Size,
Series = model.Series.ToResource(),

View File

@@ -70,13 +70,12 @@
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.4.12\lib\net40\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" />
@@ -84,10 +83,6 @@
<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">
@@ -104,13 +99,11 @@
<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" />
<Compile Include="Commands\CommandModule.cs" />
<Compile Include="Commands\CommandResource.cs" />
<Compile Include="EpisodeFiles\MediaInfoResource.cs" />
<Compile Include="Extensions\AccessControlHeaders.cs" />
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />

View File

@@ -210,12 +210,7 @@ namespace NzbDrone.Api
protected void VerifyValidationResult(ValidationResult validationResult, bool includeWarnings)
{
var result = validationResult as NzbDroneValidationResult;
if (result == null)
{
result = new NzbDroneValidationResult(validationResult.Errors);
}
var result = new NzbDroneValidationResult(validationResult.Errors);
if (includeWarnings && (!result.IsValid || result.HasWarnings))
{

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Validation.Paths;
@@ -17,9 +17,7 @@ namespace NzbDrone.Api.RootFolders
DroneFactoryValidator droneFactoryValidator,
MappedNetworkDriveValidator mappedNetworkDriveValidator,
StartupFolderValidator startupFolderValidator,
SystemFolderValidator systemFolderValidator,
FolderWritableValidator folderWritableValidator
)
FolderWritableValidator folderWritableValidator)
: base(signalRBroadcaster)
{
_rootFolderService = rootFolderService;
@@ -37,7 +35,6 @@ namespace NzbDrone.Api.RootFolders
.SetValidator(mappedNetworkDriveValidator)
.SetValidator(startupFolderValidator)
.SetValidator(pathExistsValidator)
.SetValidator(systemFolderValidator)
.SetValidator(folderWritableValidator);
}
@@ -63,4 +60,4 @@ namespace NzbDrone.Api.RootFolders
_rootFolderService.Remove(id);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.REST;
using NzbDrone.Core.RootFolders;
@@ -9,7 +9,6 @@ namespace NzbDrone.Api.RootFolders
{
public string Path { get; set; }
public long? FreeSpace { get; set; }
public long? TotalSpace { get; set; }
public List<UnmappedFolder> UnmappedFolders { get; set; }
}
@@ -26,7 +25,6 @@ namespace NzbDrone.Api.RootFolders
Path = model.Path,
FreeSpace = model.FreeSpace,
TotalSpace = model.TotalSpace,
UnmappedFolders = model.UnmappedFolders
};
}
@@ -50,4 +48,4 @@ namespace NzbDrone.Api.RootFolders
return models.Select(ToResource).ToList();
}
}
}
}

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Tv;
namespace NzbDrone.Api.Series
{
@@ -9,20 +8,18 @@ namespace NzbDrone.Api.Series
public int SeasonNumber { get; set; }
public bool Monitored { get; set; }
public SeasonStatisticsResource Statistics { get; set; }
public List<MediaCover> Images { get; set; }
}
public static class SeasonResourceMapper
{
public static SeasonResource ToResource(this Season model, bool includeImages = false)
public static SeasonResource ToResource(this Season model)
{
if (model == null) return null;
return new SeasonResource
{
SeasonNumber = model.SeasonNumber,
Monitored = model.Monitored,
Images = includeImages ? model.Images : null
Monitored = model.Monitored
};
}
@@ -33,14 +30,13 @@ namespace NzbDrone.Api.Series
return new Season
{
SeasonNumber = resource.SeasonNumber,
Monitored = resource.Monitored,
Images = resource.Images
Monitored = resource.Monitored
};
}
public static List<SeasonResource> ToResource(this IEnumerable<Season> models, bool includeImages = false)
public static List<SeasonResource> ToResource(this IEnumerable<Season> models)
{
return models.Select(s => ToResource(s, includeImages)).ToList();
return models.Select(ToResource).ToList();
}
public static List<Season> ToModel(this IEnumerable<SeasonResource> resources)

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Nancy;
using NzbDrone.Api.Extensions;
@@ -24,7 +24,7 @@ namespace NzbDrone.Api.Series
var series = resources.Select(seriesResource => seriesResource.ToModel(_seriesService.GetSeries(seriesResource.Id))).ToList();
return _seriesService.UpdateSeries(series)
.ToResource(false)
.ToResource()
.AsResponse(HttpStatusCode.Accepted);
}
}

View File

@@ -1,8 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using NzbDrone.Api.Extensions;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaCover;
@@ -46,7 +45,6 @@ namespace NzbDrone.Api.Series
SeriesExistsValidator seriesExistsValidator,
DroneFactoryValidator droneFactoryValidator,
SeriesAncestorValidator seriesAncestorValidator,
SystemFolderValidator systemFolderValidator,
ProfileExistsValidator profileExistsValidator
)
: base(signalRBroadcaster)
@@ -73,7 +71,6 @@ namespace NzbDrone.Api.Series
.SetValidator(seriesPathValidator)
.SetValidator(droneFactoryValidator)
.SetValidator(seriesAncestorValidator)
.SetValidator(systemFolderValidator)
.When(s => !s.Path.IsNullOrWhiteSpace());
SharedValidator.RuleFor(s => s.ProfileId).SetValidator(profileExistsValidator);
@@ -87,17 +84,26 @@ namespace NzbDrone.Api.Series
private SeriesResource GetSeries(int id)
{
var includeSeasonImages = Context != null && Request.GetBooleanQueryParameter("includeSeasonImages");
var series = _seriesService.GetSeries(id);
return MapToResource(series, includeSeasonImages);
return MapToResource(series);
}
private SeriesResource MapToResource(Core.Tv.Series series)
{
if (series == null) return null;
var resource = series.ToResource();
MapCoversToLocal(resource);
FetchAndLinkSeriesStatistics(resource);
PopulateAlternateTitles(resource);
return resource;
}
private List<SeriesResource> AllSeries()
{
var includeSeasonImages = Request.GetBooleanQueryParameter("includeSeasonImages");
var seriesStats = _seriesStatisticsService.SeriesStatistics();
var seriesResources = _seriesService.GetAllSeries().Select(s => s.ToResource(includeSeasonImages)).ToList();
var seriesResources = _seriesService.GetAllSeries().ToResource();
MapCoversToLocal(seriesResources.ToArray());
LinkSeriesStatistics(seriesResources, seriesStats);
@@ -135,18 +141,6 @@ namespace NzbDrone.Api.Series
_seriesService.DeleteSeries(id, deleteFiles);
}
private SeriesResource MapToResource(Core.Tv.Series series, bool includeSeasonImages)
{
if (series == null) return null;
var resource = series.ToResource(includeSeasonImages);
MapCoversToLocal(resource);
FetchAndLinkSeriesStatistics(resource);
PopulateAlternateTitles(resource);
return resource;
}
private void MapCoversToLocal(params SeriesResource[] series)
{
foreach (var seriesResource in series)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.REST;
@@ -97,7 +97,7 @@ namespace NzbDrone.Api.Series
public static class SeriesResourceMapper
{
public static SeriesResource ToResource(this Core.Tv.Series model, bool includeSeasonImages = false)
public static SeriesResource ToResource(this Core.Tv.Series model)
{
if (model == null) return null;
@@ -121,7 +121,7 @@ namespace NzbDrone.Api.Series
AirTime = model.AirTime,
Images = model.Images,
Seasons = model.Seasons.ToResource(includeSeasonImages),
Seasons = model.Seasons.ToResource(),
Year = model.Year,
Path = model.Path,
@@ -214,9 +214,9 @@ namespace NzbDrone.Api.Series
return series;
}
public static List<SeriesResource> ToResource(this IEnumerable<Core.Tv.Series> series, bool includeSeasonImages)
public static List<SeriesResource> ToResource(this IEnumerable<Core.Tv.Series> series)
{
return series.Select(s => ToResource(s, includeSeasonImages)).ToList();
return series.Select(ToResource).ToList();
}
}
}

View File

@@ -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.3" targetFramework="net40" />
<package id="NLog" version="4.4.12" targetFramework="net40" />
</packages>

View File

@@ -47,25 +47,19 @@
<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.3\lib\net40-client\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.4.12\lib\net40\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>

View File

@@ -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.3" targetFramework="net40" />
<package id="NLog" version="4.4.12" targetFramework="net40" />
<package id="NUnit" version="3.6.0" targetFramework="net40" />
</packages>

View File

@@ -45,20 +45,16 @@
<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.3\lib\net40-client\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.4.12\lib\net40\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" />

View File

@@ -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.3" targetFramework="net40" />
<package id="NLog" version="4.4.12" 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" />

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using FluentAssertions;
@@ -25,60 +24,13 @@ namespace NzbDrone.Common.Test.Http
[TestFixture(typeof(CurlHttpDispatcher))]
public class HttpClientFixture<TDispatcher> : TestBase<HttpClient> where TDispatcher : IHttpDispatcher
{
private string[] _httpBinHosts;
private int _httpBinSleep;
private int _httpBinRandom;
private static string[] _httpBinHosts = new[] { "eu.httpbin.org", "httpbin.org" };
private static 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");
@@ -98,13 +50,6 @@ 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]
@@ -300,12 +245,7 @@ namespace NzbDrone.Common.Test.Http
public void GivenOldCookie()
{
if (_httpBinHost == _httpBinHost2)
{
Assert.Inconclusive("Need both httpbin.org and eu.httpbin.org to run this test.");
}
var oldRequest = new HttpRequest($"http://{_httpBinHost2}/get");
var oldRequest = new HttpRequest("http://eu.httpbin.org/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>());
@@ -322,7 +262,7 @@ namespace NzbDrone.Common.Test.Http
{
GivenOldCookie();
var request = new HttpRequest($"http://{_httpBinHost2}/get");
var request = new HttpRequest("http://eu.httpbin.org/get");
var response = Subject.Get<HttpBinResource>(request);
@@ -338,103 +278,26 @@ namespace NzbDrone.Common.Test.Http
{
GivenOldCookie();
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest("http://httpbin.org/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 requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
var request = new HttpRequest($"http://{_httpBinHost}/get");
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
var response = Subject.Get<HttpBinResource>(request);
responseCookies.Resource.Cookies.Should().BeEmpty();
response.Resource.Headers.Should().NotContainKey("Cookie");
ExceptionVerification.IgnoreErrors();
}
@@ -444,31 +307,19 @@ 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 requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
var request = new HttpRequest($"http://{_httpBinHost}/get");
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
var response = Subject.Get<HttpBinResource>(request);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
response.Resource.Headers.Should().ContainKey("Cookie");
ExceptionVerification.IgnoreErrors();
}
var cookie = response.Resource.Headers["Cookie"].ToString();
[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");
cookie.Should().Contain("my=cookie");
ExceptionVerification.IgnoreErrors();
}
@@ -477,129 +328,21 @@ 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 requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
var request = new HttpRequest($"http://{_httpBinHost}/get");
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
var response = Subject.Get<HttpBinResource>(request);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
response.Resource.Headers.Should().ContainKey("Cookie");
ExceptionVerification.IgnoreErrors();
}
var cookie = response.Resource.Headers["Cookie"].ToString();
[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");
cookie.Should().Contain("my=cookie");
ExceptionVerification.IgnoreErrors();
}
@@ -711,9 +454,4 @@ namespace NzbDrone.Common.Test.Http
public string Url { get; set; }
public string Data { get; set; }
}
public class HttpCookieResource
{
public Dictionary<string, string> Cookies { get; set; }
}
}

View File

@@ -48,20 +48,16 @@
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.4.12\lib\net40\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" />

View File

@@ -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.3" targetFramework="net40" />
<package id="NLog" version="4.4.12" targetFramework="net40" />
<package id="NUnit" version="3.6.0" targetFramework="net40" />
</packages>

View File

@@ -200,11 +200,6 @@ namespace NzbDrone.Common.Disk
throw new IOException(string.Format("Source and destination can't be the same {0}", source));
}
CopyFileInternal(source, destination, overwrite);
}
protected virtual void CopyFileInternal(string source, string destination, bool overwrite = false)
{
File.Copy(source, destination, overwrite);
}
@@ -224,11 +219,6 @@ namespace NzbDrone.Common.Disk
}
RemoveReadOnly(source);
MoveFileInternal(source, destination);
}
protected virtual void MoveFileInternal(string source, string destination)
{
File.Move(source, destination);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Security.AccessControl;
using System.Security.Principal;
using NLog;
@@ -28,15 +28,7 @@ namespace NzbDrone.Common.EnvironmentInfo
public void Register()
{
try
{
_diskProvider.EnsureFolder(_appFolderInfo.AppDataFolder);
}
catch (UnauthorizedAccessException)
{
throw new SonarrStartupException("Cannot create AppFolder, Access to the path {0} is denied", _appFolderInfo.AppDataFolder);
}
_diskProvider.EnsureFolder(_appFolderInfo.AppDataFolder);
if (OsInfo.IsWindows)
{

View File

@@ -32,19 +32,7 @@ namespace NzbDrone.Common.Extensions
{
if (response == null || response.Content == null) return ex;
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());
var contentSample = response.Content.Substring(0, Math.Min(response.Content.Length, 512));
if (response.Headers != null)
{

View File

@@ -51,34 +51,6 @@ 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)
@@ -109,4 +81,4 @@ namespace NzbDrone.Common.Extensions
return source.Select(predicate).ToList();
}
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Globalization;
namespace NzbDrone.Common.Extensions
{
@@ -7,7 +6,7 @@ namespace NzbDrone.Common.Extensions
{
public static int? ParseInt32(this string source)
{
int result;
int result = 0;
if (int.TryParse(source, out result))
{
@@ -17,9 +16,9 @@ namespace NzbDrone.Common.Extensions
return null;
}
public static long? ParseInt64(this string source)
public static Nullable<long> ParseInt64(this string source)
{
long result;
long result = 0;
if (long.TryParse(source, out result))
{
@@ -28,17 +27,5 @@ 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;
}
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Net;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
@@ -102,7 +101,7 @@ namespace NzbDrone.Common.Http.Dispatchers
using (var responseStream = httpWebResponse.GetResponseStream())
{
if (responseStream != null && responseStream != Stream.Null)
if (responseStream != null)
{
try
{

View File

@@ -52,9 +52,7 @@ namespace NzbDrone.Common.Http
public HttpResponse Execute(HttpRequest request)
{
var cookieContainer = InitializeRequestCookies(request);
var response = ExecuteRequest(request, cookieContainer);
var response = ExecuteRequest(request);
if (request.AllowAutoRedirect && response.HasHttpRedirect)
{
@@ -73,7 +71,7 @@ namespace NzbDrone.Common.Http
throw new WebException($"Too many automatic redirections were attempted for {autoRedirectChain.Join(" -> ")}", WebExceptionStatus.ProtocolError);
}
response = ExecuteRequest(request, cookieContainer);
response = ExecuteRequest(request);
}
while (response.HasHttpRedirect);
}
@@ -100,7 +98,7 @@ namespace NzbDrone.Common.Http
return response;
}
private HttpResponse ExecuteRequest(HttpRequest request, CookieContainer cookieContainer)
private HttpResponse ExecuteRequest(HttpRequest request)
{
foreach (var interceptor in _requestInterceptors)
{
@@ -116,11 +114,11 @@ namespace NzbDrone.Common.Http
var stopWatch = Stopwatch.StartNew();
PrepareRequestCookies(request, cookieContainer);
var cookies = PrepareRequestCookies(request);
var response = _httpDispatcher.GetResponse(request, cookieContainer);
var response = _httpDispatcher.GetResponse(request, cookies);
HandleResponseCookies(response, cookieContainer);
HandleResponseCookies(request, cookies);
stopWatch.Stop();
@@ -139,91 +137,49 @@ namespace NzbDrone.Common.Http
return response;
}
private CookieContainer InitializeRequestCookies(HttpRequest request)
private CookieContainer PrepareRequestCookies(HttpRequest request)
{
lock (_cookieContainerCache)
{
var sourceContainer = new CookieContainer();
var presistentContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
var persistentCookies = presistentContainer.GetCookies((Uri)request.Url);
sourceContainer.Add(persistentCookies);
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
if (request.Cookies.Count != 0)
{
foreach (var pair in request.Cookies)
{
Cookie cookie;
if (pair.Value == null)
persistentCookieContainer.Add(new Cookie(pair.Key, pair.Value, "/", request.Url.Host)
{
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);
}
// Use Now rather than UtcNow to work around Mono cookie expiry bug.
// See https://gist.github.com/ta264/7822b1424f72e5b4c961
Expires = DateTime.Now.AddHours(1)
});
}
}
return sourceContainer;
var requestCookies = persistentCookieContainer.GetCookies((Uri)request.Url);
var cookieContainer = new CookieContainer();
cookieContainer.Add(requestCookies);
return cookieContainer;
}
}
private void PrepareRequestCookies(HttpRequest request, CookieContainer cookieContainer)
private void HandleResponseCookies(HttpRequest request, CookieContainer cookieContainer)
{
// 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())
if (!request.StoreResponseCookie)
{
return;
}
if (response.Request.StoreResponseCookie)
lock (_cookieContainerCache)
{
lock (_cookieContainerCache)
{
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
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);
}
}
}
var cookies = cookieContainer.GetCookies((Uri)request.Url);
persistentCookieContainer.Add(cookies);
}
}
@@ -267,7 +223,6 @@ 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);
}
@@ -286,16 +241,7 @@ 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);
}
}
}
}

View File

@@ -7,19 +7,13 @@ namespace NzbDrone.Common.Http
public HttpRequest Request { get; private set; }
public HttpResponse Response { get; private set; }
public HttpException(HttpRequest request, HttpResponse response, string message)
: base(message)
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))
{
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)
{
@@ -36,4 +30,4 @@ namespace NzbDrone.Common.Http
return base.ToString();
}
}
}
}

View File

@@ -13,10 +13,8 @@ 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;
@@ -39,7 +37,6 @@ 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; }

View File

@@ -55,22 +55,20 @@ 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 = GetCookieHeaders();
foreach (var cookie in setCookieHeaders)
var setCookieHeaders = Headers.GetValues("Set-Cookie");
if (setCookieHeaders != null)
{
var match = RegexSetCookie.Match(cookie);
if (match.Success)
foreach (var cookie in setCookieHeaders)
{
result[match.Groups[1].Value] = match.Groups[2].Value;
var match = RegexSetCookie.Match(cookie);
if (match.Success)
{
result[match.Groups[1].Value] = match.Groups[2].Value;
}
}
}

View File

@@ -135,7 +135,7 @@ namespace NzbDrone.Common.Http
return new HttpUri(Scheme, Host, Port, CombinePath(Path, path), Query, Fragment);
}
public static string CombinePath(string basePath, string relativePath)
private static string CombinePath(string basePath, string relativePath)
{
if (relativePath.IsNullOrWhiteSpace())
{

View File

@@ -1,13 +0,0 @@
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}]")
{
}
}
}

View File

@@ -51,7 +51,7 @@ namespace NzbDrone.Common.Instrumentation.Extensions
return logBuilder.LoggerName(logEvent.LoggerName)
.TimeStamp(logEvent.TimeStamp)
.Message(logEvent.Message, logEvent.Parameters)
.Properties(logEvent.Properties.ToDictionary(v => v.Key, v => v.Value))
.Properties((Dictionary<object, object>)logEvent.Properties)
.Exception(logEvent.Exception);
}
}

View File

@@ -31,7 +31,7 @@ namespace NzbDrone.Common.Instrumentation
if (exception is NullReferenceException &&
exception.ToString().Contains("Microsoft.AspNet.SignalR.Transports.TransportHeartbeat.ProcessServerCommand"))
{
Logger.Warn("SignalR Heartbeat interrupted");
Logger.Warn("SignalR Heartbeat interupted");
return;
}
@@ -49,4 +49,4 @@ namespace NzbDrone.Common.Instrumentation
Logger.Fatal(exception, "EPIC FAIL.");
}
}
}
}

View File

@@ -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.3\lib\net40-client\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.4.12\lib\net40\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,14 +60,11 @@
<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>
@@ -177,7 +174,6 @@
<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" />
@@ -216,13 +212,14 @@
<Compile Include="Serializer\IntConverter.cs" />
<Compile Include="Serializer\Json.cs" />
<Compile Include="Serializer\JsonVisitor.cs" />
<Compile Include="Serializer\UnderscoreStringEnumConverter.cs" />
<Compile Include="ServiceFactory.cs" />
<Compile Include="ServiceProvider.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Timeline\TimelineContext.cs" />
<Compile Include="TinyIoC.cs" />
<Compile Include="TPL\Debouncer.cs" />
<Compile Include="TPL\LimitedConcurrencyLevelTaskScheduler.cs" />
<Compile Include="Timeline\ProgressReporter.cs" />
<Compile Include="TPL\RateLimitService.cs" />
<Compile Include="TPL\TaskExtensions.cs" />
<Compile Include="Extensions\TryParseExtensions.cs" />

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
@@ -15,13 +14,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 });
@@ -35,61 +34,12 @@ namespace NzbDrone.Common.Serializer
public static T Deserialize<T>(string json) where T : new()
{
try
{
return JsonConvert.DeserializeObject<T>(json, SerializerSetting);
}
catch (JsonReaderException ex)
{
throw DetailedJsonReaderException(ex, json);
}
return JsonConvert.DeserializeObject<T>(json, SerializerSetting);
}
public static object Deserialize(string json, Type type)
{
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;
return JsonConvert.DeserializeObject(json, type, SerializerSetting);
}
public static bool TryDeserialize<T>(string json, out T result) where T : new()
@@ -128,4 +78,4 @@ namespace NzbDrone.Common.Serializer
Serialize(model, new StreamWriter(outputStream));
}
}
}
}

View File

@@ -1,58 +0,0 @@
using System;
using System.Text;
using Newtonsoft.Json;
namespace NzbDrone.Common.Serializer
{
public class UnderscoreStringEnumConverter : JsonConverter
{
public object UnknownValue { get; set; }
public UnderscoreStringEnumConverter(object unknownValue)
{
UnknownValue = unknownValue;
}
public override bool CanConvert(Type objectType)
{
return objectType.IsEnum;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var enumString = reader.Value.ToString().Replace("_", string.Empty);
try
{
return Enum.Parse(objectType, enumString, true);
}
catch
{
if (UnknownValue == null)
{
throw;
}
return UnknownValue;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var enumText = value.ToString();
var builder = new StringBuilder(enumText.Length + 4);
builder.Append(char.ToLower(enumText[0]));
for (int i = 1; i < enumText.Length; i++)
{
if (char.IsUpper(enumText[i]))
{
builder.Append('_');
}
builder.Append(char.ToLower(enumText[i]));
}
enumText = builder.ToString();
writer.WriteValue(enumText);
}
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Common.Timeline
{
public interface IProgressReporter
{
long Raw { get; }
long Total { get; }
double Progress { get; }
void UpdateProgress(long currentProgress, long maxProgress);
void FinishProgress();
}
public class ProgressReporter : IProgressReporter
{
private readonly int _maxSteps;
public long Raw { get; protected set; }
public long Total { get; private set; }
public double Progress => Total == 0 ? 1.0 : Math.Min(Raw, Total) / Total;
//public TimeSpan? EstimatedDurationRemaining { get; private set; }
public ProgressReporter(long initialProgress, long maxProgress, int maxSteps = 100)
{
_maxSteps = maxSteps;
Raw = initialProgress;
Total = maxProgress;
}
public void UpdateProgress(long currentProgress, long maxProgress)
{
bool shouldRaiseEvent;
lock (this)
{
var oldRaw = Raw;
var oldTotal = Total;
Raw = currentProgress;
Total = Total;
var oldStep = oldTotal <= 0 ? _maxSteps : oldRaw * _maxSteps / oldTotal;
var newStep = Total <= 0 ? _maxSteps : Raw * _maxSteps / Total;
shouldRaiseEvent = (oldStep != newStep);
}
if (shouldRaiseEvent)
{
RaiseEvent();
}
}
public void FinishProgress()
{
lock (this)
{
Raw = Total;
}
RaiseEvent();
}
protected virtual void RaiseEvent()
{
// TODO
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Common.Timeline
{
public enum TimelineState
{
Pending,
Started,
Completed,
Failed
}
public interface ITimelineContext : IProgressReporter
{
string Name { get; }
TimelineState State { get; }
void UpdateState(TimelineState state);
ITimelineContext AppendTimeline(string name, long initialProgress = 0, long maxProgress = 1);
void AppendTimeline(ITimelineContext timeline);
}
public class TimelineContext : ProgressReporter, ITimelineContext
{
private List<ITimelineContext> _timelines = new List<ITimelineContext>();
public string Name { get; private set; }
public TimelineState State { get; private set; }
public IEnumerable<ITimelineContext> Timelines
{
get
{
lock (this)
{
return _timelines.ToArray();
}
}
}
public TimelineContext(string name, long initialProgress, long maxProgress)
: base(initialProgress, maxProgress)
{
Name = name;
}
public void UpdateState(TimelineState state)
{
lock (this)
{
State = state;
if (State == TimelineState.Completed || State == TimelineState.Failed)
{
Raw = Total;
}
}
RaiseEvent();
}
public ITimelineContext AppendTimeline(string name, long initialProgress = 0, long maxProgress = 1)
{
lock (this)
{
var timeline = new TimelineContext(name, initialProgress, maxProgress);
_timelines.Add(timeline);
return timeline;
}
}
public void AppendTimeline(ITimelineContext timeline)
{
lock (this)
{
_timelines.Add(timeline);
}
}
protected override void RaiseEvent()
{
lock (this)
{
if (Raw == Total)
{
State = TimelineState.Completed;
}
else
{
State = TimelineState.Started;
}
}
base.RaiseEvent();
}
}
}

View File

@@ -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.3" targetFramework="net40" />
<package id="NLog" version="4.4.12" targetFramework="net40" />
<package id="SharpRaven" version="2.2.0" targetFramework="net40" />
</packages>

View File

@@ -25,15 +25,7 @@ namespace NzbDrone.Console
try
{
var startupArgs = new StartupContext(args);
try
{
NzbDroneLogger.Register(startupArgs, false, true);
}
catch (Exception ex)
{
System.Console.WriteLine("NLog Exception: " + ex.ToString());
throw;
}
NzbDroneLogger.Register(startupArgs, false, true);
Bootstrap.Start(startupArgs, new ConsoleAlerts());
}
catch (SonarrStartupException ex)

View File

@@ -66,7 +66,6 @@
<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>
@@ -80,19 +79,13 @@
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.5.3\lib\net40-client\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.4.12\lib\net40\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">

View File

@@ -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.3" targetFramework="net40" />
<package id="NLog" version="4.4.12" targetFramework="net40" />
<package id="Owin" version="1.0" targetFramework="net40" />
</packages>

View File

@@ -1,59 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class BooleanIntConverterFixture : CoreTest<Core.Datastore.Converters.BooleanIntConverter>
{
[TestCase(true, 1)]
[TestCase(false, 0)]
public void should_return_int_when_saving_boolean_to_db(bool input, int expected)
{
Subject.ToDB(input).Should().Be(expected);
}
[Test]
public void should_return_db_null_for_null_value_when_saving_to_db()
{
Subject.ToDB(null).Should().Be(DBNull.Value);
}
[TestCase(1, true)]
[TestCase(0, false)]
public void should_return_bool_when_getting_int_from_db(int input, bool expected)
{
var context = new ConverterContext
{
DbValue = (long)input
};
Subject.FromDB(context).Should().Be(expected);
}
[Test]
public void should_return_db_null_for_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(DBNull.Value);
}
[Test]
public void should_throw_for_non_boolean_equivalent_number_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = (long)2
};
Assert.Throws<ConversionException>(() => Subject.FromDB(context));
}
}
}

View File

@@ -1,64 +0,0 @@
using System;
using System.Data;
using FluentAssertions;
using Marr.Data.Converters;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv.Commands;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class CommandConverterFixture : CoreTest<CommandConverter>
{
[Test]
public void should_return_json_string_when_saving_boolean_to_db()
{
var command = new RefreshSeriesCommand();
Subject.ToDB(command).Should().BeOfType<string>();
}
[Test]
public void should_return_null_for_null_value_when_saving_to_db()
{
Subject.ToDB(null).Should().Be(null);
}
[Test]
public void should_return_db_null_for_db_null_value_when_saving_to_db()
{
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
}
[Test]
public void should_return_command_when_getting_json_from_db()
{
var dataRecordMock = new Mock<IDataRecord>();
dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0);
dataRecordMock.Setup(s => s.GetString(0)).Returns("RefreshSeries");
var context = new ConverterContext
{
DataRecord = dataRecordMock.Object,
DbValue = new RefreshSeriesCommand().ToJson()
};
Subject.FromDB(context).Should().BeOfType<RefreshSeriesCommand>();
}
[Test]
public void should_return_null_for_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(null);
}
}
}

View File

@@ -1,70 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class DoubleConverterFixture : CoreTest<DoubleConverter>
{
[Test]
public void should_return_double_when_saving_double_to_db()
{
var input = 10.5D;
Subject.ToDB(input).Should().Be(input);
}
[Test]
public void should_return_null_for_null_value_when_saving_to_db()
{
Subject.ToDB(null).Should().Be(null);
}
[Test]
public void should_return_db_null_for_db_null_value_when_saving_to_db()
{
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
}
[Test]
public void should_return_double_when_getting_double_from_db()
{
var expected = 10.5D;
var context = new ConverterContext
{
DbValue = expected
};
Subject.FromDB(context).Should().Be(expected);
}
[Test]
public void should_return_double_when_getting_string_from_db()
{
var expected = 10.5D;
var context = new ConverterContext
{
DbValue = $"{expected}"
};
Subject.FromDB(context).Should().Be(expected);
}
[Test]
public void should_return_null_for_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(DBNull.Value);
}
}
}

View File

@@ -1,57 +0,0 @@
using System;
using System.Reflection;
using FluentAssertions;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class EnumIntConverterFixture : CoreTest<Core.Datastore.Converters.EnumIntConverter>
{
[Test]
public void should_return_int_when_saving_enum_to_db()
{
Subject.ToDB(SeriesTypes.Standard).Should().Be((int)SeriesTypes.Standard);
}
[Test]
public void should_return_db_null_for_null_value_when_saving_to_db()
{
Subject.ToDB(null).Should().Be(DBNull.Value);
}
[Test]
public void should_return_enum_when_getting_int_from_db()
{
var mockMemberInfo = new Mock<MemberInfo>();
mockMemberInfo.SetupGet(s => s.DeclaringType).Returns(typeof(Series));
mockMemberInfo.SetupGet(s => s.Name).Returns("SeriesType");
var expected = SeriesTypes.Standard;
var context = new ConverterContext
{
ColumnMap = new ColumnMap(mockMemberInfo.Object) { FieldType = typeof(SeriesTypes) },
DbValue = (long)expected
};
Subject.FromDB(context).Should().Be(expected);
}
[Test]
public void should_return_null_for_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(null);
}
}
}

View File

@@ -1,51 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class GuidConverterFixture : CoreTest<GuidConverter>
{
[Test]
public void should_return_string_when_saving_guid_to_db()
{
var guid = Guid.NewGuid();
Subject.ToDB(guid).Should().Be(guid.ToString());
}
[Test]
public void should_return_db_null_for_null_value_when_saving_to_db()
{
Subject.ToDB(null).Should().Be(DBNull.Value);
}
[Test]
public void should_return_guid_when_getting_string_from_db()
{
var guid = Guid.NewGuid();
var context = new ConverterContext
{
DbValue = guid.ToString()
};
Subject.FromDB(context).Should().Be(guid);
}
[Test]
public void should_return_empty_guid_for_db_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(Guid.Empty);
}
}
}

View File

@@ -1,58 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class Int32ConverterFixture : CoreTest<Int32Converter>
{
[Test]
public void should_return_int_when_saving_int_to_db()
{
var i = 5;
Subject.ToDB(5).Should().Be(5);
}
[Test]
public void should_return_int_when_getting_int_from_db()
{
var i = 5;
var context = new ConverterContext
{
DbValue = i
};
Subject.FromDB(context).Should().Be(i);
}
[Test]
public void should_return_int_when_getting_string_from_db()
{
var i = 5;
var context = new ConverterContext
{
DbValue = i.ToString()
};
Subject.FromDB(context).Should().Be(i);
}
[Test]
public void should_return_db_null_for_db_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(DBNull.Value);
}
}
}

View File

@@ -1,49 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class OsPathConverterFixture : CoreTest<OsPathConverter>
{
[Test]
public void should_return_string_when_saving_os_path_to_db()
{
var path = @"C:\Test\TV".AsOsAgnostic();
var osPath = new OsPath(path);
Subject.ToDB(osPath).Should().Be(path);
}
[Test]
public void should_return_os_path_when_getting_string_from_db()
{
var path = @"C:\Test\TV".AsOsAgnostic();
var osPath = new OsPath(path);
var context = new ConverterContext
{
DbValue = path
};
Subject.FromDB(context).Should().Be(osPath);
}
[Test]
public void should_return_db_null_for_db_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(DBNull.Value);
}
}
}

View File

@@ -1,58 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class QualityIntConverterFixture : CoreTest<QualityIntConverter>
{
[Test]
public void should_return_int_when_saving_quality_to_db()
{
var quality = Quality.Bluray1080p;
Subject.ToDB(quality).Should().Be(quality.Id);
}
[Test]
public void should_return_0_when_saving_db_null_to_db()
{
Subject.ToDB(DBNull.Value).Should().Be(0);
}
[Test]
public void should_throw_when_saving_another_object_to_db()
{
Assert.Throws<InvalidOperationException>(() => Subject.ToDB("Not a quality"));
}
[Test]
public void should_return_quality_when_getting_string_from_db()
{
var quality = Quality.Bluray1080p;
var context = new ConverterContext
{
DbValue = quality.Id
};
Subject.FromDB(context).Should().Be(quality);
}
[Test]
public void should_return_db_null_for_db_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(Quality.Unknown);
}
}
}

View File

@@ -1,65 +0,0 @@
using System;
using System.Globalization;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class TimeSpanConverterFixture : CoreTest<TimeSpanConverter>
{
[Test]
public void should_return_string_when_saving_timespan_to_db()
{
var timeSpan = TimeSpan.FromMinutes(5);
Subject.ToDB(timeSpan).Should().Be(timeSpan.ToString("c", CultureInfo.InvariantCulture));
}
[Test]
public void should_return_null_when_saving_empty_string_to_db()
{
Subject.ToDB("").Should().Be(null);
}
[Test]
public void should_return_time_span_when_getting_time_span_from_db()
{
var timeSpan = TimeSpan.FromMinutes(5);
var context = new ConverterContext
{
DbValue = timeSpan
};
Subject.FromDB(context).Should().Be(timeSpan);
}
[Test]
public void should_return_time_span_when_getting_string_from_db()
{
var timeSpan = TimeSpan.FromMinutes(5);
var context = new ConverterContext
{
DbValue = timeSpan.ToString("c", CultureInfo.InvariantCulture)
};
Subject.FromDB(context).Should().Be(timeSpan);
}
[Test]
public void should_return_time_span_zero_for_db_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(TimeSpan.Zero);
}
}
}

View File

@@ -1,51 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class UtcConverterFixture : CoreTest<UtcConverter>
{
[Test]
public void should_return_date_time_when_saving_date_time_to_db()
{
var dateTime = DateTime.Now;
Subject.ToDB(dateTime).Should().Be(dateTime.ToUniversalTime());
}
[Test]
public void should_return_db_null_when_saving_db_null_to_db()
{
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
}
[Test]
public void should_return_time_span_when_getting_time_span_from_db()
{
var dateTime = DateTime.Now.ToUniversalTime();
var context = new ConverterContext
{
DbValue = dateTime
};
Subject.FromDB(context).Should().Be(dateTime);
}
[Test]
public void should_return_db_null_for_db_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(DBNull.Value);
}
}
}

View File

@@ -1,41 +0,0 @@
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"));
}
}
}

View File

@@ -1,163 +0,0 @@
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();
}
}
}

View File

@@ -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.RssSync
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class HistorySpecificationFixture : CoreTest<HistorySpecification>

View File

@@ -1,75 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
public class MaximumSizeSpecificationFixture : CoreTest<MaximumSizeSpecification>
{
private RemoteEpisode _remoteEpisode;
[SetUp]
public void Setup()
{
_remoteEpisode = new RemoteEpisode() { Release = new ReleaseInfo() };
}
private void WithMaximumSize(int size)
{
Mocker.GetMock<IConfigService>().SetupGet(c => c.MaximumSize).Returns(size);
}
private void WithSize(int size)
{
_remoteEpisode.Release.Size = size * 1024 * 1024;
}
[Test]
public void should_return_true_when_maximum_size_is_set_to_zero()
{
WithMaximumSize(0);
WithSize(1000);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_size_is_smaller_than_maximum_size()
{
WithMaximumSize(2000);
WithSize(1999);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_size_is_equals_to_maximum_size()
{
WithMaximumSize(2000);
WithSize(2000);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_when_size_is_bigger_than_maximum_size()
{
WithMaximumSize(2000);
WithSize(2001);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_true_when_size_is_zero()
{
WithMaximumSize(2000);
WithSize(0);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
}
}

View File

@@ -29,8 +29,6 @@ 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)
@@ -125,16 +123,5 @@ 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);
}
}
}

View File

@@ -137,7 +137,6 @@ 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>();

View File

@@ -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.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Never());
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<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.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Once());
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Exactly(2));
}
[Test]

View File

@@ -290,24 +290,6 @@ 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()
{

View File

@@ -1,49 +0,0 @@
using FluentAssertions;
using Newtonsoft.Json;
using NUnit.Framework;
using NzbDrone.Core.Download.Clients.DownloadStation;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
{
[TestFixture]
public class DownloadStationsTaskStatusJsonConverterFixture
{
[TestCase("captcha_needed", DownloadStationTaskStatus.CaptchaNeeded)]
[TestCase("filehosting_waiting", DownloadStationTaskStatus.FilehostingWaiting)]
[TestCase("hash_checking", DownloadStationTaskStatus.HashChecking)]
[TestCase("error", DownloadStationTaskStatus.Error)]
[TestCase("downloading", DownloadStationTaskStatus.Downloading)]
public void should_parse_enum_correctly(string value, DownloadStationTaskStatus expected)
{
var task = "{\"Status\": \"" + value + "\"}";
var item = JsonConvert.DeserializeObject<DownloadStationTask>(task);
item.Status.Should().Be(expected);
}
[TestCase("captcha_needed", DownloadStationTaskStatus.CaptchaNeeded)]
[TestCase("filehosting_waiting", DownloadStationTaskStatus.FilehostingWaiting)]
[TestCase("hash_checking", DownloadStationTaskStatus.HashChecking)]
[TestCase("error", DownloadStationTaskStatus.Error)]
[TestCase("downloading", DownloadStationTaskStatus.Downloading)]
public void should_serialize_enum_correctly(string expected, DownloadStationTaskStatus value)
{
var task = new DownloadStationTask { Status = value };
var item = JsonConvert.SerializeObject(task);
item.Should().Contain(expected);
}
[Test]
public void should_return_unknown_if_unknown_enum_value()
{
var task = "{\"Status\": \"some_unknown_value\"}";
var item = JsonConvert.DeserializeObject<DownloadStationTask>(task);
item.Status.Should().Be(DownloadStationTaskStatus.Unknown);
}
}
}

View File

@@ -73,7 +73,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "0"},
{ "size_uploaded", "0"},
{ "speed_download", "0" }
}
}
@@ -97,7 +96,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "1000"},
{ "size_uploaded", "100"},
{ "speed_download", "0" }
},
}
@@ -121,7 +119,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "1000"},
{ "size_uploaded", "100"},
{ "speed_download", "0" }
}
}
@@ -145,7 +142,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "100"},
{ "size_uploaded", "10"},
{ "speed_download", "50" }
}
}
@@ -169,7 +165,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "10"},
{ "size_uploaded", "1"},
{ "speed_download", "0" }
}
}
@@ -193,7 +188,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "1000"},
{ "size_uploaded", "100"},
{ "speed_download", "0" }
}
}
@@ -217,7 +211,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "1000"},
{ "size_uploaded", "100"},
{ "speed_download", "0" }
}
}
@@ -241,7 +234,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "1000"},
{ "size_uploaded", "100"},
{ "speed_download", "0" }
}
}
@@ -265,7 +257,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Transfer = new Dictionary<string, string>
{
{ "size_downloaded", "1000"},
{ "size_uploaded", "100"},
{ "speed_download", "0" }
}
}
@@ -614,12 +605,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
[TestCase(DownloadStationTaskStatus.Finished, DownloadItemStatus.Completed)]
[TestCase(DownloadStationTaskStatus.Finishing, DownloadItemStatus.Downloading)]
[TestCase(DownloadStationTaskStatus.HashChecking, DownloadItemStatus.Downloading)]
[TestCase(DownloadStationTaskStatus.CaptchaNeeded, DownloadItemStatus.Downloading)]
[TestCase(DownloadStationTaskStatus.Paused, DownloadItemStatus.Paused)]
[TestCase(DownloadStationTaskStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(DownloadStationTaskStatus.FilehostingWaiting, DownloadItemStatus.Queued)]
[TestCase(DownloadStationTaskStatus.Waiting, DownloadItemStatus.Queued)]
[TestCase(DownloadStationTaskStatus.Unknown, DownloadItemStatus.Queued)]
public void GetItems_should_return_item_as_downloadItemStatus(DownloadStationTaskStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
GivenSerialNumber();

View File

@@ -414,12 +414,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
[TestCase(DownloadStationTaskStatus.Finished, DownloadItemStatus.Completed)]
[TestCase(DownloadStationTaskStatus.Finishing, DownloadItemStatus.Downloading)]
[TestCase(DownloadStationTaskStatus.HashChecking, DownloadItemStatus.Downloading)]
[TestCase(DownloadStationTaskStatus.CaptchaNeeded, DownloadItemStatus.Downloading)]
[TestCase(DownloadStationTaskStatus.Paused, DownloadItemStatus.Paused)]
[TestCase(DownloadStationTaskStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(DownloadStationTaskStatus.FilehostingWaiting, DownloadItemStatus.Queued)]
[TestCase(DownloadStationTaskStatus.Waiting, DownloadItemStatus.Queued)]
[TestCase(DownloadStationTaskStatus.Unknown, DownloadItemStatus.Queued)]
public void GetItems_should_return_item_as_downloadItemStatus(DownloadStationTaskStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
GivenSerialNumber();

View File

@@ -162,7 +162,6 @@ 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
@@ -495,18 +494,5 @@ 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");
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using Marr.Data;
@@ -54,10 +54,8 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_release = Builder<ReleaseInfo>.CreateNew().Build();
_parsedEpisodeInfo = Builder<ParsedEpisodeInfo>.CreateNew()
.With(h => h.Quality = new QualityModel(Quality.HDTV720p))
.With(h => h.AirDate = null)
.Build();
_parsedEpisodeInfo = Builder<ParsedEpisodeInfo>.CreateNew().Build();
_parsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p);
_remoteEpisode = new RemoteEpisode();
_remoteEpisode.Episodes = new List<Episode>{ _episode };

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using Marr.Data;
@@ -98,7 +98,6 @@ 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>()

View File

@@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
.Returns(remoteEpisode);
Mocker.GetMock<IParsingService>()
.Setup(s => s.ParseSpecialEpisodeTitle(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>(), null))
.Setup(s => s.ParseSpecialEpisodeTitle(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>(), null))
.Returns(remoteEpisode.ParsedEpisodeInfo);
var client = new DownloadClientDefinition()

View File

@@ -1,65 +0,0 @@
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());
}
}
}

View File

@@ -1,59 +0,0 @@
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();
}
}
}

View File

@@ -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)))
)
);
}

View File

@@ -1,17 +1,16 @@
using System;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Test.Framework;
using FizzWare.NBuilder;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
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;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
namespace NzbDrone.Core.Test.IndexerSearchTests
{
@@ -56,7 +55,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
.Returns(new List<string>());
}
private void WithEpisode(int seasonNumber, int episodeNumber, int? sceneSeasonNumber, int? sceneEpisodeNumber, string airDate = null)
private void WithEpisode(int seasonNumber, int episodeNumber, int? sceneSeasonNumber, int? sceneEpisodeNumber)
{
var episode = Builder<Episode>.CreateNew()
.With(v => v.SeriesId == _xemSeries.Id)
@@ -65,7 +64,6 @@ 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();
@@ -110,22 +108,10 @@ 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;
}
@@ -263,68 +249,6 @@ 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()
{

View File

@@ -1,65 +0,0 @@
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));
}
}
}

View File

@@ -1,152 +0,0 @@
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());
}
}
}

View File

@@ -1,93 +0,0 @@
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);
}
}
}

View File

@@ -1,68 +0,0 @@
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);
}
}
}

View File

@@ -6,6 +6,7 @@ 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;
@@ -14,7 +15,6 @@ 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,7 +54,6 @@ 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();
@@ -68,6 +67,10 @@ 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() });
}
@@ -85,31 +88,20 @@ 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, _series, downloadClientItem, null, false);
Subject.GetImportDecisions(_videoFiles, new Series(), downloadClientItem, null, false);
_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());
_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());
}
[Test]
@@ -117,7 +109,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
GivenSpecifications(_fail1);
var result = Subject.GetImportDecisions(_videoFiles, _series);
var result = Subject.GetImportDecisions(_videoFiles, new Series());
result.Single().Approved.Should().BeFalse();
}
@@ -127,18 +119,17 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
GivenSpecifications(_pass1, _fail1, _pass2, _pass3);
var result = Subject.GetImportDecisions(_videoFiles, _series);
var result = Subject.GetImportDecisions(_videoFiles, new Series());
result.Single().Approved.Should().BeFalse();
}
[Test]
public void should_return_approved_if_all_specs_pass()
public void should_return_pass_if_all_specs_pass()
{
GivenAugmentationSuccess();
GivenSpecifications(_pass1, _pass2, _pass3);
var result = Subject.GetImportDecisions(_videoFiles, _series);
var result = Subject.GetImportDecisions(_videoFiles, new Series());
result.Single().Approved.Should().BeTrue();
}
@@ -146,10 +137,9 @@ 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, _series);
var result = Subject.GetImportDecisions(_videoFiles, new Series());
result.Single().Rejections.Should().HaveCount(3);
}
@@ -158,8 +148,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
GivenSpecifications(_pass1);
Mocker.GetMock<IAggregationService>()
.Setup(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()))
Mocker.GetMock<IParsingService>()
.Setup(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()))
.Throws<TestException>();
_videoFiles = new List<string>
@@ -173,17 +163,76 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
Subject.GetImportDecisions(_videoFiles, _series);
Mocker.GetMock<IAggregationService>()
.Verify(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
Mocker.GetMock<IParsingService>()
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), 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",
@@ -195,18 +244,162 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var decisions = Subject.GetImportDecisions(_videoFiles, _series);
Mocker.GetMock<IAggregationService>()
.Verify(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
Mocker.GetMock<IParsingService>()
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), 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<IAggregationService>()
.Setup(c => c.Augment(It.IsAny<LocalEpisode>(), It.IsAny<bool>()))
Mocker.GetMock<IParsingService>()
.Setup(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()))
.Throws<TestException>();
_videoFiles = new List<string>

View File

@@ -1,85 +0,0 @@
using System;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
[TestFixture]
public class EpisodeTitleSpecificationFixture : CoreTest<EpisodeTitleSpecification>
{
private Series _series;
private LocalEpisode _localEpisode;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew()
.With(s => s.SeriesType = SeriesTypes.Standard)
.With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic())
.Build();
var episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeasonNumber = 1)
.With(e => e.AirDateUtc = DateTime.UtcNow)
.Build()
.ToList();
_localEpisode = new LocalEpisode
{
Path = @"C:\Test\Unsorted\30 Rock\30.rock.s01e01.avi".AsOsAgnostic(),
Episodes = episodes,
Series = _series
};
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.RequiresEpisodeTitle(_series, episodes))
.Returns(true);
}
[Test]
public void should_reject_when_title_is_null()
{
_localEpisode.Episodes.First().Title = null;
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test]
public void should_reject_when_title_is_TBA()
{
_localEpisode.Episodes.First().Title = "TBA";
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test]
public void should_accept_when_did_not_air_recently_but_title_is_TBA()
{
_localEpisode.Episodes.First().AirDateUtc = DateTime.UtcNow.AddDays(-7);
_localEpisode.Episodes.First().Title = "TBA";
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_accept_when_episode_title_is_not_required()
{
_localEpisode.Episodes.First().Title = "TBA";
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.RequiresEpisodeTitle(_series, _localEpisode.Episodes))
.Returns(false);
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
}
}

View File

@@ -1,4 +1,4 @@
using FizzWare.NBuilder;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
@@ -22,24 +22,17 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi".AsOsAgnostic(),
Size = 100,
Series = Builder<Series>.CreateNew().Build(),
FileEpisodeInfo = new ParsedEpisodeInfo
ParsedEpisodeInfo = 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.FileEpisodeInfo.FullSeason = true;
_localEpisode.ParsedEpisodeInfo.FullSeason = true;
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}

View File

@@ -0,0 +1,123 @@
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();
}
}
}

View File

@@ -1,4 +1,4 @@
using FizzWare.NBuilder;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
@@ -18,16 +18,9 @@ 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.FileEpisodeInfo =
.With(l => l.ParsedEpisodeInfo =
Builder<ParsedEpisodeInfo>.CreateNew()
.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.EpisodeNumbers = new[] {5})
.With(p => p.FullSeason = false)
.Build())
.Build();
@@ -45,16 +38,6 @@ 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();
}
@@ -63,8 +46,6 @@ 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();
}
@@ -72,64 +53,32 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
[Test]
public void should_be_accepted_if_file_and_folder_have_the_same_episode()
{
_localEpisode.FileEpisodeInfo.EpisodeNumbers = new[] { 1 };
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1 };
_localEpisode.ParsedEpisodeInfo.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.FileEpisodeInfo.EpisodeNumbers = new[] { 1 };
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1 };
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 };
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E01.mkv".AsOsAgnostic();
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();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[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_the_same_episodes()
public void should_be_rejected_if_file_and_folder_do_not_have_same_episodes()
{
_localEpisode.FileEpisodeInfo.EpisodeNumbers = new[] { 5, 6 };
_localEpisode.FolderEpisodeInfo.EpisodeNumbers = new[] { 1, 2 };
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 5, 6 };
_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();
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More